Skip to content

Prevent caching OIDC redirects#1344

Merged
lovasoa merged 2 commits into
mainfrom
fix/oidc-cache-control
Jul 10, 2026
Merged

Prevent caching OIDC redirects#1344
lovasoa merged 2 commits into
mainfrom
fix/oidc-cache-control

Conversation

@lovasoa

@lovasoa lovasoa commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1341.

Reproduction

The supplied Chrome HAR makes the loop deterministic without Android:

  1. The response for GET / is a 303 to Entra containing state S. It has Cache-Control: max-age=86400 and an Expires value one day later.
  2. Chrome stores that redirect. After the callback for S succeeds, SQLPage removes sqlpage_oidc_state_S and redirects to /.
  3. Chrome serves the cached 303 for /, rather than requesting SQLPage. It follows the old Entra authorization URL, which returns a fresh code but the same state S.
  4. The callback cannot find the consumed state cookie, so SQLPage starts another login. Its fresh callback succeeds and redirects to /, returning to step 3.

The HAR proves the cache reuse: entries at 22:52:41, 22:52:43, 22:52:44, and 22:52:45 all redirect to the identical authorization URL/state Ohcyc0-izJdjD6uGl0zFkg, while their response Date is 22:51:03. The final navigation sends Cache-Control: max-age=0, bypasses the stale redirect, and succeeds. The HAR is from desktop Chrome 150.

I also reproduced this with a local app/IdP and a real Chrome 150 persistent profile: marking the state-bearing 303 as max-age=86400 produces net::ERR_TOO_MANY_REDIRECTS; changing only that header to no-store completes authentication normally.

Fix

Send Cache-Control: no-store on OIDC authorization and post-login redirects. Deployments should also ensure Apache/CDN configuration does not overwrite this directive or cache redirects / responses with Set-Cookie.

test_oidc_cached_authorization_redirect_cannot_replay_consumed_state is an integration-level reproduction of the HAR sequence. It models the observed proxy-added freshness directive and Chrome private-cache behavior: it completes a valid login, reuses the cached state-bearing authorization redirect without replaying its Set-Cookie, and obtains a callback with the now-consumed state. Before this change the test follows that callback to SQLPage's retry redirect and fails; with no-store, the browser model re-requests / with its auth cookie and receives 200.

Validation

  • cargo fmt --all
  • cargo test --test mod oidc::
  • cargo clippy --all-targets --all-features -- -D warnings

@lovasoa lovasoa force-pushed the fix/oidc-cache-control branch from 2dcdbcf to 09b6c2b Compare July 10, 2026 06:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 09b6c2ba8d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/oidc/mod.rs
Comment on lines +272 to +273
.get_all(header::CACHE_CONTROL)
.filter_map(|value| value.to_str().ok())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Call into_iter before filtering header values

When compiling this new OIDC test, HeaderMap::get_all(...) returns a GetAll collection rather than an Iterator, so chaining .filter_map(...) here fails with E0599. Add .iter() or .into_iter() before filter_map; otherwise the OIDC test target and CI cannot build.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 65f2d5cefc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CHANGELOG.md
@lovasoa lovasoa merged commit 8b67a06 into main Jul 10, 2026
51 checks passed
@lovasoa lovasoa deleted the fix/oidc-cache-control branch July 10, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OIDC redirect loop on cold browser: "No sqlpage_oidc_state_* cookie found" (variant of #1014)

1 participant