Federated Identity (Customer IdP)

Users sign in with the customer’s own identity provider. Keycloak brokers the login over OIDC and maps the customer’s groups to platform roles, so Compass model access keeps its deny-by-default rules.

Federal Frontier issues platform tokens from a realm in Keycloak, the CNCF Incubating identity project. The realm is named FAS by default, and each customer can rename it; the paths below use <REALM>. With federated identity, the customer’s users sign in with the identity provider (IdP) they already run. Keycloak brokers the login to that IdP over OpenID Connect (OIDC) and turns the IdP’s group memberships into platform roles. Compass and the llm-router keep reading the same roles and keep their deny-by-default model rules, so no application changes are needed.

How it works

  • The customer IdP authenticates the user with whatever it already enforces: password, MFA or smart card.
  • Keycloak receives the IdP’s ID token, creates or updates a federated user, and issues the access token that Federal Frontier services trust.
  • Advanced Claim to Role mappers with sync mode Force turn upstream group names into platform roles at every login. When a user leaves a group upstream, the role is removed at their next login.
  • The llm-router grants models only where a role rule exists. A user with no mapped role gets no models.

Keycloak brokers to any OpenID Connect–compliant identity provider in the same way, and the group-to-role mapping works with any provider that can include the user’s groups, or another claim, in its tokens. Providers differ in how they send groups: Microsoft Entra ID sends group object IDs rather than names by default, and leaves groups out of a JWT when a user is in more than 200; Okta needs a groups claim configured on the app or the authorization server. Only ForgeRock OpenAM has been tested so far; the other providers are listed under Not yet tested.

flowchart TD G[Customer IdP
user groups] --> T[ID token
groups claim] T --> K[Keycloak broker
Advanced Claim to Role
sync mode Force] K --> A[Platform access token
realm roles] A --> R[llm-router role rules
deny by default] R --> M1[Analyst role
local models] R --> M2[Engineer role
local models + sonnet] R --> M3[No mapped role
access denied] style G fill:#2d3748,stroke:#4299e1,color:#e2e8f0 style T fill:#2d3748,stroke:#4299e1,color:#e2e8f0 style K fill:#2d3748,stroke:#4299e1,color:#e2e8f0 style A fill:#2d3748,stroke:#4299e1,color:#e2e8f0 style R fill:#2d3748,stroke:#4299e1,color:#e2e8f0 style M1 fill:#2d3748,stroke:#48bb78,color:#e2e8f0 style M2 fill:#2d3748,stroke:#48bb78,color:#e2e8f0 style M3 fill:#2d3748,stroke:#f56565,color:#e2e8f0

Set up OIDC brokering

Placeholders: <KEYCLOAK_URL>, <REALM>, <ALIAS> (the identity provider alias in Keycloak), <IDP_ISSUER>, <CLIENT_ID> and <CLIENT_SECRET>. Keep the client secret in your secret store and the Keycloak admin console only.

1. Register Keycloak as a client in the customer IdP

Create a confidential OIDC client:

Setting Value
Grant / response type authorization_code / code
Redirect URI <KEYCLOAK_URL>/realms/<REALM>/broker/<ALIAS>/endpoint
Post-logout redirect URI <KEYCLOAK_URL>/realms/<REALM>/broker/<ALIAS>/endpoint/logout_response (required for sign-out through the broker)
Token endpoint authentication client_secret_post (must match step 3)
Scopes openid profile email groups

2. Put the user’s groups in the ID token

The ID token must carry the user’s group names as a JSON array, for example "groups": ["llm-analysts"]. How you configure this depends on the IdP; see the ForgeRock OpenAM example.

3. Add the identity provider in Keycloak

Identity providers → Add provider → OpenID Connect v1.0

Field Value
Alias / display name <ALIAS> / the label users see on the sign-in button
Discovery endpoint <IDP_ISSUER>/.well-known/openid-configuration
Logout URL the IdP’s end_session_endpoint
Client authentication Client secret sent as post
Client ID / secret <CLIENT_ID> / <CLIENT_SECRET>
Default scopes openid profile email groups
Validate signatures / use JWKS URL On / On
PKCE On, S256
Sync mode Force

Identity provider settings in Keycloak

4. Map groups to roles

Add one mapper per group under Identity providers → <ALIAS> → Mappers:

