The first auth phase delivered email/password login with server-side sessions (tower-sessions) and the Authorized<Cap> extractor framework. OIDC was deliberately split out to keep that plan small. The session/extractor layer is provider-agnostic, so OIDC slots in on top.
What to do (its own plan)
Add an OIDC relying-party flow scoped to the single org:
Provider config (issuer/discovery URL, client id/secret, redirect URI) via Config/env.
GET /api/admin/oidc/login → redirect to the provider's authorize endpoint (with state + PKCE).
GET /api/admin/oidc/callback → exchange code, validate the ID token, map the external identity to a local app_user (provision-on-first-login or match by email — decide), then call the existingauth::establish_session (same session/cookie layer as password login).
Decide account linking: does an OIDC identity require a pre-existing app_user, or auto-provision with a default role?
References
crates/auth/src/lib.rs — establish_session, the session model
Candidate crate: openidconnect (evaluate vs hand-rolling against discovery).
## Context
The first auth phase delivered **email/password** login with server-side sessions (`tower-sessions`) and the `Authorized<Cap>` extractor framework. OIDC was deliberately split out to keep that plan small. The session/extractor layer is provider-agnostic, so OIDC slots in on top.
## What to do (its own plan)
Add an OIDC relying-party flow scoped to the single org:
- Provider config (issuer/discovery URL, client id/secret, redirect URI) via `Config`/env.
- `GET /api/admin/oidc/login` → redirect to the provider's authorize endpoint (with state + PKCE).
- `GET /api/admin/oidc/callback` → exchange code, validate the ID token, map the external identity to a local `app_user` (provision-on-first-login or match by email — decide), then call the **existing** `auth::establish_session` (same session/cookie layer as password login).
- Decide account linking: does an OIDC identity require a pre-existing `app_user`, or auto-provision with a default role?
## References
- `crates/auth/src/lib.rs` — `establish_session`, the session model
- `crates/server/src/config.rs` — config pattern
- Arch spec §10 (auth); VISION "Authentication & access control".
- Candidate crate: `openidconnect` (evaluate vs hand-rolling against discovery).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Context
The first auth phase delivered email/password login with server-side sessions (
tower-sessions) and theAuthorized<Cap>extractor framework. OIDC was deliberately split out to keep that plan small. The session/extractor layer is provider-agnostic, so OIDC slots in on top.What to do (its own plan)
Add an OIDC relying-party flow scoped to the single org:
Config/env.GET /api/admin/oidc/login→ redirect to the provider's authorize endpoint (with state + PKCE).GET /api/admin/oidc/callback→ exchange code, validate the ID token, map the external identity to a localapp_user(provision-on-first-login or match by email — decide), then call the existingauth::establish_session(same session/cookie layer as password login).app_user, or auto-provision with a default role?References
crates/auth/src/lib.rs—establish_session, the session modelcrates/server/src/config.rs— config patternopenidconnect(evaluate vs hand-rolling against discovery).