feat(k8s): optionally auto-fill the medewerker OTP step for the public demo (refs #177) #181

Merged
not merged 4 commits from feat/demo-otp-autofill into main 2026-09-25 11:34:31 +00:00
Contributor

What & why

For the public demo on big-behandel / big-beheer, visitors should see MFA being enforced without needing an authenticator app. This adds an opt-in Keycloak theme that fills in and submits the medewerker OTP code itself.

  • Theme as real files in infra/keycloak/themes/big-demo/, next to the realms:
    • login/theme.properties: keycloak.v2 plus scripts=js/otp-autofill.js. I checked the 26.1 source: keycloak.v2 loads theme scripts and sets none of its own.
    • login/resources/js/otp-autofill.js: on the OTP page, computes the code (RFC 6238, Keycloak's default policy) from the fixture secret BIGMEDEWERKEROTPSEED and submits it.
    • account, admin, email: plain children of Keycloak 26's defaults. Without them the account console returns 500 (see notes).
  • Seeded like every other file input: infra/helm/seed-configmaps.sh creates the rr-kc-theme ConfigMap, and the chart mounts it as a directory. The podspec gains items so flat ConfigMap keys map to theme paths. Keycloak runs start-dev (no theme cache), so edits show up about a minute after a reseed.
  • Switch: demo.otpAutofill only decides whether KC_SPI_THEME_DEFAULT=big-demo is set. big.env now skips env values that render empty, and no existing env var is empty. Off, the render is identical to main except for that one missing variable, so Keycloak keeps its stock theme. The realm JSONs are untouched, so compose and the e2e tests still require a code.
  • Single-use codes: a second login in the same 30 s window spends the next counter, as nextUnusedCounter does in the e2e. Past that it only fills in the field and doesn't submit, so a rejected code can't loop.
  • Deploy workflow: repo variable OTP_AUTOFILL=true → --set demo.otpAutofill=true. Flipping it changes the pod's env, so Keycloak restarts.

Refs #177

Definition of Done

  • Linked Gitea issue (above).
  • Failing test committed before the implementation. (Not done; checks below.)
  • Conventional Commits referencing the issue (refs #NN).
  • CI green
  • docker compose up unaffected (chart only).
  • Docs updated (Talos runbook, "Publishing through the labs Caddy").
  • ADR. The fixture-secret trade-off is ADR-0031's; this only automates typing it in.

Notes for reviewers

  • Tested on the live cluster. I patched the running Keycloak with the rendered theme (autofill on) and ran real headless Chromium logins against the public hosts:
    • merel-behandelaar on big-behandel: only username and password typed. The OTP page loaded the script, submitted by itself, and the user landed in the Werkbak.
    • jan-burger on big-mijn still logs in (regression check).
    • /realms/medewerker/account/ returns 200.
  • Account console 500, found live and fixed in the second commit. KC_SPI_THEME_DEFAULT applies to every theme type, and Keycloak does not fall back for a type the theme lacks (NullPointerException ... "theme" is null). big-demo now declares login, account, admin and email, each a plain child of Keycloak 26's default. It's one ConfigMap mounted as a directory; the podspec gains items for that.
  • Keycloak restarts cause about 5 minutes of BFF 401s. This is not caused by this PR, but you'll see it whenever Keycloak restarts. Dev-mode Keycloak makes new signing keys on each boot, and the BFF refreshes its cached keys at most every 5 minutes. Seen live: 401 right after the restart, 204 about 4½ minutes later. Flipping OTP_AUTOFILL restarts Keycloak, so expect this briefly.
  • make k8s-lint and make k8s-drift pass. The rendered script's code matches infra/keycloak/check_realms.py otp.
  • Security: with it on, the public behandel and beheer portals are protected only by the committed password test123. That's intentional for synthetic demo data. Never enable it anywhere real.

🤖 Generated with Claude Code

## What & why For the public demo on `big-behandel` / `big-beheer`, visitors should see MFA being enforced without needing an authenticator app. This adds an opt-in Keycloak theme that fills in and submits the medewerker OTP code itself. - **Theme as real files in `infra/keycloak/themes/big-demo/`**, next to the realms: - `login/theme.properties`: `keycloak.v2` plus `scripts=js/otp-autofill.js`. I checked the 26.1 source: `keycloak.v2` loads theme `scripts` and sets none of its own. - `login/resources/js/otp-autofill.js`: on the OTP page, computes the code (RFC 6238, Keycloak's default policy) from the fixture secret `BIGMEDEWERKEROTPSEED` and submits it. - `account`, `admin`, `email`: plain children of Keycloak 26's defaults. Without them the account console returns 500 (see notes). - **Seeded like every other file input:** `infra/helm/seed-configmaps.sh` creates the `rr-kc-theme` ConfigMap, and the chart mounts it as a directory. The podspec gains `items` so flat ConfigMap keys map to theme paths. Keycloak runs `start-dev` (no theme cache), so edits show up about a minute after a reseed. - **Switch:** `demo.otpAutofill` only decides whether `KC_SPI_THEME_DEFAULT=big-demo` is set. `big.env` now skips env values that render empty, and no existing env var is empty. **Off, the render is identical to main except for that one missing variable,** so Keycloak keeps its stock theme. The realm JSONs are untouched, so compose and the e2e tests still require a code. - **Single-use codes:** a second login in the same 30 s window spends the next counter, as `nextUnusedCounter` does in the e2e. Past that it only fills in the field and doesn't submit, so a rejected code can't loop. - **Deploy workflow:** repo variable `OTP_AUTOFILL=true` → `--set demo.otpAutofill=true`. Flipping it changes the pod's env, so Keycloak restarts. Refs #177 ## Definition of Done - [x] Linked Gitea issue (above). - [x] Failing test committed before the implementation. *(Not done; checks below.)* - [x] Conventional Commits referencing the issue (`refs #NN`). - [x] CI green - [x] `docker compose up` unaffected (chart only). - [x] Docs updated (Talos runbook, "Publishing through the labs Caddy"). - [ ] ADR. The fixture-secret trade-off is ADR-0031's; this only automates typing it in. ## Notes for reviewers - **Tested on the live cluster.** I patched the running Keycloak with the rendered theme (autofill on) and ran real headless Chromium logins against the public hosts: - `merel-behandelaar` on big-behandel: only username and password typed. The OTP page loaded the script, submitted by itself, and the user landed in the Werkbak. - `jan-burger` on big-mijn still logs in (regression check). - `/realms/medewerker/account/` returns 200. - **Account console 500, found live and fixed in the second commit.** `KC_SPI_THEME_DEFAULT` applies to every theme type, and Keycloak does *not* fall back for a type the theme lacks (`NullPointerException ... "theme" is null`). `big-demo` now declares login, account, admin and email, each a plain child of Keycloak 26's default. It's one ConfigMap mounted as a directory; the podspec gains `items` for that. - **Keycloak restarts cause about 5 minutes of BFF 401s.** This is not caused by this PR, but you'll see it whenever Keycloak restarts. Dev-mode Keycloak makes new signing keys on each boot, and the BFF refreshes its cached keys at most every 5 minutes. Seen live: 401 right after the restart, 204 about 4½ minutes later. Flipping `OTP_AUTOFILL` restarts Keycloak, so expect this briefly. - `make k8s-lint` and `make k8s-drift` pass. The rendered script's code matches `infra/keycloak/check_realms.py otp`. - **Security:** with it on, the public behandel and beheer portals are protected only by the committed password `test123`. That's intentional for synthetic demo data. Never enable it anywhere real. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
not added 1 commit 2026-09-25 09:52:33 +00:00
feat(k8s): optionally auto-fill the medewerker OTP step for the public demo (refs #177)
CI / k8s (pull_request) Successful in 13s
CI / build (pull_request) Successful in 5m36s
CI / unit (pull_request) Successful in 6m18s
CI / frontend (pull_request) Successful in 3m12s
CI / verify-stack (pull_request) Canceled after 0s
CI / mutation (pull_request) Canceled after 8m0s
CI / lint (pull_request) Canceled after 21m26s
1cc04de630
A `big-demo` Keycloak login theme (keycloak.v2 + one script) is always mounted
and set as default. With demo.otpAutofill (repo variable OTP_AUTOFILL=true) the
script computes the code from the committed fixture secret and submits it, so
the demo shows MFA enforced without an authenticator. Off by default: the
script is empty and the login is plain keycloak.v2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
not added 1 commit 2026-09-25 10:02:49 +00:00
fix(k8s): declare every theme type in big-demo so the account console loads (refs #177)
CI / lint (pull_request) Successful in 2m0s
CI / k8s (pull_request) Successful in 9s
CI / build (pull_request) Successful in 1m22s
CI / unit (pull_request) Successful in 1m34s
CI / verify-stack (pull_request) Canceled after 0s
CI / frontend (pull_request) Canceled after 3m8s
CI / mutation (pull_request) Canceled after 8m10s
6cd2268be9
KC_SPI_THEME_DEFAULT applies to all theme types and Keycloak does not fall back
for one the theme lacks: the account console returned 500 (NPE on a null
theme). The theme is now one ConfigMap mounted as a directory with login,
account, admin and email types; the podspec gains `items` for that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
not added 1 commit 2026-09-25 10:15:40 +00:00
refactor(k8s): keep the big-demo theme as real files under infra/keycloak/themes (refs #177)
CI / lint (pull_request) Successful in 1m55s
CI / k8s (pull_request) Successful in 9s
CI / build (pull_request) Successful in 1m27s
CI / unit (pull_request) Successful in 1m51s
CI / frontend (pull_request) Successful in 3m24s
CI / mutation (pull_request) Successful in 6m40s
CI / verify-stack (pull_request) Failing after 1m1s
0ea562db52
The theme was inline text in a Helm template. It now lives next to the realms
and is seeded as rr-kc-theme by seed-configmaps.sh, like every other file
input, so it can be edited as a normal Keycloak theme. demo.otpAutofill now
only decides whether KC_SPI_THEME_DEFAULT is set (big.env skips values that
render empty); off, Keycloak keeps its stock theme.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
not added 1 commit 2026-09-25 11:12:22 +00:00
Merge branch 'main' into feat/demo-otp-autofill
CI / lint (pull_request) Successful in 1m44s
CI / k8s (pull_request) Successful in 12s
CI / build (pull_request) Successful in 1m17s
CI / unit (pull_request) Successful in 1m43s
CI / frontend (pull_request) Successful in 2m47s
CI / mutation (pull_request) Successful in 5m2s
CI / verify-stack (pull_request) Successful in 9m14s
408cc32594
not merged commit 0074a1bff3 into main 2026-09-25 11:34:31 +00:00
Sign in to join this conversation.