Skip to content

feat(auth): add secure loopback OAuth broker for Codex profiles - #64406

Draft
aslxiaomu wants to merge 5 commits into
NousResearch:mainfrom
aisilun:feat/oauth-broker-loopback
Draft

feat(auth): add secure loopback OAuth broker for Codex profiles#64406
aslxiaomu wants to merge 5 commits into
NousResearch:mainfrom
aisilun:feat/oauth-broker-loopback

Conversation

@aslxiaomu

Copy link
Copy Markdown

What does this PR do?

Adds an opt-in, per-machine loopback OAuth broker for OpenAI Codex profiles. The broker owns one rotating OAuth grant per account alias (A, B, C), keeps grants and the local client key in macOS Keychain, and transparently proxies Codex Responses and usage traffic.

The goal is to prevent multiple Hermes runtimes from cloning and concurrently consuming the same single-use refresh token. The broker is fail-closed: profiles do not retain upstream access/refresh tokens and there is no automatic fallback to a cloned legacy pool.

This is related to #23944 and the refresh-token failure described in #22903.

Related open PRs were reviewed before submission:

Related Issue

Related to #23944 and #22903.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • Add agent/oauth_broker/ with per-account singleflight refresh, cross-process locks, recovery markers, strict grant schemas, generation-aware 401 replay, and transparent 429/SSE forwarding.
  • Add Security.framework-backed keychain:// secret resolution without shelling out to the security CLI.
  • Add hermes oauth-broker commands for auth, run/status/doctor, launchd lifecycle, migration, and rollback.
  • Extend credential-pool/runtime handling for broker client-key references and fail-closed selection.
  • Add transactional profile migration with schema-v2 snapshots, durable journals, atomic writes, and mixed-state recovery.
  • Add user documentation and synthetic/fake-backed tests; no real Keychain values or network OAuth grants are used by the test suite.

How to Test

  1. Run the focused broker and integration suite:
    python -m pytest -q -o 'addopts=' \
      tests/agent/oauth_broker \
      tests/agent/test_keychain_secret.py \
      tests/agent/test_credential_pool*.py \
      tests/agent/test_auxiliary_client*.py \
      tests/hermes_cli/test_oauth_broker_*.py \
      --deselect tests/agent/test_credential_pool_oat_authtype.py::test_profile_global_fallback_normalizes_in_memory_without_writing
    Result: 741 passed, 1 deselected.
  2. Run the CLI/plugin-discovery regression suite:
    python -m pytest -q -o 'addopts=' \
      tests/hermes_cli/test_startup_plugin_gating.py \
      tests/hermes_cli/test_oauth_broker_cli.py \
      tests/hermes_cli/test_oauth_broker_help.py
    Result: 60 passed.
  3. Run static checks:
    ruff check <changed Python files>
    ty check agent/keychain_secret.py agent/oauth_broker hermes_cli/oauth_broker.py hermes_cli/subcommands/oauth_broker.py
    git diff --check
    Result: passed.
  4. Run the repository wrapper:
    scripts/run_tests.sh
    Result: non-zero on this machine because the profile-scoped git wrapper intercepts temp-HERMES_HOME repository tests. An exact-parent baseline run at 226e8de82 reported 33 failing files / 180 failed tests; the post-fix branch run reported 31 failing files / 177 failed tests, and every branch failure file is contained in the baseline set (zero branch-only failing files). The changed-area suites above pass.

Checklist

Code

Documentation & Housekeeping

  • I've updated relevant documentation
  • cli-config.yaml.example — N/A; no persistent config key is added
  • CONTRIBUTING.md / AGENTS.md — N/A; contributor workflow is unchanged
  • I've considered cross-platform impact — the daemon is explicitly opt-in and macOS-Keychain-backed; imports and fake-backed tests remain isolated from real Keychain access
  • Tool descriptions/schemas — N/A

Screenshots / Logs

  • Focused related suite: 741 passed, 1 deselected
  • CLI/plugin-discovery suite: 60 passed
  • Independent security review covered migration interruption cleanup, strict grant/URI parsing, external re-auth precedence, cancellation-safe locks, Cookie isolation, gzip 429 forwarding, and launchd rollback.

@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 area/auth Authentication, OAuth, credential pools provider/openai OpenAI / Codex Responses API P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jul 14, 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 the substantial security-focused implementation. The underlying Codex refresh race is already addressed on current main, so this needs review as a new optional broker architecture rather than as a still-unfixed concurrency repair.

Problems

  • Current main already locks the Codex refresh read→POST→write-back sequence in agent/credential_pool.py:1022-1044; commit da6d5fcd13af2adb6ce7961e06f85cf714fde7f5 documents that exact single-use-token race.
  • The guide is added under docs/user-guide/features/oauth-broker.md:1, whereas published docs are sourced from website/docs; website/sidebars.ts:53-123 also has no new entry. Its docs/design/oauth-broker.md reference at line 7 is not added by this PR.
  • tests/hermes_cli/test_oauth_broker_cli.py:136 reads hermes_cli/main.py and asserts strings, contrary to the source-reading test prohibition in AGENTS.md:1370.

Suggested changes

  • Reframe the feature’s independent value and move the guide into the published docs surface.
  • Replace the source-shape assertion with a real parser/dispatch behavior test.

Automated hermes-sweeper review.

chain per account alias (`A`, `B`, `C`) and transparently forwards Codex
Responses/usage traffic for local Hermes profiles. Profiles stop holding
OpenAI tokens entirely — they persist only a `keychain://` reference to the
broker's local client key.

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 guide is outside the Docusaurus source tree (website/docs), and this referenced docs/design/oauth-broker.md file is not part of the PR. Please move the published guide into website/docs/user-guide/features/, add the sidebar entry, and either add this design document or remove the link.



def test_main_registers_oauth_broker_parser():
main_source = Path("hermes_cli/main.py").read_text(encoding="utf-8")

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.

Please replace this source-text assertion with a behavior test. AGENTS.md explicitly forbids tests that read source code; parse/dispatch the real CLI and assert the registered command behavior instead.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/profiles Multi-profile isolation, HERMES_HOME scoping labels Jul 16, 2026
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 area/profiles Multi-profile isolation, HERMES_HOME scoping comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have provider/openai OpenAI / Codex Responses API sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows 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