docs(auth): verify open/SMART auth end to end + local Keycloak (#198)#207
docs(auth): verify open/SMART auth end to end + local Keycloak (#198)#207angela-helios wants to merge 2 commits into
Conversation
Deliverable for #198: end-to-end verification of open/SMART auth against the committed local Keycloak, the runbook, the UI login-flow design, and an IdP login-screen theming matrix. - docs/auth-verification.md: what was verified and how (discovery, 401, scope enforcement full/read-only, signature, algorithm allow-list, issuer, JWKS, JTI, audience), the Keycloak runbook, the browser Authorization Code + PKCE login-flow design and what HFS must add, and a login-screen customization matrix across Keycloak/Okta/Auth0/Entra with a recommendation. Two defects found and filed while verifying: - #205: the JTI replay cache rejects legitimate reuse of bearer access tokens (default config unusable against Keycloak; workaround HFS_AUTH_JTI_BACKEND=disabled). - #206: HFS_AUTH_AUDIENCE is not enforced when the token omits aud. Low-risk fixes included so the runbook works as written: - docker/keycloak/realm.json: add a hardcoded-audience mapper (aud: hfs-api) to both service-account clients so audience validation is exercisable locally (verified: matching aud 200, wrong aud 401). - docker/keycloak/get-token.sh: parse the token response with whichever of jq/node/python3 actually works, not python3 only (a Windows stub alias resolves but fails).
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Deliver the section-4 recommendation from the auth verification: brand the Keycloak login screen in the IdP (HFS is not the auth server). - docker/keycloak/themes/helios/: CSS-only login theme extending the stock keycloak theme -- Helios logo, Figtree, accent #33b8ff, light surface, from the Dashboard V1.1 tokens (crates/ui/assets/app.css). No template forks, so it survives Keycloak upgrades. Includes the password show/hide input-group styling for Keycloak 26. - docker-compose.yml: mount ./themes read-only. - realm.json: select the theme (loginTheme: helios), rename the realm display to Helios FHIR Server, and add the scaffolding to render the browser login end to end -- a public Authorization Code + PKCE client (hfs-web) and a demo/demo test user. These are what the #198 section-3 browser-login build will plug into. - docs/auth-verification.md: recommendation and notes updated from recommended to shipped for the theme and interactive client. Rendered and verified via headless Chrome against the running Keycloak.
Helios login theme (part 4 of #198 — login-screen customization)Answering the customization question concretely: yes, and here it is. Since HFS is not the auth server, the login screen is IdP-owned — so this brands Keycloak (the IdP we bundle) with a CSS-only theme: Helios logo, Figtree, accent Wired up: Rendered and verified via headless Chrome against the running Keycloak. The password show/hide input-group needed extra styling for Keycloak 26 — a defect caught only because the page was actually rendered, not curl'd. |

Deliverable for #198. Everything in the verification section was executed against a running stack (HFS + Keycloak 26.1 from
docker/keycloak/), not read off the code.docs/auth-verification.mdcovers401with no token, SMART v2 scope enforcement (full-access writes; read-only client gets403on create and on an out-of-scope type), signature/tampering, algorithm allow-list, issuer, JWKS fetch, JTI (all three backends), and audience (positive + negative).Defects found while verifying (filed separately)
401ed. Single-usejtibelongs toprivate_key_jwtclient assertions (IdP-side), not resource-server access tokens. With the defaultmemorybackend HFS is effectively unusable against Keycloak untilHFS_AUTH_JTI_BACKEND=disabled.redisbehaves the same (persistence across restarts verified).HFS_AUTH_AUDIENCEis bypassed by a token with noaudclaim (jsonwebtokenonly validatesaudwhen present).Neither is fixed here — this pass verifies and reports.
Low-risk fixes included (so the runbook works as written)
docker/keycloak/realm.json: hardcoded-audience mapper (aud: hfs-api) on both service clients, so audience validation is exercisable locally — verified matchingaud→200, wrong expectedaud→401. (Makes auth: HFS_AUTH_AUDIENCE is not enforced when the token lacks an aud claim #206 observable; doesn't fix the missing-claim bypass.)docker/keycloak/get-token.sh: parse the token JSON with whichever ofjq/node/python3actually works (a Windowspython3stub alias resolves but fails), instead ofpython3only.Not covered (called out in the doc)
private_key_jwtwith a real asymmetric client assertion — the committed realm's clients useclient-secret; HFS's validation role is identical either way, but the advertised capability isn't exercised until the realm gains such a client.private_key_jwt+ RS384/ES384 while the realm usesclient-secret+ RS256 — a config-fidelity gap (documented, not filed).Closes #198.