Skip to content

Conversation

@parmesant
Copy link
Contributor

@parmesant parmesant commented Dec 22, 2025

In case the jwks becomes invalid, create a new oidc client with a set of fresh keys
Fixes #XXXX.

Description


This PR has:

  • been tested to ensure log ingestion and log query works.
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added documentation for new or modified features or behaviors.

Summary by CodeRabbit

  • Chores

    • Updated openid dependency to v0.18.3.
  • Bug Fixes

    • Token refresh now fails gracefully and clears stale sessions on failure.
    • Login/logout flows return Unauthorized when the OIDC client cannot be resolved.
    • Improved error handling and logging for token and session errors.
  • Improvements

    • Introduced a global OIDC client mechanism with automatic recovery on token validation failures, reducing unexpected login interruptions and improving authentication reliability.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Walkthrough

Replaces per-request OIDC client storage with a global, lazily-initialized OnceCell<Option<Arc<RwLock<GlobalClient>>>>; upgrades openid to 0.18.3; updates handlers and middleware to use the global client, adds id_token decode-and-retry that can replace the global client, and tightens refresh error handling.

Changes

Cohort / File(s) Summary
Dependency upgrade
Cargo.toml
Updated openid dependency from 0.15.00.18.3 (keeps default-features = false, features = ["rustls"])
Global OIDC client state
src/handlers/http/modal/mod.rs
Added public static OIDC_CLIENT: OnceCell<Option<Arc<RwLock<GlobalClient>>>>; introduced GlobalClient with constructor, set, and client accessors; initialize global client at startup and removed per-request app-data injection
Handlers: login/reply/request token
src/handlers/http/oidc.rs
login and reply_login now read OIDC client from global OIDC_CLIENT (async .read().await); reply_login signature removed HttpRequest; request_token now accepts &Arc<RwLock<GlobalClient>>; on id_token decode failure, recreate client, retry validation, and update global store
Middleware: token refresh & session handling
src/handlers/http/middleware.rs
Switched from per-request app-data client to global OIDC_CLIENT; explicit matching on refresh results, log errors, remove session on refresh failure and return 401 Unauthorized; updated imports to use modal::OIDC_CLIENT and rbac::RBACError

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Client
    participant Handler as HTTP Handler
    participant Global as OIDC_CLIENT\n(OnceCell + RwLock<GlobalClient>)
    participant Provider as OIDC Provider
    participant Middleware as Auth Middleware

    Browser->>Handler: GET /login (authorize)
    Handler->>Global: read() -> DiscoveredClient
    Global-->>Handler: client
    Handler->>Provider: redirect / exchange code
    Provider-->>Handler: auth code / tokens
    Handler->>Global: read() use client to decode id_token
    alt decode succeeds
      Handler-->>Browser: set session / redirect
    else decode fails
      Handler->>Provider: build new client, retry decode
      Handler->>Global: write() replace GlobalClient
      Global-->>Handler: new client installed
      Handler-->>Browser: proceed if decode succeeds
    end

    Browser->>Middleware: request with session
    Middleware->>Global: read() client for refresh
    Middleware->>Provider: refresh token
    alt refresh succeeds
      Provider-->>Middleware: new tokens
      Middleware-->>Browser: continue request
    else refresh fails
      Middleware->>Middleware: log, remove session
      Middleware-->>Browser: 401 Unauthorized
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect OnceCell initialization and Arc<RwLock<GlobalClient>> read/write usage for race conditions.
  • Review request_token client-recreation and global replacement for correctness and concurrency safety.
  • Verify middleware session removal semantics and consistent error handling/messages.

Poem

🐇 I found a client, snug and neat,

OnceCell keeps it safe and fleet,
Locks in place, we hop and play,
Tokens hop home without delay,
Hooray for hops to 0.18.3 ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description provides context about the bug (invalid JWKS) and solution (creating new client), but lacks specific implementation details, testing evidence, and has unchecked critical items in the template checklist. Check the three template items confirming testing was performed, comments explain code intent, and documentation was added. Provide the actual issue number in 'Fixes #XXXX'. Add implementation details if needed.
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: creating a new OIDC client when token decoding fails, which aligns with the primary fix implemented in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dcfa19 and 6e44dce.

