Skip to content

fix(auth_n): try refresh oidc client on token exchange failure#1088

Merged
ayushjain17 merged 7 commits into
mainfrom
oidc_client_refresh
Jul 17, 2026
Merged

fix(auth_n): try refresh oidc client on token exchange failure#1088
ayushjain17 merged 7 commits into
mainfrom
oidc_client_refresh

Conversation

@ayushjain17

@ayushjain17 ayushjain17 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Change log

  1. Try to refresh on every token exchange with OIDC IdP in case the exchange fails and use the refreshed info to re-try token exchange - refresh happens only during the call.

Note: The refresh would keep happening on every fail token exchange if a successful refresh was not achieved. token exchange re-try is only done once, re-try failure will lead to a terminal state in the user login experience avoiding the infinite redirects.

  1. Reduce auth cookie to only contain only id_token
  2. Add support of using id_token as Bearer token value

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added direct token-based authentication support across enabled and disabled modes.
    • Improved OIDC sign-in by refreshing provider metadata and reusing refreshed client configuration.
  • Bug Fixes
    • Prevents login failures caused by stale signing keys by retrying ID token verification once after refresh.
    • Returns a clear “Sign-in failed” unauthorized response when verification still fails.
  • Refactor
    • Streamlined authentication middleware flow for consistent handling of login type and responses.

Copilot AI review requested due to automatic review settings July 9, 2026 11:35
@ayushjain17
ayushjain17 requested a review from a team as a code owner July 9, 2026 11:35
@semanticdiff-com

semanticdiff-com Bot commented Jul 9, 2026

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a33e94b3-e563-484f-8370-08685c1e5672

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

OIDC authentication now uses refreshable clients and direct ID-token verification. Failed verification triggers one metadata/client refresh and retry. Token-based authentication is supported across authenticators, and middleware authentication control flow is consolidated around a single future.

Changes

OIDC authentication refresh and verification

Layer / File(s) Summary
OIDC contracts, token types, and client helpers
crates/service_utils/src/middlewares/auth_n/oidc/{types,utils}.rs, crates/service_utils/src/middlewares/auth_n/oidc.rs
Updates client and token contracts, adds provider metadata discovery and client construction helpers, and replaces response-oriented token aliases with direct ID-token types.
Refreshable SaaS and Simple authenticators
crates/service_utils/src/middlewares/auth_n/oidc/{saas_authenticator,simple_authenticator}.rs
Stores typed configuration and lock-protected clients, rebuilds clients from refreshed provider metadata, and updates global/org client derivation and verification.
Direct token authentication
crates/service_utils/src/middlewares/auth_n/authentication.rs, crates/service_utils/src/middlewares/auth_n/no_auth.rs, crates/service_utils/src/middlewares/auth_n/oidc/{saas_authenticator,simple_authenticator}.rs
Adds authenticate_with_token to the authenticator contract and implementations, including direct global/org token decoding and user conversion.
Login verification retry flow
crates/service_utils/src/middlewares/auth_n/oidc.rs
Verifies ID tokens after exchange, refreshes and retries once on failure, then returns an Unauthorized sign-in failure response if verification still fails.
Unified middleware authentication flow
crates/service_utils/src/middlewares/auth_n.rs
Computes login type once and awaits one authentication future for authorization-header and fallback paths.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant OidcLogin
  participant OIDCAuthenticator
  participant OidcProvider

  Client->>OidcLogin: authorization code
  OidcLogin->>OidcProvider: exchange code for tokens
  OidcLogin->>OidcLogin: verify_id_token(current client)
  alt verification fails
    OidcLogin->>OIDCAuthenticator: refresh_client()
    OIDCAuthenticator->>OidcProvider: fetch provider metadata
    OIDCAuthenticator->>OIDCAuthenticator: build_client(metadata)
    OidcLogin->>OidcLogin: verify_id_token(refreshed client)
    OidcLogin-->>Client: authenticated result or Unauthorized sign-in failure
  else verification succeeds
    OidcLogin-->>Client: authenticated result
  end
Loading

Poem

A rabbit found a stale key in the hay,
Refreshed the client and hopped on its way.
Tokens were checked, then checked once more,
While locks kept the metadata safe at the door. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the main change: retrying OIDC after client refresh on authentication failure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch oidc_client_refresh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI 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.

Pull request overview

This PR updates the OIDC authentication flow to better tolerate IdP metadata/JWKS changes by allowing the OIDC client (and, for SaaS, provider metadata) to be refreshed at runtime and retrying ID-token verification once after a refresh. It also changes the terminal failure behavior to return an explicit “Sign-in failed” response instead of redirecting again (avoiding redirect loops).

