Skip to content

test(deployment): rehearse OIDC upgrade on a persistent preview - #7424

Draft
snehasaisneha wants to merge 33 commits into
mainfrom
state-nonce-preview
Draft

snehasaisneha wants to merge 33 commits into
mainfrom
state-nonce-preview

Conversation

@snehasaisneha

@snehasaisneha snehasaisneha commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Temporary upgrade-test preview for #6994. Do not merge this PR into main.

The baseline starts from main at 87f6dc1 and changes only developmentDatabasePersistence to true. This disables values-driven recreation of the development databases; it does not attach persistent volumes or guarantee survival of pod replacement.

We will first verify the baseline Keycloak realm and browser login/logout, recording local screenshots and a test user. Only after that will state_nonce be merged into this preview branch, retaining the same database. We will then check that the original user and realm settings survive, apply the documented client-setting updates, and test login/logout and callback protections again.

This PR is for deployment verification, not a change to production defaults.

🚀 Preview: https://state-nonce-preview.loculus.org

anna-parker and others added 30 commits July 18, 2026 15:30
… key (#7368)

`consumeAuthRequest` looks up the stored login transaction with
`store[state]`. `store` is built by `Object.fromEntries(...)` so it
inherits from `Object.prototype`, and `state` is the raw value handed
back on the OIDC callback, never validated. A callback carrying
`state=constructor` — or `__proto__`, `toString`, `hasOwnProperty`,
`valueOf` — therefore resolves to an inherited property, `transaction
=== undefined` is never true, and the server-side `state` check is
skipped for those values.

Nothing downstream catches it. The object that comes back has no `nonce`
and no `codeVerifier`, and openid-client skips the nonce comparison when
both the expected nonce and the ID token claim are absent, and omits
`code_verifier` from the token request when it is undefined. So a
callback with one of those state values is accepted as though this
browser had started the login, with none of state, nonce or PKCE
actually gating it.

Whether that is reachable depends on the realm. A freshly imported realm
sets `pkce.code.challenge.method: S256` on `backend-client`, and
Keycloak then refuses to issue a code for an authorization request that
carries no `code_challenge`, so the path is blocked. A realm upgraded in
place is not protected: Keycloak skips re-importing a realm that already
exists, so `backend-client` keeps `redirectUris: https://<host>/*` and
gains no PKCE requirement. On those instances the chain can be walked
end to end and an attacker-supplied callback URL makes the visitor's
browser adopt the attacker's session — the login CSRF that `state`
exists to prevent. Because the callback branch now runs before the
cookie check, it can also replace the session of a user who is already
signed in.

The guard is `Object.prototype.hasOwnProperty.call(store, state)` rather
than the more idiomatic `Object.hasOwn`, because `Object.hasOwn` is
ES2022 and `website/tsconfig.json` pins `"lib": ["ES2021", "DOM"]`, so
it fails `npm run check-types`. Raising that pin is worth doing
separately; it is not needed here.

This is the only place the transaction store is indexed by
attacker-controlled input — `activeTransactions` iterates with
`Object.entries`, which sees own enumerable keys only, and
`addAuthRequest` indexes with a state it generated itself — so the
single guard is sufficient. `delete store[state]` is left as it is,
since deleting a key that is not an own property is a no-op.

The added tests fail against the unguarded lookup (six failures) and
pass with it, so they genuinely pin the behaviour rather than restating
it. Other test gaps on this branch are untouched here: there is still no
coverage for an oversized `returnTo`, for two logins started
concurrently in different tabs, or for an already-authenticated visitor
hitting the callback with an unknown state.

https://claude.ai/code/session_01DWz25xM6t728H59XpfV2TB

🚀 Preview: Add `preview` label to enable

---------

Co-authored-by: corneliusroemer-agent <cornelius.roemer+githubagent@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Sneha S <github@saisneha.com>
@snehasaisneha snehasaisneha added the preview Triggers a deployment to argocd label Sep 26, 2026
@claude claude Bot added the deployment Code changes targetting the deployment infrastructure label Sep 26, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deployment Code changes targetting the deployment infrastructure preview Triggers a deployment to argocd

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants