ADR-005: Keycloak for OIDC/RBAC

On this page

Status

Accepted

Context

CRAIG requires authentication and authorization for all API and web UI access. Child welfare systems typically integrate with state-level identity providers (Active Directory, SAML). We needed an identity provider that supports OIDC, RBAC, and can federate with external identity sources.

Decision

Use Keycloak as the OIDC identity provider with a craig realm containing:

  • 6 realm roles: admin, supervisor, caseworker, eligibility_worker, icpc_coordinator, readonly

  • 2 clients:

    • craig-api — public, Direct Access Grant (ROPC) for CLI and tests

    • craig-ui — public, Authorization Code + PKCE for web UI

Rationale

  • Industry standard: Keycloak is the most widely deployed open-source identity provider, used in government and healthcare.

  • OIDC support: Native OpenID Connect support with JWT access tokens, JWKS endpoint, and standard flows.

  • Flexible federation: Keycloak can federate with LDAP, Active Directory, SAML 2.0 IdPs — essential for state agency deployments.

  • Role-based access: Realm roles are embedded in JWT realm_access.roles claim. Services extract roles via craig-auth::Claims and enforce via require_role().

  • Self-contained devstack: Keycloak’s realm import feature (--import-realm) allows the devstack to start with pre-configured users, roles, and clients from a JSON file.

Role Hierarchy

admin           → full access to all services
supervisor      → caseworker + management actions (approve plans, manage homes)
caseworker      → core case management (CRUD on cases, referrals, placements)
eligibility_worker → eligibility-specific actions (future)
icpc_coordinator   → ICPC request management
readonly        → view-only access (future)

Helper functions: require_role(claims, Role::CaseworkerOrAbove) — combines caseworker, supervisor, and admin.

Dual URL Pattern

In containerized deployments:

Both must resolve to the same Keycloak realm.

JWKS Auto-Refresh

Services fetch the JWKS at startup and spawn a background task that re-fetches periodically. This ensures key rotation is handled without service restarts.

Consequences

  • Keycloak is an additional infrastructure dependency (Java-based, ~500MB image).

  • Token expiration (30 minutes default) means long-running CLI sessions may need to re-authenticate.

  • Role changes in Keycloak require the user to obtain a new token — existing JWTs retain old roles until expiry.

Edit this page · latest