Changes:

  • Add shared helpers to (re)discover provider metadata and rebuild a CoreClient.
  • Store OIDC clients (and SaaS provider metadata) behind locks so they can be swapped on refresh.
  • On ID-token verification failure, refresh provider metadata/client once and retry verification; on repeated failure, return an unauthorized error page.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
crates/service_utils/src/middlewares/auth_n/oidc/utils.rs Adds provider metadata discovery and CoreClient construction helpers for reuse and refresh.
crates/service_utils/src/middlewares/auth_n/oidc/simple_authenticator.rs Switches to a refreshable, lock-protected CoreClient and implements refresh_client().
crates/service_utils/src/middlewares/auth_n/oidc/saas_authenticator.rs Switches to refreshable, lock-protected CoreClient + provider metadata and implements refresh_client().
crates/service_utils/src/middlewares/auth_n/oidc.rs Extends the OIDC authenticator trait with refresh, retries ID-token verification after refresh, and adds a terminal failure response.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +141 to +147
// Verify the freshly-minted ID token. If verification fails, the most
// common cause is that the IdP has rotated its signing keys since we
// last fetched the JWKS, so the cached keys can't validate the new
// token's signature. Refresh the provider metadata once and retry
// before giving up — this self-heals key rotation without a restart.
let mut response =
verify_id_token(&data.get_client(), &token_response, &p_cookie.nonce);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

if no id token is there, then shouldn't the person login to get the info

Comment on lines +90 to +95
fn get_client(&self) -> CoreClient {
self.client
.read()
.unwrap_or_else(|e| e.into_inner())
.clone()
}
Comment on lines +225 to +230
fn get_client(&self) -> CoreClient {
self.client
.read()
.unwrap_or_else(|e| e.into_inner())
.clone()
}
Comment on lines +141 to +147
// Verify the freshly-minted ID token. If verification fails, the most
// common cause is that the IdP has rotated its signing keys since we
// last fetched the JWKS, so the cached keys can't validate the new
// token's signature. Refresh the provider metadata once and retry
// before giving up — this self-heals key rotation without a restart.
let mut response =
verify_id_token(&data.get_client(), &token_response, &p_cookie.nonce);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
crates/service_utils/src/middlewares/auth_n/oidc/saas_authenticator.rs (1)

232-253: 🚀 Performance & Scalability | 🔵 Trivial

Consider de-duplicating concurrent refreshes to avoid a discovery-fetch storm during key rotation.

refresh_client is invoked from the login handler on every ID-token verification failure. During a JWKS-rotation window, multiple in-flight logins will each fail verification and independently call refresh_client, issuing N concurrent discovery fetches against the IdP for what is effectively the same refresh. A single-flight guard (collapse concurrent refreshes into one) or a short minimum interval between refreshes would bound the load on the IdP discovery endpoint while preserving the self-heal behavior.

The two-step swap (metadata then client under separate write locks) is fine for the retry path, since both writes are in-memory and no lock is held across the await.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/service_utils/src/middlewares/auth_n/oidc/saas_authenticator.rs`
around lines 232 - 253, `refresh_client` in `SaasAuthenticator` is doing
redundant concurrent discovery fetches when multiple login attempts fail at the
same time during key rotation. Add a single-flight or other coalescing guard
around `refresh_client` so only one refresh runs per issuer at a time, and have
other callers reuse or await that in-flight refresh instead of calling
`fetch_provider_metadata` repeatedly. Keep the existing two-step swap into
`provider_metadata` and `client` as-is, but make the refresh entrypoint
resilient to concurrent invocations from the login handler.
crates/service_utils/src/middlewares/auth_n/oidc.rs (1)

208-208: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the redundant .map_err(|e| e)
claims(...) already returns ClaimsVerificationError, so the identity map_err adds no value and may trigger clippy::map_identity under -D warnings.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/service_utils/src/middlewares/auth_n/oidc.rs` at line 208, The
`claims(...)` call in `Oidc` token verification is wrapped with a redundant
`.map_err(|e| e)` identity conversion. Remove that `map_err` from the `and_then`
chain so the result from `client.id_token_verifier()` and `claims(...)` is
passed through directly without triggering the identity-map warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/service_utils/src/middlewares/auth_n/oidc.rs`:
- Line 208: The `claims(...)` call in `Oidc` token verification is wrapped with
a redundant `.map_err(|e| e)` identity conversion. Remove that `map_err` from
the `and_then` chain so the result from `client.id_token_verifier()` and
`claims(...)` is passed through directly without triggering the identity-map
warning.

In `@crates/service_utils/src/middlewares/auth_n/oidc/saas_authenticator.rs`:
- Around line 232-253: `refresh_client` in `SaasAuthenticator` is doing
redundant concurrent discovery fetches when multiple login attempts fail at the
same time during key rotation. Add a single-flight or other coalescing guard
around `refresh_client` so only one refresh runs per issuer at a time, and have
other callers reuse or await that in-flight refresh instead of calling
`fetch_provider_metadata` repeatedly. Keep the existing two-step swap into
`provider_metadata` and `client` as-is, but make the refresh entrypoint
resilient to concurrent invocations from the login handler.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d2124887-1eaa-46d6-a65a-b70094563ab2

📥 Commits

Reviewing files that changed from the base of the PR and between 77e840c and fdc2937.

📒 Files selected for processing (4)
  • crates/service_utils/src/middlewares/auth_n/oidc.rs
  • crates/service_utils/src/middlewares/auth_n/oidc/saas_authenticator.rs
  • crates/service_utils/src/middlewares/auth_n/oidc/simple_authenticator.rs
  • crates/service_utils/src/middlewares/auth_n/oidc/utils.rs

@ayushjain17
ayushjain17 force-pushed the oidc_client_refresh branch from fdc2937 to af596e7 Compare July 9, 2026 11:55
@ayushjain17

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/service_utils/src/middlewares/auth_n/oidc.rs`:
- Around line 141-161: The retry in the ID token verification flow still uses
the pre-refresh client and stale JWKS. Update verify_id_token to borrow only
token_response from the token response while accepting the client independently,
then re-fetch the client via data.get_client() after a successful
data.refresh_client() before retrying verification.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dc09bdfe-6081-454f-8df0-aef64fed821e

