Skip to content

fix: enforce session check before creating custom LLM providers#2014

Merged
senamakel merged 3 commits into
tinyhumansai:mainfrom
xinzhuwang-wxz:fix/session-gate-for-custom-providers
May 18, 2026
Merged

fix: enforce session check before creating custom LLM providers#2014
senamakel merged 3 commits into
tinyhumansai:mainfrom
xinzhuwang-wxz:fix/session-gate-for-custom-providers

Conversation

@xinzhuwang-wxz
Copy link
Copy Markdown
Contributor

@xinzhuwang-wxz xinzhuwang-wxz commented May 17, 2026

Summary

An unregistered user can configure every workload (reasoning, coding, agentic, memory, heartbeat, etc.) to use a custom cloud provider (e.g. DeepSeek, OpenAI, Anthropic) and bypass the OpenHuman session requirement entirely. The core never creates an OpenHumanBackendProvider, so the JWT check in resolve_bearer() is never reached.

Root Cause

provider_for_role() returns "openhuman" when no explicit workload override is configured — but if every workload has an explicit override pointing to a custom provider, no role ever resolves to "openhuman", and the session gate is bypassed.

Fix

Add verify_session_active() check in create_chat_provider_from_string() that gates custom provider creation behind an active session, mirroring the same guard used for the primary cloud path.

Changes

  • src/openhuman/inference/provider/factory.rs: +69 lines — session gate
  • src/openhuman/inference/provider/factory_test.rs: +73 lines — tests

Summary by CodeRabbit

  • New Features

    • Empty or "cloud" workload overrides now resolve to the configured primary cloud provider when applicable.
  • Bug Fixes

    • Prevent selection of custom providers for unauthenticated users; default provider remains usable without signing in.
    • Session-expired state now reliably blocks custom-provider usage.
  • Tests

    • Added tests for session verification, enforcement for custom providers, and bypass behavior for the default provider.

Review Change Stack

@xinzhuwang-wxz xinzhuwang-wxz requested a review from a team May 17, 2026 16:40
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds primary-cloud-based routing for provider selection and gates custom provider strings behind a session verification check that requires a non-empty app-session token; unit tests cover missing, empty, and present tokens and preserve OpenHuman bypass behavior.

Changes

Provider Routing with Session Gate

Layer / File(s) Summary
Primary cloud provider routing
src/openhuman/inference/provider/factory.rs
provider_for_role resolves config.primary_cloud to a non-openhuman slug and returns it as the provider when no per-workload override is set; otherwise falls back to openhuman.
Session verification implementation
src/openhuman/inference/provider/factory.rs
New private verify_session_active(config) fast-fails on scheduler_gate::is_signed_out(), resolves state dir, and requires a non-empty app-session token via AuthService, returning SESSION_EXPIRED on missing/empty token.
Session gate in provider creation
src/openhuman/inference/provider/factory.rs
create_chat_provider_from_string guards custom provider handling with verify_session_active(config)? under #[cfg(not(test))] after early returns for empty/"cloud" and "openhuman".
Session verification tests
src/openhuman/inference/provider/factory_test.rs
New config_in_tempdir helper and tests: reject when session token is absent or empty, accept when non-empty, and assert OpenHuman bypasses the gate while custom providers are blocked without session.

Sequence Diagram

sequenceDiagram
  participant Caller
  participant CreateChatProvider
  participant VerifySession
  participant Scheduler
  participant AuthService
  
  Caller->>CreateChatProvider: create_chat_provider_from_string(config, provider_str)
  CreateChatProvider->>VerifySession: verify_session_active(config)?
  VerifySession->>Scheduler: is_signed_out()
  Scheduler-->>VerifySession: signed-out?
  alt signed out
    VerifySession-->>CreateChatProvider: SESSION_EXPIRED error
    CreateChatProvider-->>Caller: error
  else not signed out
    VerifySession->>AuthService: read app-session token from state dir
    AuthService-->>VerifySession: token (present/empty/missing)
    alt Token Missing or Empty
      VerifySession-->>CreateChatProvider: SESSION_EXPIRED error
      CreateChatProvider-->>Caller: error
    else Token Valid
      VerifySession-->>CreateChatProvider: ok
      CreateChatProvider->>CreateChatProvider: parse and create provider (ollama:<model> / <slug>:<model>)
      CreateChatProvider-->>Caller: ChatProvider
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

working

Poem

🐰 I nibble tokens in the glen,
Guard the gate for custom men.
Clouds pick slugs where paths align,
Sessions prove the key is fine.
Hop, verify — then speak with me.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: adding session enforcement before custom LLM provider creation, which matches the core change of adding verify_session_active() to gate custom providers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@coderabbitai coderabbitai Bot added the working A PR that is being worked on by the team. label May 17, 2026
Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (2)
src/openhuman/inference/provider/factory_test.rs (1)

372-372: 💤 Low value

Remove unused mut qualifiers.

config is never mutated in these tests; Clippy will warn.

