Skip to content

Epic: Member OIDC (Member SSO) #328

@nkutub

Description

@nkutub

Summary

Enable members (citizens) to sign in to OSCER via a state-configured citizen identity provider (IdP) using OpenID Connect, so they can use one account for OSCER and other government services. When no citizen IdP is configured, members continue using app-managed credentials (e.g. Cognito email/password). Implementation reuses the existing staff SSO stack (OmniAuth, Devise, User model) with a separate provider, config, and provisioner.

Business Value

  • Single account for citizens: Members use their existing state portal or citizen IdP account; no separate OSCER password when OIDC is configured
  • State flexibility: Each deployment configures its own citizen IdP via MEMBER_OIDC_* env vars; no state-specific names in code
  • Security alignment: MFA and password policy are the IdP’s responsibility; app skips in-app MFA for OIDC users
  • Optional alongside Cognito: Member OIDC can be the only member auth or offered alongside email/password; login page shows both when both are enabled

Authentication Flow

sequenceDiagram
    participant Member
    participant OSCER
    participant IdP as Citizen IdP

    Member->>OSCER: Visit member login (or redirect when OIDC only)
    OSCER->>Member: Show "Sign in with your account" and/or email/password

    Member->>OSCER: POST /auth/member_oidc (OmniAuth)
    OSCER->>Member: Redirect to IdP
    Member->>IdP: Login + MFA
    IdP->>Member: Redirect to callback with code

    Member->>OSCER: GET /auth/member_oidc/callback
    OSCER->>IdP: Exchange code for tokens
    IdP->>OSCER: ID token
    OSCER->>OSCER: MemberOidcProvisioner.provision!(claims)
    OSCER->>OSCER: sign_in(user) — no MFA challenge
    OSCER->>Member: Redirect to dashboard or return_to
Loading

Scope

In Scope

  • OIDC redirect flow for members (OmniAuth provider :member_oidc)
  • Just-In-Time member user provisioning (find/create by UID, sync email/name; no role mapping)
  • MFA bypass for member OIDC users (mfa_preference set to opt_out)
  • Member login UI: "Sign in with your account" when MEMBER_OIDC_ENABLED; optional bypass of login screen when OIDC is the only auth
  • Shared redirect-URI helper and claim extractor (staff + member reuse)
  • Generic naming only (MEMBER_OIDC_*, no state or IdP names in repo)
  • Local development with Keycloak (optional second realm for citizen IdP)

Out of Scope

  • SAML for citizen IdP
  • Multiple citizen IdPs per deployment
  • Role or group mapping for members (members are non-staff)
  • IdP-initiated login

--

Epic Acceptance Criteria

  • When MEMBER_OIDC_ENABLED=true, members can sign in via the configured citizen IdP (OIDC redirect, callback, provision, session)
  • Member user records are created/updated by UID; email and name synced from IdP claims; no staff role
  • Member OIDC users bypass the app’s MFA preference page and Cognito MFA challenge (mfa_preference set to opt_out)
  • Member login page shows "Sign in with your account" when member OIDC is enabled; when OIDC is the only member auth, app can redirect directly to OIDC flow (bypass login form)
  • On failure, redirect to member sign-in path with alert (not necessarily root)
  • All config and code use generic names (MEMBER_OIDC_*, member_oidc); no state or IdP names in repo
  • Staff SSO behavior unchanged; redirect URI and claim extraction shared where appropriate
  • I18n keys for member OIDC (login_success, authentication_failed, not_enabled, button text) are generic

Sub-issues

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions