SSO Integration
One login for hrida-ai-studio and HridaOne — per-user data isolation via Keycloak.
When SSO is configured, employees sign in once through Keycloak. Both hrida-ai-studio and HridaOne trust the same realm, so the HR Agent can call HridaOne with the employee's own token — HridaOne's RBAC then decides exactly what that employee may see. Without SSO, the agent uses a service account instead.
Architecture
┌─────────────────────────────────────────────────────────┐
│ Keycloak (shared realm, e.g. "hrida") │
│ Clients: hrida-ai-studio, hridaone │
└──────────────────────┬──────────────────────────────────┘
│ OIDC / JWT (RS256)
┌────────────┴──────────────────┐
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ hrida-ai-studio │ │ HridaOne HRMS │
│ (port 8080) │ │ (port 8081) │
│ OIDC login │ │ validates KC │
└────────┬────────┘ │ tokens natively │
│ per-user KC token └────────▲─────────┘
│ → Redis │
▼ │
hr-agent MCP tools ──── REST ────────────┘
Authorization: Bearer {user_kc_token}
For the step-by-step token lifecycle in chat and in workflow runs, see Architecture & Token Flow.
Data isolation by role
| HridaOne role | What the HR Agent can see / do |
|---|---|
| Employee | Own profile, leave balance, attendance, payslip; apply for own leave; raise tickets |
| Manager | All of above + team members' leave and attendance |
| HR Admin | All of above + org-wide attendance dashboard, all employee data |
The agent has no hard-coded data restrictions — the scope is entirely determined by the token presented to HridaOne.
1. Configure Keycloak
Create or reuse a realm
Both applications must use the same realm (for example hrida).
Register the clients
hrida-ai-studio (skip if Studio already signs in through Keycloak)
- Clients → Create client, Client ID
hrida-ai-studio, protocolopenid-connect. - Turn on Client authentication and Standard flow.
- Set Valid redirect URIs to
https://<your-studio-domain>/oauth/oidc/callback. - Copy the client secret from the Credentials tab.
hridaone
- Clients → Create client, Client ID
hridaone, protocolopenid-connect, Client authentication on. - Set Valid redirect URIs to
https://<your-hridaone-domain>/sso/callback(used by HridaOne's own SSO sign-in). - Copy the client secret from the Credentials tab.
HridaOne accepts tokens issued to any client listed in KEYCLOAK_ALLOWED_CLIENT_IDS (default hridaone,hrida-ai-studio), so tokens Studio obtains at login work against HridaOne directly.
Map the tenant code into the token
HridaOne reads the tenant (company) from the hrida_tenant_code claim — the claim name is fixed.
- Client scopes → profile → Mappers → Add mapper → By configuration → User Attribute.
- Set User Attribute and Token Claim Name to
hrida_tenant_code, and enable Add to access token. - Set the attribute on each user: Users → (user) → Attributes, key
hrida_tenant_code, value = the tenant code in HridaOne (for exampleACME).
Users that HridaOne creates in Keycloak, and users synced through an LDAP/AD provider that HridaOne provisions, get hrida_tenant_code set automatically. You still need the mapper from step 1 so the attribute reaches the token.
Check the token claims
The realm's standard profile and email scopes provide sub, email, and name, which HridaOne uses for lookup and JIT provisioning.
2. Configure HridaOne
HridaOne validates Keycloak tokens in KcJwtAuthenticationFilter. Set these environment variables:
| Variable | Description |
|---|---|
KEYCLOAK_ISSUER_URI | Realm URL as HridaOne reaches it, e.g. http://keycloak:9090/realms/hrida. Leave empty to disable Keycloak auth. |
KEYCLOAK_PUBLIC_ISSUER_URI | Public realm URL when it differs (e.g. https://keycloak.example.com/realms/hrida). Tokens whose iss matches either value are accepted. |
KEYCLOAK_CLIENT_ID / KEYCLOAK_CLIENT_SECRET | The hridaone client |
KEYCLOAK_ALLOWED_CLIENT_IDS | Clients whose tokens are accepted (default hridaone,hrida-ai-studio) |
How the filter works
- Reads the
Authorization: Bearerheader. RS256 tokens are treated as Keycloak tokens; HS256 tokens go to HridaOne's native login. - Verifies the signature against Keycloak's public keys (
{issuer}/protocol/openid-connect/certs, cached and refreshed on rotation). - Checks
issagainst the issuer URIs andazpagainstKEYCLOAK_ALLOWED_CLIENT_IDS. - Looks up the user by email — or JIT-provisions an
EMPLOYEEunder the tenant fromhrida_tenant_code. - Sets the security context so the request runs as that employee.
3. Configure hrida-ai-studio
Point Studio's OAuth login at the same realm:
ENABLE_OAUTH_SIGNUP=true
OAUTH_CLIENT_ID=hrida-ai-studio
OAUTH_CLIENT_SECRET=<from-keycloak>
OPENID_PROVIDER_URL=https://<keycloak-host>/realms/hrida/.well-known/openid-configuration
REDIS_URL=redis://redis:6379/0See SSO for all OAuth options. No other switch is needed: the HR Agent's Digital Employee filter and the workflow worker store the user's Keycloak token in Redis automatically.
4. Configure hrida-mcpo
Turn on per-user tokens in the hr-agent entry of mcpo's config.json, and point it at the same Redis as Studio:
"env": {
"HRIDAONE_API_URL": "http://hridaone:8081",
"HRIDAONE_USE_PER_USER_TOKEN": "true",
"REDIS_URL": "redis://redis:6379/0",
"HRIDAONE_EMAIL": "hr-agent@yourcompany.com",
"HRIDAONE_PASSWORD": "<service-account-password>"
}Keep the service account credentials as a fallback for cron/webhook-triggered runs and users who signed in without Keycloak.
Token TTL and expiry
| Scenario | Token used | Redis TTL |
|---|---|---|
| User chats with the HR Agent | User's KC token (hrida:kc_token:{user_id}) | 600 s, refreshed on each message |
| User starts a workflow run | User's KC token (hrida:kc_token:{run_id}) | WORKFLOW_MAX_TIMEOUT_SECONDS + 60 s |
| Cron or webhook trigger (no user session) | Service account | — |
| No per-user token found | Service account | — |
The Redis TTL only controls how long the token is available. Keycloak's own Access Token Lifespan (5 minutes by default) decides how long HridaOne accepts it — raise it under Realm Settings → Tokens if long runs hit 401s.
Troubleshooting
HR Agent returns data from the service account, not the user's own
→ The per-user token isn't being used. Check HRIDAONE_USE_PER_USER_TOKEN=true in mcpo, that the user signed in through Keycloak, and that the key exists: redis-cli keys "hrida:kc_token:*".
HridaOne returns 401 for KC tokens
→ The token's iss must match KEYCLOAK_ISSUER_URI or KEYCLOAK_PUBLIC_ISSUER_URI exactly, and its azp must be in KEYCLOAK_ALLOWED_CLIENT_IDS. Check HridaOne's logs for the rejection reason.
JIT provisioning fails or creates users in the wrong company
→ The hrida_tenant_code claim is missing or wrong. Decode the token and confirm the claim; add the mapper from Map the tenant code into the token.
Redis connection refused from mcpo
→ Redis must be reachable from the mcpo process. Set REDIS_URL in the hr-agent env block of config.json.
More cases: HR Agent Troubleshooting.
Related
- HR Agent — setup and overview
- Architecture & Token Flow — how tokens travel end to end
- Access Control — restrict which users can chat with the HR Agent