Authenticated widget integration with Ruby on Rails
This guide shows how a customer Rails backend gives the Guanta widget a verified user identity while retaining ownership of authentication, sessions, users, and authorization.
Return to the live Rails example ยท View the source repository
The trust boundary
| Customer Rails application | Guanta |
|---|---|
| Authenticates users and owns its session | Trusts the backend's signed assertion |
| Stores passwords, SSO data, and authorization rules | Never receives passwords, hashes, or Rails cookies |
| Chooses a stable external ID and display name | Makes the trusted identity available to the configured agent |
| Creates an opaque value for each successful login | Binds conversation continuity to that exact login context |
Never accept the asserted user ID, name, or login context from browser parameters. Derive all three from authenticated backend state.
End-to-end flow
- Rails authenticates the visitor, rotates its session, and generates a new opaque login context.
- The standard Guanta loader calls the same-origin
POST /widget-identityroute when it initializes. - The controller reads the current user exclusively from the encrypted Rails session.
Guanta::Clientcalls the tenant identity-token API with a server-only credential.- Rails returns the one-time token to the loader with no-store cache headers.
- The loader securely starts or resumes the matching Guanta conversation.
Configuration
Obtain the base URL, public widget ID, and SRI integrity value from the interface Playground. Create the server credential under Widget Identity Credentials for that same interface. Add the application's exact origin to the interface's allowed origins.
APP_URL=https://customer.example
APP_SESSION_SECURE=true
GUANTA_BASE_URL=https://your-tenant.example
GUANTA_WIDGET_PUBLIC_ID=your-widget-public-id
GUANTA_WIDGET_IDENTITY_CREDENTIAL=wic_your-id.your-secret
GUANTA_WIDGET_LOADER_INTEGRITY=sha384-your-published-integrity
The credential is a backend secret. Do not place it in HTML, JavaScript, Git, logs, screenshots, analytics, or browser storage.
Reusable Rails client
The example isolates Guanta API behavior in app/services/guanta/client.rb and app/services/guanta/http_transport.rb. Both are independent of the demo controllers and include YARD comments for every method.
client = Guanta::Client.new(
base_url: Rails.application.credentials.dig(:guanta, :base_url),
widget_public_id: Rails.application.credentials.dig(:guanta, :widget_public_id),
identity_credential: Rails.application.credentials.dig(:guanta, :identity_credential)
)
token = client.create_widget_identity_token(
external_user_id: current_user.id.to_s,
full_name: current_user.full_name,
login_context_id: session.fetch(:widget_login_context_id)
)
render json: { identity_token: token.value }
The method raises Guanta::Client::ConfigurationError for invalid local values and Guanta::Client::RequestError for connectivity, rejection, or malformed-response failures.
Identity-token request
POST /api/v1/widget-identity-tokens
Authorization: Bearer wic_credential.secret
Accept: application/json
Content-Type: application/json
{
"widget_public_id": "your-widget-public-id",
"external_user_id": "stable-customer-user-id",
"full_name": "Customer User",
"login_context_id": "opaque-per-login-value"
}
- Use an immutable database ID or UUID as
external_user_id(maximum 128 characters). - Use the customer display name as
full_name(maximum 191 characters). - Generate an unpredictable
login_context_idafter every successful login (maximum 128 characters). - Keep the same context during navigation and remove it on logout.
Conversation continuity
The identity token is short-lived and single-use. The loader separately stores an encrypted Guanta resume token and a visibility preference in the current tab's sessionStorage. On each new Rails page it obtains a fresh identity token. Guanta resumes only when the widget, origin, user, login context, active conversation, and expiry all match.
Login and logout use reset_session. Each login receives a new SecureRandom.hex(32) context, so an old browser-held token cannot restore an earlier authenticated conversation. A new browser tab also starts closed because tab storage is separate.
Production checklist
- Serve both applications over HTTPS and use Secure, HttpOnly, SameSite cookies.
- Use separate identity credentials per environment and store them in a secret manager.
- Allow only the required customer origins on the widget interface.
- Do not log credentials, identity tokens, or authenticated endpoint responses.
- Fail authenticated initialization closed rather than silently becoming anonymous.
- Test anonymous use, login, navigation resume, logout/login reset, New chat, and a new tab.
- Never treat agent output as application authentication or authorization.