fix(provider): fallback to config.api_key when auth-profiles.json has no key#2724
fix(provider): fallback to config.api_key when auth-profiles.json has no key#2724ccpty wants to merge 1 commit into
Conversation
… no key When a custom provider's API key is set in config.toml but not saved through the UI into auth-profiles.json, the HTTP request would be sent without an Authorization header, causing authentication failures. This change adds a fallback in lookup_key_for_slug() that reads from the top-level config.api_key field (which is auto-decrypted by the config loader from the enc2:-prefixed value in config.toml). Fixes: API key bug where config.toml has api_key but HTTP requests lack Authorization header for custom providers.
📝 WalkthroughWalkthroughThe PR adds a fallback authentication-key resolution mechanism in ChangesAuth Key Resolution Fallback
Estimated code review effort🎯 2 (Simple) | ⏱️ ~5 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
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 `@src/openhuman/inference/provider/factory.rs`:
- Around line 641-651: The fallback returning config.api_key is currently
unconditional and can leak a global key to any provider; restrict this to only
the legacy direct-inference provider by checking that the provider endpoint for
the given slug matches config.inference_url before returning the key. In
practical terms, in the code around factory.rs where you read config.api_key and
have access to slug, resolve or compute the provider endpoint for slug (the same
logic used elsewhere in this function) and only use
config.api_key.trim().to_string() when endpoint == config.inference_url (and the
trimmed key is non-empty); otherwise skip this fallback and continue with normal
auth-profile lookup.
🪄 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: a98ef126-9f54-4de8-a118-15e9685bb58a
📒 Files selected for processing (1)
src/openhuman/inference/provider/factory.rs
| // Fallback: read from top-level config.api_key (direct config.toml api_key). | ||
| // This handles the case where a key was set in config.toml but not saved | ||
| // through the UI into auth-profiles.json. | ||
| if let Some(config_key) = config.api_key.as_ref() { | ||
| if !config_key.trim().is_empty() { | ||
| log::debug!( | ||
| "[providers][chat-factory] auth lookup slug={} key_present=true (config.toml fallback)", | ||
| slug | ||
| ); | ||
| return Ok(config_key.trim().to_string()); | ||
| } |
There was a problem hiding this comment.
Scope the config.api_key fallback to avoid cross-provider credential leakage.
This fallback applies to any slug when profile lookup is empty, so a global key can be sent to the wrong provider endpoint. Please gate it to the legacy direct-inference provider only (e.g., slug whose endpoint matches config.inference_url) instead of unconditional fallback.
Suggested scoped fallback
- if let Some(config_key) = config.api_key.as_ref() {
- if !config_key.trim().is_empty() {
- log::debug!(
- "[providers][chat-factory] auth lookup slug={} key_present=true (config.toml fallback)",
- slug
- );
- return Ok(config_key.trim().to_string());
- }
- }
+ let is_legacy_inference_slug = config
+ .inference_url
+ .as_deref()
+ .map(str::trim)
+ .filter(|u| !u.is_empty())
+ .and_then(|inference_url| {
+ config
+ .cloud_providers
+ .iter()
+ .find(|e| e.slug == slug)
+ .map(|e| normalize_endpoint_for_compare(&e.endpoint)
+ == normalize_endpoint_for_compare(inference_url))
+ })
+ .unwrap_or(false);
+
+ if is_legacy_inference_slug {
+ if let Some(config_key) = config.api_key.as_ref() {
+ if !config_key.trim().is_empty() {
+ log::debug!(
+ "[providers][chat-factory] auth lookup slug={} key_present=true (config.toml fallback)",
+ slug
+ );
+ return Ok(config_key.trim().to_string());
+ }
+ }
+ }Based on learnings: src/openhuman/config/schema/types.rs documents top-level api_key as paired with inference_url for direct endpoint routing.
🤖 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 641 - 651, The
fallback returning config.api_key is currently unconditional and can leak a
global key to any provider; restrict this to only the legacy direct-inference
provider by checking that the provider endpoint for the given slug matches
config.inference_url before returning the key. In practical terms, in the code
around factory.rs where you read config.api_key and have access to slug, resolve
or compute the provider endpoint for slug (the same logic used elsewhere in this
function) and only use config.api_key.trim().to_string() when endpoint ==
config.inference_url (and the trimmed key is non-empty); otherwise skip this
fallback and continue with normal auth-profile lookup.
graycyrus
left a comment
There was a problem hiding this comment.
Summary
PR adds a fallback in lookup_key_for_slug() to read from config.api_key when a custom provider's API key isn't found in auth-profiles.json. Tests pass, CI is green.
Findings
CodeRabbit caught a major security issue — credential leakage. The fallback applies config.api_key to any provider slug when profile lookup fails. If you have multiple cloud_providers configured, they all get the same credential, even if config.api_key was only intended for the legacy inference_url provider.
Scoping required: Only apply the fallback when the provider's endpoint matches config.inference_url. CodeRabbit's suggested fix at line 651 is the right approach — gate the fallback to check if slug points to the provider entry whose endpoint matches the configured inference_url.
This is a blocker — the credential leakage needs to be closed before merge.
graycyrus
left a comment
There was a problem hiding this comment.
I see a REQUEST_CHANGES is already in place for the credential leakage issue flagged by CodeRabbit. Before I can approve this, that concern needs to be addressed — the fallback should be scoped to only the legacy inference provider (where the endpoint matches config.inference_url) to avoid sending the global config.api_key to unintended providers.
Also, there's a failing CI check (test / Rust Core Tests (Windows — secrets ACL)). Once the credential leakage issue is fixed and CI passes, I'll do a full review.
When a custom provider's API key is set in config.toml but not saved through the UI into auth-profiles.json, the HTTP request would be sent without an Authorization header, causing authentication failures.
This change adds a fallback in
lookup_key_for_slug()that reads from the top-levelconfig.api_keyfield (which is auto-decrypted by the config loader from the enc2:-prefixed value in config.toml).Before: api_key bytes=0 (key not found, requests fail)
After: api_key bytes=51 (key found via fallback, requests succeed)
Summary by CodeRabbit