📥 Commits

Reviewing files that changed from the base of the PR and between 77e840c and fd669a6.

📒 Files selected for processing (8)
  • crates/service_utils/src/middlewares/auth_n.rs
  • crates/service_utils/src/middlewares/auth_n/authentication.rs
  • crates/service_utils/src/middlewares/auth_n/no_auth.rs
  • crates/service_utils/src/middlewares/auth_n/oidc.rs
  • crates/service_utils/src/middlewares/auth_n/oidc/saas_authenticator.rs
  • crates/service_utils/src/middlewares/auth_n/oidc/simple_authenticator.rs
  • crates/service_utils/src/middlewares/auth_n/oidc/types.rs
  • crates/service_utils/src/middlewares/auth_n/oidc/utils.rs

Comment thread crates/service_utils/src/middlewares/auth_n/oidc.rs Outdated
Ok(id_token)
}

fn sign_in_failed_response(retry_url: &str) -> HttpResponse {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we just redirect the user when they fall into this case?

@sauraww

sauraww commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

@ayushjain17 Can you resolve the conflicts ?

@ayushjain17
ayushjain17 force-pushed the oidc_client_refresh branch from fd669a6 to b47bd65 Compare July 15, 2026 17:28
Comment thread examples/token-introspection/server.js Dismissed
Comment thread crates/service_utils/src/middlewares/auth_n.rs Outdated
Comment thread crates/service_utils/src/middlewares/auth_n/oidc.rs Outdated
@ayushjain17
ayushjain17 force-pushed the oidc_client_refresh branch from 3c4ddab to 1eb6338 Compare July 17, 2026 09:45
@ayushjain17
ayushjain17 requested a review from sauraww July 17, 2026 10:05

@sauraww sauraww left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The refresh and retry is currently applied only during the OIDC login callback. Direct bearer-token verification still fails immediately with stale JWKS. Since this PR adds bearer ID token authentication, should authenticate_with_bearer_token also refresh metadata and retry once on claims-verification failure?

@ayushjain17

Copy link
Copy Markdown
Collaborator Author

The refresh and retry is currently applied only during the OIDC login callback. Direct bearer-token verification still fails immediately with stale JWKS. Since this PR adds bearer ID token authentication, should authenticate_with_bearer_token also refresh metadata and retry once on claims-verification failure?

not valid for this flow

@ayushjain17
ayushjain17 requested a review from sauraww July 17, 2026 10:32
@sauraww

sauraww commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

The refresh and retry is currently applied only during the OIDC login callback. Direct bearer-token verification still fails immediately with stale JWKS. Since this PR adds bearer ID token authentication, should authenticate_with_bearer_token also refresh metadata and retry once on claims-verification failure?

not valid for this flow

Valid as discussed offline

@ayushjain17
ayushjain17 force-pushed the oidc_client_refresh branch from 0c86a63 to 0887816 Compare July 17, 2026 13:15
@ayushjain17
ayushjain17 enabled auto-merge July 17, 2026 13:23
@ayushjain17
ayushjain17 disabled auto-merge July 17, 2026 13:23
@ayushjain17
ayushjain17 merged commit d3ebf3e into main Jul 17, 2026
29 of 37 checks passed
@ayushjain17
ayushjain17 deleted the oidc_client_refresh branch July 17, 2026 13:24
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.

5 participants