Skip to content

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 on authlib + httpx:
    • get_authorize_url(redirect_uri) — loads {server_url}/realms/{realm}/.well-known/openid-configuration and returns the authorization URL plus a random state.
    • handle_callback(code, redirect_uri, session) — exchanges the code at the discovered token endpoint, loads userinfo, and creates or updates the staff User, an OAuthAccount row (provider="keycloak"), and mapped global UserRole rows. It returns {user, oauth_account, is_new_user}, the same shape as the core OAuthService.handle_callback.
  • Realm-role mapping: realm_access.roles from the id_token are translated through the role_mapping config into Vectis role slugs.
  • The auth.keycloak_login permission (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

  1. In Keycloak, you need a realm and a confidential OIDC client. The service authenticates with client_id/client_secret and requests the openid email profile scopes.
  2. Enable the extension for the channel under Settings > Extensions and fill in server_url, realm, client_id, and client_secret. The client secret is a secret field: stored Fernet-encrypted and masked in the admin.
  3. Optionally set role_mapping to map Keycloak realm role names to Vectis role slugs. The admin_only flag (default true) is exposed as KeycloakService.admin_only for the deployment's login wiring to consult; the service's own staff-only guard (see Operational Notes) applies regardless of this flag.
  4. Wire your admin login flow to KeycloakService — the constructor accepts the per-channel ChannelExtension.config dict 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_url and realm must be reachable from the API container.
  • Role sync is authoritative for managed slugs only: on each login, existing global UserRole rows whose role appears in role_mapping's values are deleted and re-created from the user's current Keycloak realm roles. Roles not listed in role_mapping are never touched.
  • role_mapping values 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 an email claim.
  • Userinfo gaps are back-filled from id_token claims (sub, email, name, preferred_username, given_name); a login without a sub is rejected.
  • Access and refresh tokens are stored on the OAuthAccount row and updated on each login; the raw userinfo (including a keycloak_realm_roles list) is kept on the row.