Wire Kratos session tokenizer template (todo §3); plainpages JWT (sub/email/roles), 10m TTL, Jsonnet claims mapper reading metadata_admin

This commit is contained in:
2026-06-17 12:02:21 +02:00
parent 0313f48112
commit 95c759d773
5 changed files with 49 additions and 4 deletions

View File

@@ -62,7 +62,7 @@ everything via Docker.
- [x] Kratos self-service flows (login, registration, recovery, verification, settings) → return URLs at our themed pages. → `ory/kratos/kratos.yml`: all five flows enabled, each `ui_url` (+ after/return URLs) points at our web routes (`/login`, `/registration`, `/recovery`, `/verification`, `/settings`; §4 renders the fields). Recovery + verification run on the email `code` method (login stays password-only — `code.passwordless_enabled` left default-off); registration after-hooks `session` + `show_verification_ui`; settings gets `privileged_session_max_age` + `required_aal: highest_available`. Added a `courier` (SMTP) sending to a pinned dev mail catcher — **mailpit** (`axllent/mailpit:v1.30.1`) in `compose.override.yml`, web UI on `:8025`; prod overrides `COURIER_SMTP_CONNECTION_URI`. Kratos `serve` now runs `--watch-courier` so queued codes actually dispatch (without it they sit "queued"). Tests-first (`kratos.test.ts`: five flow ui_urls → our pages, recovery/verification use `code` + courier + `--watch-courier`, mailpit pin). Boot-verified end-to-end: all four public browser-flows 303 → `127.0.0.1:3000/<flow>?flow=…`, a registration delivered a real "Use code … to verify your account" email to mailpit (queue → `sent`); torn down. typecheck + 120 units green.
- [x] Kratos OIDC/SSO providers (Google/Microsoft/SAML) config (secrets via env). **None enabled by default** — a clean clone runs password-only; a provider activates purely by supplying its env creds. → `ory/kratos/kratos.yml` adds the `oidc` method present-but-disabled with an empty `providers: []` (clean clone = password-only, boots clean). Activation is pure env, no code/rebuild: `SELFSERVICE_METHODS_OIDC_ENABLED=true` + `SELFSERVICE_METHODS_OIDC_CONFIG_PROVIDERS=[…]` (the whole-array override is the only env-settable form Kratos offers — nested-field env vars aren't supported). Providers (`google`/`microsoft`/OIDC bridges) carry their `client_id`/`client_secret` and reference the committed shared claims mapper `ory/kratos/oidc/claims.jsonnet` (provider claims → `email` + `name{first,last}`). **SAML isn't in OSS Kratos** (Enterprise/Network/Polis only) — documented: front it with an OIDC bridge (Ory Polis) and register that bridge as a generic OIDC provider. README **Social sign-in (SSO)** section documents activation; §4 will derive the buttons from the live provider list. Tests-first (`kratos.test.ts`: oidc disabled + empty by default, mapper maps email/name). Boot-verified both halves: clean stack → login flow has only `default`+`password` groups; a one-off kratos with the SSO env → login flow gains an `oidc` group + a `google` button, no boot errors; torn down. typecheck + 122 units green.
- [x] Kratos session settings (cookie name, lifespan, sliding refresh). → `ory/kratos/kratos.yml` adds a `session` block: branded cookie `name: plainpages_session` (`persistent: true`, `same_site: Lax`), `lifespan: 720h` (30d "stay signed in" backbone the app re-mints the ~10m JWT off, §4), and sliding refresh via `earliest_possible_extend: 24h` (an active session extends back to full lifespan only once within 24h of expiry — no DB write per request). Tests-first (`kratos.test.ts`: cookie name + lifespan + extend window). Boot-verified: kratos serves `/health/ready` 200 with the block; a real browser registration (one-off `--dev` kratos, since Secure cookies don't ride plain http — that's the line-69 split) issued `Set-Cookie: plainpages_session=…; Max-Age=2591999; Expires=…; HttpOnly; SameSite=Lax` — name/persistent/lifespan all as configured; torn down. typecheck + 123 units green.
- [ ] Kratos tokenizer template `plainpages`: claims `{ sub, email, roles }`, `ttl ≈ 10m`, `jwks_url` signer, `claims_mapper_url` (Jsonnet reading `metadata_admin.roles`).
- [x] Kratos tokenizer template `plainpages`: claims `{ sub, email, roles }`, `ttl ≈ 10m`, `jwks_url` signer, `claims_mapper_url` (Jsonnet reading `metadata_admin.roles`).`ory/kratos/kratos.yml` adds `session.whoami.tokenizer.templates.plainpages`: `ttl: 10m`, `subject_source: id` (sub = identity id), `claims_mapper_url`/`jwks_url` pointing at the mounted config dir. `ory/kratos/tokenizer/plainpages.jsonnet` is the claims mapper — `email` from `session.identity.traits.email`, `roles` from the `metadata_admin` projection (§4 refreshes it from Keto at login; absent on a fresh identity ⇒ `[]`, defensive `objectHas`). `sub` is fixed to the identity id by Kratos (`subject_source`), not the mapper. The JWKS signing key referenced by `jwks_url` is generated/mounted by the next §3 item — Kratos loads it lazily at tokenize time, so this boots clean. Tests-first (`kratos.test.ts`: template ttl/subject_source/urls + mapper email/roles-from-metadata_admin). Boot-verified: kratos serves `/admin/health/ready` 200 with the tokenizer wired (config schema accepts the block); torn down. typecheck + 125 units green.
- [ ] Generate + mount the JWT signing JWKS; document key rotation.
- [ ] `keto` service (pinned) + `migrate`; namespaces in OPL (`role`, `group`, resource permissions).
- [ ] `hydra` service (pinned) + `migrate`; issuer + login/consent URLs → our app.