[VS-NfD] Implement OIDC Authorization Code with PKCE, Keycloak as reference IdP #214

Closed
opened 2026-07-30 01:43:46 +02:00 by fable-5 · 1 comment
Collaborator

Plan reference: 20-massnahmenplan.md -> P1-1
ADR: ADR 0021
Effort: L (5–6 AT)
Depends on: #188

Context

Authentication is a security base function §52 VSA assigns to the
platform. Delegating it to the operator's IdP is the single most important
step in keeping Dorfteich out of the certification obligation under §51
VSA.

Current state

  • The data model has the slot: model UserIdentity
    (apps/api/prisma/schema.prisma:562–577) — "provider is 'password'
    today and 'oidc:' later", @@unique([provider, subject]),
    credential holds the Argon2id hash for password identities.
  • No OIDC implementation exists: a search for oidc across
    apps/api/src and packages/shared/src returns nothing outside that
    comment. ADR 0007 declares the readiness, not the feature.

Acceptance criteria

  • Authorization Code flow with PKCE, state and nonce validation,
    discovery-based configuration, and JWKS-based token validation using
    the vetted library from #188 — no hand-rolled JWT verification.
  • Identity linking follows the existing model: provider = "oidc:<issuer>", subject from the token; an existing local user is
    linked by a documented, deliberate rule (not silently by e-mail).
  • Login, logout (including IdP-initiated single logout or a documented
    decision against it) and session creation reuse the existing session
    service — no parallel session mechanism.
  • Verified against a Keycloak instance; the setup used is documented so
    the test is repeatable.
  • Tests: successful login creates/links the identity; invalid state,
    nonce, signature, issuer and audience each rejected; expired token
    rejected.
  • docs/architecture/security.md and #227 document configuration;
    ADR 0021 records the decisions.

Out of scope

SAML and LDAP, claim-to-role mapping (#217), and disabling local
authentication (#216).

**Plan reference:** `20-massnahmenplan.md` -> P1-1 **ADR:** ADR 0021 **Effort:** L (5–6 AT) **Depends on:** #188 ## Context Authentication is a security base function §52 VSA assigns to the platform. Delegating it to the operator's IdP is the single most important step in keeping Dorfteich out of the certification obligation under §51 VSA. ## Current state - The data model has the slot: `model UserIdentity` (`apps/api/prisma/schema.prisma:562–577`) — "`provider` is 'password' today and 'oidc:<issuer>' later", `@@unique([provider, subject])`, `credential` holds the Argon2id hash for password identities. - **No OIDC implementation exists**: a search for `oidc` across `apps/api/src` and `packages/shared/src` returns nothing outside that comment. ADR 0007 declares the readiness, not the feature. ## Acceptance criteria - [ ] Authorization Code flow with PKCE, state and nonce validation, discovery-based configuration, and JWKS-based token validation using the vetted library from #188 — no hand-rolled JWT verification. - [ ] Identity linking follows the existing model: `provider = "oidc:<issuer>"`, `subject` from the token; an existing local user is linked by a documented, deliberate rule (not silently by e-mail). - [ ] Login, logout (including IdP-initiated single logout or a documented decision against it) and session creation reuse the existing session service — no parallel session mechanism. - [ ] Verified against a Keycloak instance; the setup used is documented so the test is repeatable. - [ ] Tests: successful login creates/links the identity; invalid state, nonce, signature, issuer and audience each rejected; expired token rejected. - [ ] `docs/architecture/security.md` and #227 document configuration; ADR 0021 records the decisions. ## Out of scope SAML and LDAP, claim-to-role mapping (#217), and disabling local authentication (#216).
fable-5 added this to the M27 — VS-NfD: external authentication milestone 2026-07-30 01:43:46 +02:00
fable-5 added the
effort:L
area:auth
vs-nfd:blocker
vs-nfd
labels 2026-07-30 01:43:46 +02:00
Author
Collaborator

Implemented in PR #282 (commit 5796b7a); merged via the fast-forward chain PRs #283-#285 (runs 602-604 green over the cumulative diffs; #282 own run 601 failed only on the known action-cache runner signature). CD 609 green, readyz test+int ok; GET /auth/methods live answers {local:true, oidc:null} (OIDC deploy-level unconfigured on the stages, as intended). OIDC Authorization Code + PKCE on jose+fetch (no new dependency), discovery + JWKS validation (RS256/ES256 allowlist, issuer/audience/expiry/nonce), signed state cookie with a dedicated HKDF purpose, JIT accounts (verified-mail required), NO silent adoption by e-mail (oidc_link_required + explicit link flow, audited, catalogue v1.3). 5 e2e tests against a protocol-faithful fake IdP; verified end-to-end against real Keycloak 26.0 via curl (repeatable procedure in security.md, section External authentication).

Implemented in PR #282 (commit 5796b7a); merged via the fast-forward chain PRs #283-#285 (runs 602-604 green over the cumulative diffs; #282 own run 601 failed only on the known action-cache runner signature). CD 609 green, readyz test+int ok; GET /auth/methods live answers {local:true, oidc:null} (OIDC deploy-level unconfigured on the stages, as intended). OIDC Authorization Code + PKCE on jose+fetch (no new dependency), discovery + JWKS validation (RS256/ES256 allowlist, issuer/audience/expiry/nonce), signed state cookie with a dedicated HKDF purpose, JIT accounts (verified-mail required), NO silent adoption by e-mail (oidc_link_required + explicit link flow, audited, catalogue v1.3). 5 e2e tests against a protocol-faithful fake IdP; verified end-to-end against real Keycloak 26.0 via curl (repeatable procedure in security.md, section External authentication).
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#214
No description provided.