Skip to content

feat(auth): machine-wide Anthropic OAuth shared credential pool - #70088

Open
danpetkovic wants to merge 1 commit into
NousResearch:mainfrom
danpetkovic:feat/universal-anthropic-oauth-pool
Open

feat(auth): machine-wide Anthropic OAuth shared credential pool#70088
danpetkovic wants to merge 1 commit into
NousResearch:mainfrom
danpetkovic:feat/universal-anthropic-oauth-pool

Conversation

@danpetkovic

Copy link
Copy Markdown

Summary

Implements an opt-in, machine-wide Anthropic OAuth-only credential pool shared by every Hermes profile under one root. Default behavior is unchanged until the operator enables shared scope.

Closes the multi-profile re-materialization / single-use refresh races for Anthropic Max OAuth that were fixed earlier for Codex/xAI.

Design

  • Scope marker: <root>/shared/anthropic_pool_scope.json (absent = legacy profile mode)
  • Canonical store: root auth.jsonshared_credential_pools.anthropic (never profile credential_pool)
  • Exactly three OAuth grants (manual:hermes_pkce), strategy fixed to fill_first
  • Cross-process root-auth lock + token_generation / refresh_attempt for single-use refresh safety
  • Authoritative resolution gate: while shared + official api.anthropic.com, ignore env keys, Claude Code files, explicit api_key, and profile rows
  • Non-official targets (Azure/Bedrock/custom) keep endpoint-specific auth
  • CLI: hermes auth scope|backup|restore, --shared on add/remove/reset/logout
  • Dashboard pool mutations return HTTP 409 in shared mode
  • Generic backup refuses when active/dormant shared state exists
  • Break-glass helper + deterministic secret scanner

Prior art

Test plan

  • Focused gate list 128/128 green
  • Secret scanner clean on diff
  • ruff on touched modules
  • CI full suite

Fork merge: danpetkovic#1 (32d2482baf2170d73e7da05a4028973086fbf0ff)

Add an opt-in shared OAuth-only Anthropic pool under the Hermes root so every
profile resolves the same three grants with one canonical refresh-token chain
per account. Shared scope is controlled by a non-secret marker, uses strict
root-auth locking for single-use refresh, closes env/Claude-Code/explicit-key
bypasses for official api.anthropic.com targets, and guards CLI/dashboard/
backup mutation paths. Includes focused tests, secret scanner, break-glass
helper, and docs.

Refs: NousResearch#29530, NousResearch#8040
@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard provider/anthropic Anthropic native Messages API area/auth Authentication, OAuth, credential pools P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 23, 2026

@teknium1 teknium1 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.

Thanks for tackling the real multi-profile OAuth refresh race. Current main still serializes only Codex/xAI refreshes in agent/credential_pool.py:1132-1163, so the underlying Anthropic gap remains.

Problems

  • hermes_cli/auth.py:516-525 catches an unexpected shared-resolver error, then swallows its own re-raise and falls back to ANTHROPIC_* credentials at :527-533. Active shared scope must fail closed.
  • agent/auxiliary_client.py:3763-3790 catches all shared refresh/load failures at :3780, then can refresh a Claude Code credential and return success at :3790. This is another shared-scope bypass.
  • agent/anthropic_shared_pool.py:1668-1672 reads and archives the marker before validating it is not a symlink, contradicting the module's no-follow safety contract.

Suggested changes

  • Route every active-shared-scope path through one fail-closed authoritative resolver, including auxiliary refresh.
  • Validate marker file type before reading it, then add regression coverage for resolver failures with valid env/Claude Code credentials.

This is an automated hermes-sweeper review.

Comment thread hermes_cli/auth.py
return ctx.access_token
except AuthError:
raise
except Exception:

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.

This handler fails open. If resolve_shared_anthropic_credential() raises a non-AuthError while the marker is active, the bare re-raise inside the nested try is swallowed by its except Exception, and the function returns an ANTHROPIC_* credential below. Preserve the original error whenever shared scope is active.

Comment thread agent/auxiliary_client.py
return False
_evict_cached_clients(normalized)
return True
except Exception:

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.

Do not swallow shared-pool failures here. The legacy branch immediately refreshes Claude Code credentials and can return success while shared scope is active, bypassing the asserted authoritative shared credential gate.

ts = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
backup = recovery_dir() / f"{ts}-anthropic-scope.json"
# Read raw bytes without following if possible
raw = path.read_bytes() if path.exists() else b""

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.

Validate this path before reading it. Path.read_bytes() follows a symlink, so this archives its target before _path_is_safe_regular_file() rejects the marker at line 1672. Use the existing no-follow validation/read pattern first.

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

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have provider/anthropic Anthropic native Messages API sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants