Keycloak SSO¶
Integrates a Keycloak server as an OpenID Connect identity provider for admin login, with role mapping. The service exchanges an OIDC authorization code for tokens, provisions or updates the matching staff user, and can map Keycloak realm roles onto Vectis roles on every login.
At a glance
| Extension id | keycloak |
| Category | authentication |
| Version | 0.0.1 |
| Provides | Admin OIDC login service (KeycloakService) with realm-role mapping |
| External account | A Keycloak server with a confidential OIDC client (client_id + client_secret) |
What It Provides¶
KeycloakService(ext_keycloak/service.py), an OIDC integration built onauthlib+httpx:get_authorize_url(redirect_uri)— loads{server_url}/realms/{realm}/.well-known/openid-configurationand returns the authorization URL plus a randomstate.handle_callback(code, redirect_uri, session)— exchanges the code at the discovered token endpoint, loads userinfo, and creates or updates the staffUser, anOAuthAccountrow (provider="keycloak"), and mapped globalUserRolerows. It returns{user, oauth_account, is_new_user}, the same shape as the coreOAuthService.handle_callback.
- Realm-role mapping:
realm_access.rolesfrom the id_token are translated through therole_mappingconfig into Vectis role slugs. - The
auth.keycloak_loginpermission (declared in the manifest).
Bring your own login route
The extension registers the manifest, configuration schema, and service — no core route calls KeycloakService out of the box. Deployments wire their admin login flow (route or BFF) to the service; see Admin SSO in Building Extensions.
Setup¶
- In Keycloak, you need a realm and a confidential OIDC client. The service authenticates with
client_id/client_secretand requests theopenid email profilescopes. - Enable the extension for the channel under Settings > Extensions and fill in
server_url,realm,client_id, andclient_secret. The client secret is a secret field: stored Fernet-encrypted and masked in the admin. - Optionally set
role_mappingto map Keycloak realm role names to Vectis role slugs. Theadmin_onlyflag (defaulttrue) is exposed asKeycloakService.admin_onlyfor the deployment's login wiring to consult; the service's own staff-only guard (see Operational Notes) applies regardless of this flag. - Wire your admin login flow to
KeycloakService— the constructor accepts the per-channelChannelExtension.configdict or explicit keyword arguments.
The extension is distributed as the vectis-ext-keycloak wheel from the enterprise-extensions repo (dev-linked from the sibling checkout in development) and discovered at startup via its vectis.extensions entry point.
Configuration Reference¶
| Key | Type | Required | Secret | Description |
|---|---|---|---|---|
server_url |
string | Yes | No | Keycloak base URL (e.g. https://auth.example.com) |
realm |
string | Yes | No | — |
client_id |
string | Yes | No | — |
client_secret |
string | Yes | Yes | — |
admin_only |
boolean | No (default true) |
No | When true, Keycloak is intended for admin/staff SSO only |
role_mapping |
object | No (default {}) |
No | Map Keycloak realm role names to Vectis role slugs |
Permissions¶
auth.keycloak_login
Manifest permissions are upserted at boot; assigning them to roles remains a manual step.
Operational Notes¶
- The OIDC discovery document is fetched on every authorize and callback call, so
server_urlandrealmmust be reachable from the API container. - Role sync is authoritative for managed slugs only: on each login, existing global
UserRolerows whose role appears inrole_mapping's values are deleted and re-created from the user's current Keycloak realm roles. Roles not listed inrole_mappingare never touched. role_mappingvalues that don't match an existing Vectis role slug are skipped with a logged warning.- Staff-only guard: if the login email already belongs to a non-staff user, the callback is refused. New users are created as
type="staff"with no local password, and a new user requires anemailclaim. - Userinfo gaps are back-filled from id_token claims (
sub,email,name,preferred_username,given_name); a login without asubis rejected. - Access and refresh tokens are stored on the
OAuthAccountrow and updated on each login; the raw userinfo (including akeycloak_realm_roleslist) is kept on the row.