OIDC steady-state Check (session validation, claim injection, 302/403/5xx) #23

Merged
ginjiruu merged 1 commit from feat/oidc-steady-state-check into master 2026-09-15 01:39:14 +00:00
Owner

Closes #14

Summary

Implements the OIDC steady-state Check (the :8083 gRPC ext_authz
OIDC Flow). Given a request, the Check resolves the App by the
request's Host, gates on the fail-closed status model, validates the
Session cookie's id_token against the Snapshot's JWKS, and either
forwards the verified identity (OK + injected claim headers) or sends
the browser to the authorization endpoint (a 302 with the state/nonce
cookie).

What changed

  • OIDC steady-state Check (internal/serve/oidc.go): replaces the
    stub Check with the real flow — App resolution by Host, the
    fail-closed status model, Session id_token validation
    (signature + exp + iss + aud via internal/jwt), and the
    OidcInjection claim→header injection on the OK path.
  • Fail-closed status model (ADR 0011): no App resolved → 403;
    App unavailable (secret not ready, no OIDC flow, or no signing keys)
    → 503; no/invalid Session → 302 to the authorization endpoint
    carrying the state/nonce cookie (appKey|nonce|returnTo) on the
    denied_response Set-Cookie. A failure never mis-authorizes.
  • State-model carry: the resolved state now carries the IdP
    issuer (registry.Entry.Shared.Issuer, set from the OauthClient's
    issuerURL) so the Check can validate the token's iss without a
    new K8s read on the serving path.
  • Session codec: the stateless Session is a plain cookie holding the
    id_token and the refresh_token (ADR 0007); the Check decodes
    the id_token.

Out of scope (a follow-on ticket, ADR 0008): the lazy refresh on the
Check path. The Check validates the presented id_token and goes
straight to OK/302.

Verification

Verified via the flow-serving server seam (Seam A): an in-process Server
serving a constructed Snapshot with a go-jose ES256 key in the JWKS and
signed fixture id_tokens — valid session (OK + injected headers),
no/expired/mis-signed/mismatched-aud/mismatched-iss session (302),
unknown Host (403), and the 5xx gates. GOTOOLCHAIN=go1.26.0 make lint and make test pass.

Closes #14 ## Summary Implements the OIDC steady-state `Check` (the `:8083` gRPC ext_authz OIDC Flow). Given a request, the `Check` resolves the App by the request's Host, gates on the fail-closed status model, validates the Session cookie's `id_token` against the Snapshot's JWKS, and either forwards the verified identity (OK + injected claim headers) or sends the browser to the authorization endpoint (a 302 with the state/nonce cookie). ## What changed - **OIDC steady-state Check** (`internal/serve/oidc.go`): replaces the stub `Check` with the real flow — App resolution by Host, the fail-closed status model, Session `id_token` validation (signature + `exp` + `iss` + `aud` via `internal/jwt`), and the `OidcInjection` claim→header injection on the OK path. - **Fail-closed status model (ADR 0011):** no App resolved → `403`; App unavailable (secret not ready, no OIDC flow, or no signing keys) → `503`; no/invalid Session → `302` to the authorization endpoint carrying the `state`/nonce cookie (`appKey|nonce|returnTo`) on the `denied_response` `Set-Cookie`. A failure never mis-authorizes. - **State-model carry:** the resolved state now carries the IdP `issuer` (`registry.Entry.Shared.Issuer`, set from the OauthClient's `issuerURL`) so the `Check` can validate the token's `iss` without a new K8s read on the serving path. - **Session codec:** the stateless Session is a plain cookie holding the `id_token` and the `refresh_token` (ADR 0007); the `Check` decodes the `id_token`. Out of scope (a follow-on ticket, ADR 0008): the lazy refresh on the `Check` path. The `Check` validates the presented `id_token` and goes straight to OK/302. ## Verification Verified via the flow-serving server seam (Seam A): an in-process Server serving a constructed Snapshot with a go-jose ES256 key in the JWKS and signed fixture `id_token`s — valid session (OK + injected headers), no/expired/mis-signed/mismatched-aud/mismatched-iss session (302), unknown Host (403), and the `5xx` gates. `GOTOOLCHAIN=go1.26.0 make lint` and `make test` pass.
add oidc steady-state check: session validation, claim injection, 302/403/5xx
Some checks failed
Lint / Run on Ubuntu (push) Has been cancelled
E2E Tests / Run on Ubuntu (push) Has been cancelled
Tests / Run on Ubuntu (push) Has been cancelled
Lint / Run on Ubuntu (pull_request) Has been cancelled
E2E Tests / Run on Ubuntu (pull_request) Has been cancelled
Tests / Run on Ubuntu (pull_request) Has been cancelled
1ce4a553e1
- internal/serve: the gRPC ext_authz OIDC Check resolves the App by
  Host, gates on the fail-closed status model (403 no App / 5xx
  unavailable / 302 re-login), validates the Session id_token against
  the Snapshot JWKS (signature + exp + iss + aud via internal/jwt), and
  injects the OidcInjection claims as OK headers. The no/invalid
  Session path 302s to the authorization endpoint with the state/nonce
  cookie on the denied_response Set-Cookie.
- internal/registry: the resolved state now carries the IdP issuer
  (Entry.Shared.Issuer, from the OauthClient issuerURL) so the Check
  can validate iss without a new K8s read on the serving path.

Verified via Seam A (in-process Server + constructed Snapshot + go-jose
ES256 key + signed fixture tokens); GOTOOLCHAIN=go1.26.0 make lint and
make test pass.
ginjiruu merged commit 541e1cb823 into master 2026-09-15 01:39:14 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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
lab/authz-bridge!23
No description provided.