Field Value
Mapper type Advanced Claim to Role
Sync mode override Force
Claims key groups, value llm-analysts (exact match)
Role realm role llm-analyst

Optionally add a Username Template Importer mapper with template ${CLAIM.sub} so usernames match the upstream login name.

Advanced Claim to Role mapper

Identity provider mappers list

5. Send users straight to the customer IdP

Applications add kc_idp_hint=<ALIAS> to the sign-in request, and Keycloak redirects to the customer IdP without showing its own sign-in page. Without the hint, users see the Keycloak sign-in page with the customer IdP button.

Keycloak sign-in page with the customer IdP button

6. Keep the Compass role rules

llm-router role rules are keyed by role and deny by default, for example llm-analyst → [local] and llm-engineer → [local, sonnet]. Brokering does not change them.

Example: ForgeRock OpenAM and OpenDJ

Tested with Open Identity Platform OpenAM 16.1.3 (the open-source ForgeRock AM code line) and OpenDJ 5.1.2 as the user store.

  1. Groups claim: copy the default OIDC Claims Script and add a groups claim built from identity.getMemberships(IdType.GROUP). Map scope groups to claim groups, and add com.sun.identity.idm.IdType and com.sun.identity.idm.AMIdentity to the OIDC_CLAIMS scripting allow list.
  2. OAuth2 provider: use that claims script, support scopes openid profile email groups, and turn on Always return claims in ID tokens.
  3. Client: create the confidential client from step 1 above.
  4. Groups and users: the test used group llm-analysts (maria.gonzalez, an analyst user), group llm-engineers (james.whitaker, an engineer user), and priya.raman, a user in no group.

OpenAM sign-in page

OpenAM OAuth2 provider: claims script and scopes

OpenAM client for the Keycloak broker

OpenAM groups

Sign-out: in testing, OpenAM’s end-session endpoint redirected correctly but did not end the OpenAM session, so a user could be signed straight back in. The tested sign-out therefore also ended the OpenAM session before the Keycloak logout. With other IdPs, check end-session behavior, or use short IdP session lifetimes or prompt=login.

Tested

Tested on 2026-10-21 in Chrome, in a single-VM test environment with Keycloak in development mode:

  • Keycloak 26.4 brokers over OIDC to OpenAM 16.1.3 with OpenDJ 5.1.2, with PKCE and kc_idp_hint.
  • Advanced Claim to Role mappers with sync mode Force map the upstream groups to realm roles.
  • Compass llm-router deny-by-default still holds: maria.gonzalez (an analyst user) gets local models only, james.whitaker (an engineer user) gets local models plus sonnet, and priya.raman (a user with no mapped role) is denied (HTTP 403).
  • Sign-out returns the user to the application, and the customer IdP asks for credentials again.

No model was called; the tests covered model listing and routing decisions only.

Screenshots for each user: back at the Keycloak account console, the user’s role mapping, the demo page with the permitted models, and the decoded tokens (signatures redacted).

maria.gonzalez (an analyst user, upstream group llm-analysts): local models only

maria.gonzalez: Keycloak account console

maria.gonzalez: role mapping

maria.gonzalez: demo page

maria.gonzalez: decoded tokens

maria.gonzalez: decoded tokens, continued

james.whitaker (an engineer user, upstream group llm-engineers): local models plus sonnet

james.whitaker: Keycloak account console

james.whitaker: role mapping

james.whitaker: demo page

james.whitaker: decoded tokens

james.whitaker: decoded tokens, continued

priya.raman (a user with no upstream group): access denied

priya.raman: Keycloak account console

priya.raman: role mapping

priya.raman: demo page

priya.raman: decoded tokens

priya.raman: decoded tokens, continued

Sign-out

Demo page after sign-out

After sign-out, the customer IdP asks for credentials again

Not yet tested

  • Microsoft Entra ID (group object IDs in groups, groups overage claim, app roles in roles)
  • Okta (groups claim on the app or authorization server)
  • Ping: PingFederate, PingOne and PingAM
  • SAML 2.0 brokering, including ADFS
  • Grafana, Argo CD, Remuda and the Kubernetes API signing in through the brokered identity provider
  • Keycloak in production mode behind HTTPS
  • Back-channel logout between Keycloak and the IdP