Ruby on Rails
Step 1: Create View Template
Add a container element in your ERB template:
<!-- app/views/your_controller/index.html.erb -->
<h1>Your Application</h1>
<div id="rollout-container"></div>
Step 2: Add JavaScript Initialization
Create a JavaScript file or add to your existing pack:
// app/javascript/packs/rollout.js
document.addEventListener('DOMContentLoaded', function() {
const container = document.getElementById('rollout-container');
// Fetch token from Rails backend
fetch('/rollout-token', {
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector("[name='csrf-token']").content
}
})
.then(response => response.json())
.then(data => {
const token = data.token;
// Add required styles
const styleLink = document.createElement('link');
styleLink.rel = 'stylesheet';
styleLink.href = 'https://esm.sh/@rollout/link-react@latest/dist/style.css';
document.body.appendChild(styleLink);
// Dynamically load Rollout bundle
import(/* webpackIgnore: true */ 'https://esm.sh/@rollout/link-react@latest?bundle=all').then(
({ RolloutLinkProvider, CredentialsManager, createElement, createRoot }) => {
const root = createRoot(container);
root.render(
createElement(
RolloutLinkProvider,
{ token },
createElement(CredentialsManager)
)
);
}
);
});
});
Step 3: Add Rails Route
Create a route to handle token generation:
# config/routes.rb
get '/rollout-token', to: 'rollout#generate_token'
Step 4: Create Controller
Implement token generation in a controller:
# app/controllers/rollout_controller.rb
class RolloutController < ApplicationController
def generate_token
# Implement your actual token generation logic here
# This should use your ROLLOUT_CLIENT_SECRET
render json: {
token: generate_secure_token # Replace with actual token generation
}
end
private
def generate_token(user_id)
now = Time.now.to_i
JWT.encode({
iss: ENV['ROLLOUT_CLIENT_ID'],
sub: user_id,
iat: now,
exp: now + 900 # 15 minutes
},
ENV['ROLLOUT_CLIENT_SECRET'],
'HS512')
end
end
Step 5 (Optional): Handle Credential Events
Add event handling in the JavaScript:
// Update the import block
.then(({ RolloutLinkProvider, CredentialsManager, createElement, createRoot }) => {
const handleCredentialAdded = ({ id, appKey }) => {
console.log(`New credential added - ID: ${id}, App: ${appKey}`);
// Send to Rails backend
fetch('/store-credential', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector("[name='csrf-token']").content
},
body: JSON.stringify({
credential_id: id,
app_key: appKey
})
});
};
const root = createRoot(container);
root.render(
createElement(
RolloutLinkProvider,
{ token },
createElement(CredentialsManager, {
onCredentialAdded: handleCredentialAdded
})
)
);
});
Step 6: Update CSP
Update config/initializers/content_security_policy.rb
:
# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
policy.default_src :self
policy.font_src :self, :data
policy.img_src :self, :data
policy.object_src :none
# Allow esm.sh for scripts and styles
policy.script_src :self, :unsafe_eval, :unsafe_inline, 'https://esm.sh'
policy.style_src :self, :unsafe_inline, 'https://esm.sh'
# Allow connecting to your backend and Rollout's API
policy.connect_src :self, 'https://universal.rollout.com'
# If you're using webpack-dev-server in development
if Rails.env.development?
policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035", 'https://universal.rollout.com'
end
policy.worker_src :self
end
Alternative to CSP
You can also use nonces or hashes for better security, but that's more complex to set up with dynamic imports. If you want to go that route, you'd need to:
rubyCopyRails.application.config.content_security_policy do |policy|
policy.script_src :self, :unsafe_eval, 'https://esm.sh' do |source|
source.nonce :script
end
end
And then update your JavaScript to include the nonce:
<script nonce="<%= request.content_security_policy_nonce %>">
// Your code here
</script>