📒 Files selected for processing (2)
  • src/handlers/http/modal/mod.rs
  • src/handlers/http/oidc.rs
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-28T02:10:41.140Z
Learnt from: nikhilsinhaparseable
Repo: parseablehq/parseable PR: 1453
File: src/parseable/mod.rs:397-400
Timestamp: 2025-10-28T02:10:41.140Z
Learning: In Parseable enterprise deployments with multiple query nodes, hot tier configuration must be persisted in object storage so that newly started query nodes can fetch and synchronize the hot tier settings at startup (file: src/parseable/mod.rs, function: create_stream_and_schema_from_storage).

Applied to files:

  • src/handlers/http/modal/mod.rs
📚 Learning: 2025-10-21T02:22:24.403Z
Learnt from: nikhilsinhaparseable
Repo: parseablehq/parseable PR: 1448
File: src/parseable/mod.rs:419-432
Timestamp: 2025-10-21T02:22:24.403Z
Learning: In Parseable's internal stream creation (`create_internal_stream_if_not_exists` in `src/parseable/mod.rs`), errors should not propagate to fail server initialization. The function creates both pmeta and pbilling internal streams, and failures are logged but the function always returns `Ok(())` to ensure server startup resilience. Individual stream creation failures should not prevent syncing of successfully created streams.

Applied to files:

  • src/handlers/http/oidc.rs
🧬 Code graph analysis (1)
src/handlers/http/oidc.rs (1)
src/handlers/http/modal/mod.rs (3)
  • client (75-77)
  • token (589-589)
  • token (599-601)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Build Default x86_64-unknown-linux-gnu
  • GitHub Check: Build Default aarch64-unknown-linux-gnu
  • GitHub Check: Build Default x86_64-pc-windows-msvc
  • GitHub Check: Build Default aarch64-apple-darwin
  • GitHub Check: Build Kafka x86_64-unknown-linux-gnu
  • GitHub Check: Build Kafka aarch64-apple-darwin
  • GitHub Check: Quest Smoke and Load Tests for Distributed deployments
  • GitHub Check: Quest Smoke and Load Tests for Standalone deployments
  • GitHub Check: coverage
🔇 Additional comments (5)
src/handlers/http/oidc.rs (3)

83-86: LGTM: Safe retrieval of global OIDC client.

The pattern correctly handles both the outer OnceCell and inner Option without panicking. When OIDC is not configured, OIDC_CLIENT.get() returns Some(&None), and .as_ref().cloned() safely produces None.


176-183: LGTM: Safe retrieval and error handling.

The let-chain pattern correctly handles the nested Option structure, and returns a proper Unauthorized response when OIDC is not configured.


411-433: LGTM: Token decode recovery mechanism is well-implemented.

The recovery path correctly reuses the already-obtained token and validates it with the newly created client's JWKS keys. The drop(old_client) on line 429 properly releases the read lock before acquiring the write lock, avoiding potential deadlock.

src/handlers/http/modal/mod.rs (2)

63-82: LGTM: Well-designed global client wrapper.

The GlobalClient struct provides a clean abstraction over the DiscoveredClient with appropriate methods for setting and accessing the inner client. The OnceCell<Option<Arc<RwLock<GlobalClient>>>> pattern ensures thread-safe, lazy initialization while allowing the client to be updated during runtime (e.g., when JWKS keys need refresh).


116-123: LGTM: Proper initialization of global OIDC client.

The initialization correctly handles both cases:

  • When OIDC is configured, it connects and wraps the client in the global structure.
  • When OIDC is not configured, it explicitly initializes OIDC_CLIENT to None.

This resolves the previous concern about the OnceCell never being initialized when OIDC is disabled.


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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/handlers/http/oidc.rs (1)

151-158: Inconsistent: logout still uses app_data instead of global OIDC_CLIENT.

The logout function still retrieves the OIDC client via req.app_data::<Data<Option<DiscoveredClient>>>(), while all other functions have been migrated to use the global OIDC_CLIENT. This inconsistency may cause logout functionality to fail since the app_data is no longer being populated.