♻️ Proposed fix
 fn verify_session_active_rejects_when_token_is_empty() {
     let tmp = TempDir::new().expect("tempdir");
-    let mut config = config_in_tempdir(&tmp);
+    let config = config_in_tempdir(&tmp);
 fn verify_session_active_passes_when_session_token_present() {
     let tmp = TempDir::new().expect("tempdir");
-    let mut config = config_in_tempdir(&tmp);
+    let config = config_in_tempdir(&tmp);

Also applies to: 386-386

🤖 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 `@src/openhuman/inference/provider/factory_test.rs` at line 372, The variable
declaration "let mut config = config_in_tempdir(&tmp);" uses an unnecessary
mutable qualifier; change it to an immutable binding ("let config =
config_in_tempdir(&tmp);") wherever applicable (e.g., the occurrences around the
tests using config at lines similar to 372 and 386) because the variable is
never mutated. Update any other instances of "let mut config" in those tests to
"let config" and run cargo clippy to ensure no remaining unused mut warnings.
src/openhuman/inference/provider/factory.rs (1)

238-264: ⚡ Quick win

Add debug logging for session verification outcomes.

This security-critical function lacks diagnostic logging. Adding trace/debug logs on rejection and success paths aids audit trails and debugging auth issues.

As per coding guidelines: "Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions."

♻️ Proposed logging additions
 fn verify_session_active(config: &Config) -> anyhow::Result<()> {
     // Fast path: the scheduler gate already knows the session is dead.
     if crate::openhuman::scheduler_gate::is_signed_out() {
+        log::debug!("[providers][session-gate] rejected: scheduler reports signed-out");
         anyhow::bail!(
             "SESSION_EXPIRED: backend session not active — sign in to use custom providers"
         );
     }
     // Verify the app-session JWT actually exists in auth-profiles.
     let state_dir = config
         .config_path
         .parent()
         .map(std::path::PathBuf::from)
         .unwrap_or_else(|| {
             directories::UserDirs::new()
                 .map(|d| d.home_dir().join(".openhuman"))
                 .unwrap_or_else(|| std::path::PathBuf::from(".openhuman"))
         });
     let auth = AuthService::new(&state_dir, config.secrets.encrypt);
     let has_session = auth
         .get_provider_bearer_token(crate::openhuman::credentials::APP_SESSION_PROVIDER, None)?
         .filter(|s| !s.trim().is_empty())
         .is_some();
     if !has_session {
+        log::debug!("[providers][session-gate] rejected: no app-session token in state_dir={:?}", state_dir);
         anyhow::bail!("SESSION_EXPIRED: no backend session — sign in to use OpenHuman")
     }
+    log::trace!("[providers][session-gate] session verified");
     Ok(())
 }
🤖 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 `@src/openhuman/inference/provider/factory.rs` around lines 238 - 264, Add
trace/debug logging to verify_session_active: log entry (with config identifier
but not secrets) at trace/debug, log a debug/trace message when
crate::openhuman::scheduler_gate::is_signed_out() short‑circuits indicating the
session is known dead, log a debug/trace message when
AuthService::get_provider_bearer_token (using
crate::openhuman::credentials::APP_SESSION_PROVIDER) returns no/empty token
before bailing, and log a success debug/trace just before returning Ok()
indicating the session is active; use the project's tracing/log crate and avoid
including token or secret values in the messages.
🤖 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 `@src/openhuman/inference/provider/factory_test.rs`:
- Around line 402-419: The test
verify_session_active_called_for_custom_provider_not_for_openhuman declares an
unused variable `err` when calling create_chat_provider_from_string("reasoning",
"ollama:llama3", &config); rename `err` to `_` (or prefix with underscore) to
silence the unused variable warning, then run cargo fmt to fix formatting issues
so the test file (including the verify_session_active and
create_chat_provider_from_string usages) is properly formatted.

In `@src/openhuman/inference/provider/factory.rs`:
- Around line 458-460: The file contains a formatting issue around the test
module declaration (the lines with #[cfg(test)] #[path = "factory_test.rs"] mod
factory_test;) causing cargo fmt to report a diff; run `cargo fmt` (or format
that area) to remove trailing whitespace/newline inconsistencies and ensure the
module attributes and declaration follow rustfmt style so the pipeline passes.

---

Nitpick comments:
In `@src/openhuman/inference/provider/factory_test.rs`:
- Line 372: The variable declaration "let mut config = config_in_tempdir(&tmp);"
uses an unnecessary mutable qualifier; change it to an immutable binding ("let
config = config_in_tempdir(&tmp);") wherever applicable (e.g., the occurrences
around the tests using config at lines similar to 372 and 386) because the
variable is never mutated. Update any other instances of "let mut config" in
those tests to "let config" and run cargo clippy to ensure no remaining unused
mut warnings.

In `@src/openhuman/inference/provider/factory.rs`:
- Around line 238-264: Add trace/debug logging to verify_session_active: log
entry (with config identifier but not secrets) at trace/debug, log a debug/trace
message when crate::openhuman::scheduler_gate::is_signed_out() short‑circuits
indicating the session is known dead, log a debug/trace message when
AuthService::get_provider_bearer_token (using
crate::openhuman::credentials::APP_SESSION_PROVIDER) returns no/empty token
before bailing, and log a success debug/trace just before returning Ok()
indicating the session is active; use the project's tracing/log crate and avoid
including token or secret values in the messages.
🪄 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: 2af3c1d3-bb7c-4aa3-90fb-ff509659da61

📥 Commits

Reviewing files that changed from the base of the PR and between f9de38d and c3a77ba.

📒 Files selected for processing (2)
  • src/openhuman/inference/provider/factory.rs
  • src/openhuman/inference/provider/factory_test.rs

Comment thread src/openhuman/inference/provider/factory_test.rs Outdated
Comment thread src/openhuman/inference/provider/factory.rs Outdated
@xinzhuwang-wxz xinzhuwang-wxz force-pushed the fix/session-gate-for-custom-providers branch 3 times, most recently from f0977cd to 126d44d Compare May 17, 2026 17:03
@xinzhuwang-wxz xinzhuwang-wxz force-pushed the fix/session-gate-for-custom-providers branch from 126d44d to 7ddfdf3 Compare May 17, 2026 17:16
Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
src/openhuman/inference/provider/factory.rs (1)

238-264: ⚡ Quick win

Add diagnostic logging for session verification.

Per coding guidelines, use log/tracing at debug level on error paths and state transitions. This function should log when session verification fails to aid debugging authentication issues.

Also, the error message at line 261 says "sign in to use OpenHuman" but the user is actually trying to use a custom provider. Consider aligning with the message at line 242.

♻️ Proposed fix
 fn verify_session_active(config: &Config) -> anyhow::Result<()> {
     // Fast path: the scheduler gate already knows the session is dead.
     if crate::openhuman::scheduler_gate::is_signed_out() {
+        log::debug!("[providers][chat-factory] session gate: scheduler reports signed out");
         anyhow::bail!(
             "SESSION_EXPIRED: backend session not active — sign in to use custom providers"
         );
     }
     // Verify the app-session JWT actually exists in auth-profiles.
     let state_dir = config
         .config_path
         .parent()
         .map(std::path::PathBuf::from)
         .unwrap_or_else(|| {
             directories::UserDirs::new()
                 .map(|d| d.home_dir().join(".openhuman"))
                 .unwrap_or_else(|| std::path::PathBuf::from(".openhuman"))
         });
     let auth = AuthService::new(&state_dir, config.secrets.encrypt);
     let has_session = auth
         .get_provider_bearer_token(crate::openhuman::credentials::APP_SESSION_PROVIDER, None)?
         .filter(|s| !s.trim().is_empty())
         .is_some();
     if !has_session {
-        anyhow::bail!("SESSION_EXPIRED: no backend session — sign in to use OpenHuman")
+        log::debug!("[providers][chat-factory] session gate: no app-session token found");
+        anyhow::bail!("SESSION_EXPIRED: no backend session — sign in to use custom providers")
     }
+    log::trace!("[providers][chat-factory] session gate: verified active session");
     Ok(())
 }

As per coding guidelines: "Use log / tracing at debug or trace level on... error paths, state transitions".

🤖 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 `@src/openhuman/inference/provider/factory.rs` around lines 238 - 264, The
verify_session_active function needs debug-level tracing on the two failure
paths and an aligned error message: add a debug/tracing log call before the
scheduler gate bail (referencing
crate::openhuman::scheduler_gate::is_signed_out) that records the signed-out
state, and add a debug/tracing log before the auth-session bail (referencing
AuthService::new and get_provider_bearer_token) that records that no app-session
token was found; change the second bail message to match the first context
(e.g., "SESSION_EXPIRED: backend session not active — sign in to use custom
providers") so both logs and the returned error consistently indicate the
custom-provider use case.
🤖 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 `@src/openhuman/inference/provider/factory.rs`:
- Around line 238-264: The verify_session_active function needs debug-level
tracing on the two failure paths and an aligned error message: add a
debug/tracing log call before the scheduler gate bail (referencing
crate::openhuman::scheduler_gate::is_signed_out) that records the signed-out
state, and add a debug/tracing log before the auth-session bail (referencing
AuthService::new and get_provider_bearer_token) that records that no app-session
token was found; change the second bail message to match the first context
(e.g., "SESSION_EXPIRED: backend session not active — sign in to use custom
providers") so both logs and the returned error consistently indicate the
custom-provider use case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 078d384c-7814-49a1-a632-0531472520f7

📥 Commits

Reviewing files that changed from the base of the PR and between 126d44d and 7ddfdf3.

📒 Files selected for processing (2)
  • src/openhuman/inference/provider/factory.rs
  • src/openhuman/inference/provider/factory_test.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/openhuman/inference/provider/factory_test.rs

CodeRabbit flagged let err = ... as unused. The result is intentionally
discarded in the test path — renamed to let _ = to suppress the warning.
@xinzhuwang-wxz
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@senamakel senamakel merged commit 82c8320 into tinyhumansai:main May 18, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants