Skip to content

Fix Claude plugin auth compatibility across custom OAuth and keychain setups#331

Open
robinebers wants to merge 2 commits intomainfrom
fix/claude-plugin-auth-compat
Open

Fix Claude plugin auth compatibility across custom OAuth and keychain setups#331
robinebers wants to merge 2 commits intomainfrom
fix/claude-plugin-auth-compat

Conversation

@robinebers
Copy link
Copy Markdown
Owner

@robinebers robinebers commented Mar 31, 2026

Summary

  • add Claude auth compatibility for custom config dirs, custom OAuth endpoints, staging/local OAuth, and env-injected access tokens
  • update Claude credential lookup and refresh behavior to use the correct keychain service, pass custom home paths to ccusage, and include the user:file_upload scope
  • expand host API env allowlists and redaction coverage for Claude auth inputs, keychain logging, IDs, and filesystem paths
  • add plugin and Rust regression tests covering the new Claude auth and redaction paths

Testing

  • Not run (not requested)

Note

Medium Risk
Touches OAuth credential discovery/refresh and macOS keychain read/write behavior, which can break authentication or usage reporting across environments if misconfigured. Adds broader env passthrough and more logging/redaction logic, increasing configuration surface area but with regression tests to reduce risk.

Overview
Improves the Claude plugin’s auth compatibility by supporting CLAUDE_CONFIG_DIR for credential file lookup and ccusage home overrides, env-injected access tokens via CLAUDE_CODE_OAUTH_TOKEN (treated as inference-only and skips live /api/oauth/usage), and expanded refresh scopes to include user:file_upload.

Adds flexible OAuth routing for local/staging/custom endpoints and client ID overrides, and namespaces keychain service names with an OAuth suffix to avoid collisions. On the Rust side, expands the host env allowlist for these Claude variables, fixes macOS keychain access to always target the current account (and logs hits/misses), and strengthens redaction to cover additional IDs and filesystem paths (including redacting app_data_dir and ccusage runner paths).

Written by Cursor Bugbot for commit cc1d180. This will update automatically on new commits. Configure here.


Summary by cubic

Fixes Claude plugin auth across custom config dirs and custom/staging/local OAuth, with account-scoped keychain and broader redaction. Adds env token support, includes the user:file_upload scope, and skips live usage when tokens lack user:profile.

  • New Features

    • Read creds from CLAUDE_CONFIG_DIR; pass and expand home path to ccusage.
    • Support staging/local/custom OAuth endpoints and client IDs; keychain service names include an OAuth suffix.
    • Accept CLAUDE_CODE_OAUTH_TOKEN for inference-only sessions; skip live usage when missing user:profile.
    • Include user:file_upload in OAuth refresh scope.
    • Added tests for custom home, keychain suffixes, env tokens, scope, and usage-skip behavior.
  • Bug Fixes

    • macOS keychain reads/writes target the current account; logs redact the account.
    • Expanded host API env allowlist for Claude auth vars (e.g., CLAUDE_CONFIG_DIR, USE_LOCAL_OAUTH, CLAUDE_LOCAL_OAUTH_API_BASE, CLAUDE_CODE_OAUTH_CLIENT_ID); sensitive values redacted.
    • Redaction now covers team/payment IDs and filesystem paths; app_data_dir and ccusage paths masked as [PATH].
    • More robust token refresh and 401 retry handling in usage requests.

Written for commit cc1d180. Summary will update on new commits.

@github-actions github-actions bot added rust Pull requests that update rust code plugin labels Mar 31, 2026
@augmentcode
Copy link
Copy Markdown

augmentcode bot commented Mar 31, 2026

🤖 Augment PR Summary

Summary: This PR improves Claude plugin authentication compatibility across custom config locations and multiple OAuth environments, and strengthens host-side redaction/logging around those auth flows.

Changes:

  • Claude plugin now reads credentials from CLAUDE_CONFIG_DIR, supports staging/local/custom OAuth bases, and supports env-injected access tokens.
  • Keychain credential lookup/refresh now uses an OAuth-environment-specific service name and refresh scope includes user:file_upload.
  • Claude usage querying via ccusage can receive an explicit home path override.
  • Host API expands the env allowlist for Claude auth inputs and adds more robust redaction of IDs and filesystem paths.
  • macOS keychain integration now targets an explicit account (current user) and adds structured logging for keychain hits/misses/writes.
  • Adds JS plugin regression tests for the new Claude auth behaviors and Rust tests for env allowlisting, keychain args, and redaction.

Technical Notes: Live usage fetch is skipped for “inference-only” env-injected tokens, but ccusage-based historical usage is still queried.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

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

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

accessToken = refreshed
} else {
ctx.host.log.warn("proactive refresh failed, trying with existing token")
const homePath = getClaudeHomeOverride(ctx)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

homePath is taken directly from CLAUDE_CONFIG_DIR and passed through to ccusage, but the Rust ccusage wrapper sets the env var without ~ expansion (unlike host.fs.* paths). If a user sets CLAUDE_CONFIG_DIR=~/.claude-custom, credential reads may work while ccusage queries silently point at a non-existent directory.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

let old = std::env::var(name).ok();
let _restore = RestoreEnvVar { name, old };
// SAFETY: this test restores the previous value in `Drop`.
unsafe { std::env::set_var(name, "openusage-test-user") };
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This test mutates the process-wide USER env var and assumes env changes are “serialized”, but Rust tests run in parallel by default so this can be flaky and/or leak into other tests. If any other test reads USER concurrently, results can become nondeterministic.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

@robinebers robinebers linked an issue Mar 31, 2026 that may be closed by this pull request
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 5 files

@validatedev validatedev requested a review from Copilot April 1, 2026 11:50
@validatedev
Copy link
Copy Markdown
Collaborator

@codex review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Improves Claude plugin authentication compatibility across custom config directories and multiple OAuth environments, while expanding redaction coverage and adding regression tests.

Changes:

  • Added support for CLAUDE_CONFIG_DIR, env-injected OAuth tokens, and staging/local/custom OAuth routing in the Claude plugin.
  • Updated Rust host API to broaden env allowlisting, improve macOS keychain access/logging, and redact additional IDs/paths in logs/bodies.
  • Added JS + Rust tests for the new Claude auth flows, ccusage home overrides, and redaction behavior.

Reviewed changes

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

Show a summary per file
File Description
src-tauri/src/plugin_engine/host_api.rs Expands env allowlist, adds keychain account scoping + logging, ccusage home expansion, and broader redaction.
src-tauri/src/lib.rs Redacts app_data_dir logging output while preserving a non-sensitive tail.
plugins/claude/plugin.js Adds CLAUDE_CONFIG_DIR support, OAuth env routing, env token handling, scope expansion, and usage-fetch gating.
plugins/claude/plugin.test.js Adds regression tests for new credential discovery, keychain service suffixes, env token behavior, and refresh scope.
.gitignore Ignores a new docs directory.

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

Comment on lines +1841 to 1852
let account = current_macos_keychain_account();
let args = keychain_find_generic_password_args(&service, &account);
let redacted_account = redact_value(&account);
log::info!(
"[plugin:{}] keychain read: service={}, account={}",
pid_read,
service,
redacted_account
);
let output = std::process::Command::new("security")
.args(["find-generic-password", "-s", &service, "-w"])
.args(&args)
.output()
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

readGenericPassword now always queries the keychain with both -s <service> and -a <current account>. Any existing items that were previously created without -a (or with a different account value) will not be found anymore, which can silently break credential discovery after upgrade. Consider falling back to a service-only lookup when the account-scoped lookup misses (and/or migrating the item to the account-scoped form once found) so existing installs continue to work.

Copilot uses AI. Check for mistakes.
Comment on lines +338 to +340
if let Ok(path_re) =
regex_lite::Regex::new(r#"(/(?:Users|home|opt|private|var|tmp|Applications)/[^\s"')]+)"#)
{
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The path redaction regex only matches Unix-style absolute paths starting with /Users, /home, /opt, etc. On Windows, common sensitive paths like C:\Users\... or UNC paths (\\server\share\...) will not be redacted and may still leak via logs. Consider adding Windows/UNC path patterns (and tests) so redaction is effective cross-platform.

Suggested change
if let Ok(path_re) =
regex_lite::Regex::new(r#"(/(?:Users|home|opt|private|var|tmp|Applications)/[^\s"')]+)"#)
{
if let Ok(path_re) = regex_lite::Regex::new(
r#"((?:/(?:Users|home|opt|private|var|tmp|Applications)|[A-Za-z]:\\|\\\\)[^\s"')]+)"#,
) {

Copilot uses AI. Check for mistakes.
Comment on lines +269 to +272
if (Array.isArray(scopes) && scopes.length > 0) {
return scopes.indexOf("user:profile") !== -1
}
return true
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

hasProfileScope returns true when creds.oauth.scopes is an empty array, which effectively treats an explicitly-specified “no scopes” token as having user:profile and will attempt the live usage call. If the intent is to skip live usage whenever user:profile is missing, this should return scopes.includes("user:profile") for any array (including empty).

Suggested change
if (Array.isArray(scopes) && scopes.length > 0) {
return scopes.indexOf("user:profile") !== -1
}
return true
if (!Array.isArray(scopes)) {
return false
}
return scopes.includes("user:profile")

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

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

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: cc1d1806e1

ℹ️ 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 on lines +73 to +75
OsString::from("-a"),
OsString::from(account),
OsString::from("-s"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add fallback keychain lookup when account-scoped read misses

This change makes keychain reads require an exact account match (-a <current user>), but existing items created before this change may not match that account, so credential discovery can regress to "not logged in" after upgrade. In this codepath, a miss is treated as absent credentials rather than retried with a service-only lookup, so affected users lose persisted auth until they re-login or manually migrate keychain items. Add a fallback find-generic-password -s <service> -w (or migration) when account-scoped lookup fails.

Useful? React with 👍 / 👎.

Comment on lines +338 to +340
if let Ok(path_re) =
regex_lite::Regex::new(r#"(/(?:Users|home|opt|private|var|tmp|Applications)/[^\s"')]+)"#)
{
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Redact full filesystem paths that contain spaces

The new path redaction regex stops at whitespace, so common macOS paths like /Users/.../Library/Application Support/... are only partially masked (e.g., leaving Support/... in logs). Since this commit now logs redacted app_data_dir on startup, the partial match still leaks path fragments in routine logs. Update the pattern to consume full quoted/unquoted path segments including spaces.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugin rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claud useage is not visable

3 participants