🔎 Proposed fix to use global OIDC_CLIENT
 pub async fn logout(req: HttpRequest, query: web::Query<RedirectAfterLogin>) -> HttpResponse {
-    let oidc_client = match req.app_data::<Data<Option<DiscoveredClient>>>() {
-        Some(client) => {
-            let c = client.clone().into_inner();
-            c.as_ref().clone()
-        }
-        None => None,
+    let oidc_client = match OIDC_CLIENT.get() {
+        Some(Some(client)) => Some(client.read().await.client().clone()),
+        _ => None,
     };
     let Some(session) = extract_session_key_from_req(&req).ok() else {
         return redirect_to_client(query.redirect.as_str(), None);
     };

Note: This will require making logout an async function if it isn't already, which it is.

🧹 Nitpick comments (2)
src/handlers/http/modal/mod.rs (1)

116-122: Remove unnecessary .clone() on client.

The client.clone() on line 121 is unnecessary since GlobalClient::new takes ownership of the client, and it's not used elsewhere after this point.

🔎 Proposed fix
         if let Some(config) = oidc_client {
             let client = config
                 .connect(&format!("{API_BASE_PATH}/{API_VERSION}/o/code"))
                 .await?;
             OIDC_CLIENT
-                .get_or_init(|| Some(Arc::new(RwLock::new(GlobalClient::new(client.clone())))));
+                .get_or_init(|| Some(Arc::new(RwLock::new(GlobalClient::new(client)))));
         }
src/handlers/http/oidc.rs (1)

415-420: Replace .unwrap() with proper error handling.

While reaching this code path implies OIDC was configured, using .unwrap() is fragile. If configuration state changes or there's a race condition, this will cause a panic.

🔎 Proposed fix
-        let new_client = PARSEABLE
+        let openid_config = PARSEABLE
             .options
             .openid()
-            .unwrap()
+            .ok_or_else(|| anyhow::anyhow!("OIDC configuration not available"))?;
+        let new_client = openid_config
             .connect(&format!("{API_BASE_PATH}/{API_VERSION}/o/code"))
             .await?;
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8114bc6 and 4646f92.

📒 Files selected for processing (4)
  • Cargo.toml
  • src/handlers/http/middleware.rs
  • src/handlers/http/modal/mod.rs
  • src/handlers/http/oidc.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-28T02:10:41.140Z
Learnt from: nikhilsinhaparseable
Repo: parseablehq/parseable PR: 1453
File: src/parseable/mod.rs:397-400
Timestamp: 2025-10-28T02:10:41.140Z
Learning: In Parseable enterprise deployments with multiple query nodes, hot tier configuration must be persisted in object storage so that newly started query nodes can fetch and synchronize the hot tier settings at startup (file: src/parseable/mod.rs, function: create_stream_and_schema_from_storage).

Applied to files:

  • src/handlers/http/modal/mod.rs
🧬 Code graph analysis (2)
src/handlers/http/middleware.rs (1)
src/handlers/http/modal/mod.rs (1)
  • client (75-77)
src/handlers/http/modal/mod.rs (2)
src/cli.rs (1)
  • openid (528-548)
src/handlers/http/modal/ssl_acceptor.rs (1)
  • get_ssl_acceptor (27-62)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Build Default x86_64-pc-windows-msvc
  • GitHub Check: Build Default aarch64-unknown-linux-gnu
  • GitHub Check: Build Kafka x86_64-unknown-linux-gnu
  • GitHub Check: Build Default x86_64-unknown-linux-gnu
  • GitHub Check: Build Kafka aarch64-apple-darwin
  • GitHub Check: Build Default aarch64-apple-darwin
  • GitHub Check: Quest Smoke and Load Tests for Distributed deployments
  • GitHub Check: Quest Smoke and Load Tests for Standalone deployments
  • GitHub Check: coverage
🔇 Additional comments (6)
src/handlers/http/modal/mod.rs (1)

65-82: LGTM!

The GlobalClient wrapper struct provides a clean API for managing the OIDC client lifecycle, with set() enabling client replacement when JWKS keys become invalid.

src/handlers/http/middleware.rs (2)

185-192: LGTM!

The migration to the global OIDC_CLIENT is correctly implemented. The read lock is acquired and released within a narrow scope, with the client being cloned to avoid holding the lock during the subsequent async operations.


211-224: Good security practice: invalidating session on refresh failure.

The explicit error handling ensures that users with expired/invalid tokens are properly logged out rather than left in an ambiguous state. The session removal on line 219 prevents lingering invalid sessions.

src/handlers/http/oidc.rs (2)

83-99: LGTM!

The login function correctly retrieves the OIDC client from the global OIDC_CLIENT and handles both the None (no OIDC configured) and Some cases appropriately.


178-193: LGTM!

The reply_login function correctly retrieves the OIDC client from the global state and handles the unavailable case by returning Unauthorized.

Cargo.toml (1)

65-65: Compatibility confirmed for openid 0.18.3.

The codebase uses standard OpenID Connect client APIs (request_token, decode_token, validate_token, request_userinfo, refresh_token) that remain compatible with openid 0.18.3. All imported types (Client, Discovered, StandardClaims, Bearer, Token, Userinfo, Options) are documented in the latest version with consistent signatures. No breaking changes found for this upgrade path.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/handlers/http/oidc.rs (1)

151-174: Inconsistency: logout function still uses per-request app data.

The logout function (lines 152-158) still retrieves the OIDC client via req.app_data::<Data<Option<DiscoveredClient>>>(), while login and reply_login have been migrated to use the global OIDC_CLIENT static. This inconsistency could lead to the logout function receiving None when the OIDC client is actually configured, since the app data is no longer populated with the OIDC client (based on the changes in src/handlers/http/modal/mod.rs line 138-146 where create_app_fn no longer adds OIDC client to app data).

🔎 Proposed fix to use global OIDC_CLIENT
 pub async fn logout(req: HttpRequest, query: web::Query<RedirectAfterLogin>) -> HttpResponse {
-    let oidc_client = match req.app_data::<Data<Option<DiscoveredClient>>>() {
-        Some(client) => {
-            let c = client.clone().into_inner();
-            c.as_ref().clone()
-        }
-        None => None,
-    };
+    let oidc_client = if let Some(client) = OIDC_CLIENT.get()
+        && let Some(client) = client
+    {
+        Some(client.read().await.client().clone())
+    } else {
+        None
+    };
+    
     let Some(session) = extract_session_key_from_req(&req).ok() else {
         return redirect_to_client(query.redirect.as_str(), None);
     };
🧹 Nitpick comments (2)
src/handlers/http/modal/mod.rs (1)

116-124: Consider removing unnecessary clone().

Line 121 calls client.clone() before passing client to GlobalClient::new(), but since client is moved into the constructor anyway, the clone is unnecessary.

🔎 Proposed fix
         if let Some(config) = oidc_client {
             let client = config
                 .connect(&format!("{API_BASE_PATH}/{API_VERSION}/o/code"))
                 .await?;
             OIDC_CLIENT
-                .get_or_init(|| Some(Arc::new(RwLock::new(GlobalClient::new(client.clone())))));
+                .get_or_init(|| Some(Arc::new(RwLock::new(GlobalClient::new(client)))));
         } else {
             OIDC_CLIENT.get_or_init(|| None);
         }

Note: The else branch correctly addresses the previous review comment about explicit None initialization.

src/handlers/http/oidc.rs (1)

83-86: Consider simplifying the OIDC client retrieval.

The pattern matching for extracting the OIDC client could be simplified for better readability.

🔎 Proposed refactor
-    let oidc_client = match OIDC_CLIENT.get() {
-        Some(c) => c.as_ref().cloned(),
-        None => None,
-    };
+    let oidc_client = OIDC_CLIENT.get().and_then(|c| c.as_ref().cloned());
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4646f92 and ccfa067.

📒 Files selected for processing (2)
  • src/handlers/http/modal/mod.rs
  • src/handlers/http/oidc.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-28T02:10:41.140Z
Learnt from: nikhilsinhaparseable
Repo: parseablehq/parseable PR: 1453
File: src/parseable/mod.rs:397-400
Timestamp: 2025-10-28T02:10:41.140Z
Learning: In Parseable enterprise deployments with multiple query nodes, hot tier configuration must be persisted in object storage so that newly started query nodes can fetch and synchronize the hot tier settings at startup (file: src/parseable/mod.rs, function: create_stream_and_schema_from_storage).

Applied to files:

  • src/handlers/http/modal/mod.rs
🧬 Code graph analysis (1)
src/handlers/http/oidc.rs (1)
src/handlers/http/modal/mod.rs (1)
  • client (75-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: coverage
  • GitHub Check: Build Default aarch64-unknown-linux-gnu
  • GitHub Check: Build Default x86_64-pc-windows-msvc
  • GitHub Check: Build Default aarch64-apple-darwin
  • GitHub Check: Quest Smoke and Load Tests for Standalone deployments
  • GitHub Check: Build Default x86_64-unknown-linux-gnu
  • GitHub Check: Quest Smoke and Load Tests for Distributed deployments
  • GitHub Check: Build Kafka aarch64-apple-darwin
  • GitHub Check: Build Kafka x86_64-unknown-linux-gnu
🔇 Additional comments (5)
src/handlers/http/modal/mod.rs (2)

21-21: LGTM: Imports support the global OIDC client mechanism.

The added imports (from_fn, OnceCell, RwLock, and DiscoveredClient) are necessary for implementing the global OIDC client pattern.

Also applies to: 28-28, 34-34, 43-43


63-82: LGTM: GlobalClient struct provides proper encapsulation.

The GlobalClient wrapper with accessor methods provides a clean interface for managing the OIDC client globally. The OnceCell<Option<Arc<RwLock<GlobalClient>>>> type properly distinguishes between "not initialized" and "OIDC not configured" states.

src/handlers/http/oidc.rs (3)

19-19: LGTM: Imports updated for global OIDC client access.

The import changes correctly support the new global OIDC_CLIENT pattern, bringing in Arc, RwLock, GlobalClient, and the static OIDC_CLIENT.

Also applies to: 32-32, 37-43


178-193: LGTM: Correctly migrated to global OIDC client access.

The signature change removes the unnecessary HttpRequest parameter, and the function correctly retrieves the OIDC client from the global OIDC_CLIENT static, returning Unauthorized when unavailable.


400-443: Recovery logic correctly addresses past review comments.

The updated request_token implementation properly handles JWKS refresh:

  1. Authorization code reuse fixed: Line 423 reuses the already-obtained token instead of re-exchanging the consumed authorization code, addressing the critical issue from previous reviews.
  2. Duplicate decode removed: Line 437 no longer calls decode_token again; the token is decoded on line 423 (recovery path) or the success path validates only.
  3. Proper lock management: Line 431 explicitly drops the read lock before acquiring the write lock on line 433, preventing deadlock.

Note on concurrency: A minor race exists between releasing the read lock (line 431) and acquiring the write lock (line 433), where multiple threads could sequentially update the client. However, since all threads would create fresh clients with updated JWKS, the end result remains correct.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 22, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/handlers/http/modal/mod.rs (1)

116-124: Remove unnecessary clone when initializing GlobalClient.

On line 121, client.clone() is unnecessary because client is not used after this point and can be moved directly into GlobalClient::new().

🔎 Proposed fix
         if let Some(config) = oidc_client {
             let client = config
                 .connect(&format!("{API_BASE_PATH}/{API_VERSION}/o/code"))
                 .await?;
             OIDC_CLIENT
-                .get_or_init(|| Some(Arc::new(RwLock::new(GlobalClient::new(client.clone())))));
+                .get_or_init(|| Some(Arc::new(RwLock::new(GlobalClient::new(client)))));
         } else {
             OIDC_CLIENT.get_or_init(|| None);
         }
src/handlers/http/oidc.rs (2)

83-86: Simplify OIDC client retrieval pattern.

The current pattern c.as_ref().cloned() is correct but verbose. Consider simplifying to improve readability.

🔎 Proposed refactor
-    let oidc_client = match OIDC_CLIENT.get() {
-        Some(c) => c.as_ref().cloned(),
-        None => None,
-    };
+    let oidc_client = OIDC_CLIENT.get().and_then(|opt| opt.clone());

Apply the same simplification at line 139 where this pattern is repeated.


429-429: Remove unnecessary explicit drop.

The explicit drop(old_client) is unnecessary because old_client is a cloned DiscoveredClient (not a lock guard) and will be automatically dropped at the end of the scope. Dropping it before acquiring the write lock doesn't reduce lock contention.

🔎 Proposed cleanup
         let userinfo = new_client.request_userinfo(&token).await?;
         let bearer = token.bearer;

-        // replace old client with new one
-        drop(old_client);
-
         oidc_client.write().await.set(new_client);
         return Ok((claims, userinfo, bearer));
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccfa067 and 0ab84c5.

📒 Files selected for processing (2)
  • src/handlers/http/modal/mod.rs
  • src/handlers/http/oidc.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-28T02:10:41.140Z
Learnt from: nikhilsinhaparseable
Repo: parseablehq/parseable PR: 1453
File: src/parseable/mod.rs:397-400
Timestamp: 2025-10-28T02:10:41.140Z
Learning: In Parseable enterprise deployments with multiple query nodes, hot tier configuration must be persisted in object storage so that newly started query nodes can fetch and synchronize the hot tier settings at startup (file: src/parseable/mod.rs, function: create_stream_and_schema_from_storage).

Applied to files:

  • src/handlers/http/modal/mod.rs
🧬 Code graph analysis (2)
src/handlers/http/modal/mod.rs (2)
src/cli.rs (1)
  • openid (528-548)
src/handlers/http/modal/ssl_acceptor.rs (1)
  • get_ssl_acceptor (27-62)
src/handlers/http/oidc.rs (1)
src/handlers/http/modal/mod.rs (3)
  • client (75-77)
  • token (590-590)
  • token (600-602)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Build Kafka x86_64-unknown-linux-gnu
  • GitHub Check: Build Kafka aarch64-apple-darwin
  • GitHub Check: Build Default x86_64-pc-windows-msvc
  • GitHub Check: Build Default aarch64-apple-darwin
  • GitHub Check: coverage
  • GitHub Check: Build Default x86_64-unknown-linux-gnu
  • GitHub Check: Build Default aarch64-unknown-linux-gnu
  • GitHub Check: Quest Smoke and Load Tests for Distributed deployments
  • GitHub Check: Quest Smoke and Load Tests for Standalone deployments
🔇 Additional comments (3)
src/handlers/http/modal/mod.rs (1)

63-82: LGTM: Global OIDC client wrapper is well-structured.

The GlobalClient struct and static OIDC_CLIENT provide a clean global access pattern with thread-safe mutation via RwLock. The initialization paths below properly handle both the configured and unconfigured cases.

src/handlers/http/oidc.rs (2)

176-191: LGTM: Safe OIDC client access with proper error handling.

The let-else chain correctly unwraps the nested Option without risk of panicking, and error handling appropriately returns Unauthorized when OIDC is unavailable or token exchange fails.


411-433: LGTM: Recovery logic correctly reuses the obtained token.

The recovery path properly creates a new client with fresh JWKS keys and reuses the already-obtained token rather than re-exchanging the authorization code (which would fail since auth codes are single-use). This correctly addresses the previous review feedback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/handlers/http/oidc.rs (1)

152-155: Critical: Unwrap will panic when OIDC is not configured.

Line 153 calls .unwrap() on c.as_ref(), which will panic when OIDC is not configured. When OIDC_CLIENT is initialized to Some(None) (line 122 in modal/mod.rs), c.as_ref() returns None, causing the unwrap to panic.

This issue was previously reported and marked as addressed, but the problematic code remains.

🔎 Proposed fix
-    let oidc_client = match OIDC_CLIENT.get() {
-        Some(c) => Some(c.as_ref().unwrap().read().await.client().clone()),
-        None => None,
-    };
+    let oidc_client = if let Some(Some(client)) = OIDC_CLIENT.get() {
+        Some(client.read().await.client().clone())
+    } else {
+        None
+    };
🧹 Nitpick comments (2)
src/handlers/http/oidc.rs (1)

411-433: Consider guarding against concurrent client recreation.

When token decoding fails, multiple concurrent requests may all attempt to create new OIDC clients (lines 413-418). While only one will succeed in replacing the global client (line 431), the others will waste resources creating clients that are discarded.

Additionally, if JWKS validation fails repeatedly, every request will trigger client recreation without rate limiting, potentially amplifying issues with the identity provider.

For the current use case this is likely acceptable, but if token decode failures become frequent, consider adding:

  • A flag/timestamp to coordinate client recreation across concurrent requests
  • Rate limiting or circuit breaker logic for client recreation attempts
src/handlers/http/modal/mod.rs (1)

63-82: Consider adding documentation for public API.

The public static OIDC_CLIENT and struct GlobalClient lack doc comments. While these are internal APIs, documentation would help maintainers understand:

  • The purpose of the GlobalClient wrapper
  • Thread-safety guarantees and usage patterns
  • Initialization lifecycle and access patterns
Example documentation
/// Global OIDC client store, initialized once at server startup.
/// - `None`: OIDC_CLIENT has not been initialized yet
/// - `Some(None)`: OIDC is not configured
/// - `Some(Some(client))`: OIDC is configured and client is available
pub static OIDC_CLIENT: OnceCell<Option<Arc<RwLock<GlobalClient>>>> = OnceCell::new();

/// Wrapper around DiscoveredClient to enable global state management.
/// Thread-safe via RwLock; allows client replacement during JWKS refresh.
#[derive(Debug)]
pub struct GlobalClient {
    client: DiscoveredClient,
}
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab84c5 and 2dcfa19.

📒 Files selected for processing (2)
  • src/handlers/http/modal/mod.rs
  • src/handlers/http/oidc.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-28T02:10:41.140Z
Learnt from: nikhilsinhaparseable
Repo: parseablehq/parseable PR: 1453
File: src/parseable/mod.rs:397-400
Timestamp: 2025-10-28T02:10:41.140Z
Learning: In Parseable enterprise deployments with multiple query nodes, hot tier configuration must be persisted in object storage so that newly started query nodes can fetch and synchronize the hot tier settings at startup (file: src/parseable/mod.rs, function: create_stream_and_schema_from_storage).

Applied to files:

  • src/handlers/http/modal/mod.rs
🧬 Code graph analysis (1)
src/handlers/http/oidc.rs (3)
src/option.rs (1)
  • url (139-141)
src/utils/actix.rs (2)
  • extract_session_key_from_req (51-71)
  • req (31-31)
src/handlers/http/modal/mod.rs (1)
  • client (75-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Build Kafka x86_64-unknown-linux-gnu
  • GitHub Check: coverage
  • GitHub Check: Build Default aarch64-unknown-linux-gnu
  • GitHub Check: Quest Smoke and Load Tests for Standalone deployments
  • GitHub Check: Build Default x86_64-pc-windows-msvc
  • GitHub Check: Build Default x86_64-unknown-linux-gnu
  • GitHub Check: Build Default aarch64-apple-darwin
  • GitHub Check: Quest Smoke and Load Tests for Distributed deployments
  • GitHub Check: Build Kafka aarch64-apple-darwin
🔇 Additional comments (1)
src/handlers/http/modal/mod.rs (1)

116-123: LGTM! OIDC_CLIENT initialization handles both cases correctly.

The initialization now properly handles both configured and unconfigured OIDC scenarios, addressing the previously reported concern. The use of get_or_init ensures thread-safe initialization.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 22, 2025
@nitisht nitisht merged commit 15cde20 into parseablehq:main Dec 23, 2025
12 checks passed
@nitisht nitisht mentioned this pull request Dec 23, 2025
@thenodon
Copy link
Contributor

I can confirm my oidc auth flow works with a setup of dex+MS-ADFS after at least approx 20 hours. No 401 detected after logout+login or logout+new auth flow to ADFS+login.
Great work!

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.

3 participants