Skip to content

Proposal: Provider Auth v2 (encrypted credential vault + multi-account OAuth + same-request rotation) #5748

Closed

Description

@jroth1111

Context

I'm the author of the recently opened auth-v2 PRs from a new GitHub account (my prior account lost access). One of the PRs was closed as potential spam — totally fair given the account/volume — so I'm starting an issue to discuss direction first.

The full RFC is in specs/provider-auth-v2.md (included in the PR branches).

Problem

Today's auth path is effectively single-credential and makes it hard to support:

  • multiple OAuth subscription accounts per provider
  • deterministic rotation/cooldowns on 429 within the same user request
  • refresh-on-expired (401/403) where supported
  • a single composable integration point across providers

Proposal (Auth v2)

  • Encrypted-at-rest credential vault (AES-256-GCM) with atomic writes + lockfile coordination.
  • Multi-record credential store (providerId + namespace + label), enabling multiple accounts per provider.
  • Provider auth registry/adapters to unify OAuth flows + apply auth headers for Anthropic/OpenAI/Google/Copilot/Qwen/Cursor.
  • Fetch-level rotation middleware:
    • rotate on 429 (Retry-After-aware) and retry within the same request
    • refresh on 401/403 when supported
    • persist pool ordering + cooldowns
  • Migrate legacy auth files into the new store; keep behavior compatible.

Optional UX/docs pieces:

  • TUI connected-accounts management + rotation stats
  • Opt-in OpenAI-compatible model discovery via /models with caching
  • Vault key management commands (init/export/import)

PRs (if you'd like to review code)

Questions

  1. Is this direction acceptable for OpenCode?
  2. Would you prefer the work reviewed as split PRs or as a single PR?
  3. Any preferences on vault key location / migration strategy / naming before I iterate further?

Activity

  1. jroth1111 commented on Dec 18, 2025

    @jroth1111
    Author

    Totally understand if this isn't a direction you want in core — consider this a proposal with a concrete implementation you can cherry-pick from (or close entirely).

    Why I think it's valuable:

    • Security: credentials are encrypted at rest (vault + atomic writes/locking) instead of living as plaintext JSON.
    • Reliability: same-request rotation on 429 (Retry-After-aware) and refresh-on-expired where supported reduces user-visible failures.
    • UX: supports multiple connected subscription accounts per provider/namespace; makes it possible to pool/rotate accounts intentionally.
    • Architecture: keeps auth concerns behind provider adapters and a single fetch integration point, so it's provider-agnostic.

    No pressure to merge — happy to iterate to match your preferences (split PRs, smaller surface area, rename/restructure) or you can close it and I'll keep it as a fork/local patch.

  2. github-actions commented on Dec 18, 2025

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if your proposal addresses aspects beyond these existing issues.

  3. rekram1-node commented on Dec 18, 2025

    @rekram1-node
    Collaborator

    regarding a part of this:

    how credentials are stored

    Bun added support for native secrets api, we should probably just use that and I think we have an issue w someone working on that support

  4. jroth1111 commented on Dec 18, 2025

    @jroth1111
    Author

    Thanks — re: credential storage, I agree Bun.secrets / system keyring is probably the right long-term direction (and I see #4318 is already tracking that).

    My current branch uses encrypted record files on disk (AES-256-GCM) + a vault key loaded from env or generated locally, mainly for portability/headless environments + easy backup/restore.

    If you’d prefer aligning with Bun.secrets, I can change the implementation to match #4318 in one of two ways:

    • A) Store each credential secret JSON directly in Bun.secrets (service opencode, name based on record id/provider/namespace) and keep only non-secret metadata/pool/cooldowns on disk.
    • B) Keep encrypted record files, but store the vault key in Bun.secrets (with file/env fallback for CI/server).

    Given someone is already working on #4318, I’d rather not duplicate effort — happy to either (1) refactor this to use a pluggable backend so it can swap to Bun.secrets, or (2) rebase/cherry-pick the rotation/multi-account parts on top of the eventual keyring PR.

    Which approach would you prefer?

  5. jroth1111 commented on Dec 18, 2025

    @jroth1111
    Author

    Re the possible-duplicate bot note: totally fair — this overlaps with a few threads.

    The additional piece this proposal tries to bring is the same-request rotation/refresh engine (Retry-After-aware 429 rotation + refresh-on-expired where supported) behind a single fetch integration point, plus the provider adapter registry so auth differences live in one place.

    If you’d rather land this as smaller pieces that align with those issues, I’m happy to restructure.

  6. jroth1111 commented on Dec 18, 2025

    @jroth1111
    Author

    @rekram1-node totally agree re: using Bun.secrets / system keyring for how secrets are stored (and +1 to aligning with #4318).

    The main value I’m aiming for here is multi-account OAuth subscription failover (not API-key billing):

    • Allow multiple OAuth “subscription” accounts per provider/namespace.
    • On 429 (Retry-After aware) rotate to the next account and retry within the same user request.
    • On expired auth (401/403) refresh where supported; otherwise rotate.
    • Persist pool ordering + cooldowns so failover is stable across runs.

    This is implemented as a fetch-level wrapper so most requests are transparent to the user; the known limitation is mid-stream rotation once tokens have started emitting — that still surfaces an error and the user retries (called out in the RFC).

    Happy to rework the storage backend to use Bun.secrets while keeping the multi-account + rotation engine intact.

  7. jroth1111 commented on Dec 18, 2025

    @jroth1111
    Author

    I created a new focused PR that aligns with the Bun.secrets/keychain direction and scopes to the core value (multi-account OAuth subscription failover):

    Highlights:

    • multiple OAuth accounts per provider
    • same-request retry/failover on 429 (Retry-After aware)
    • refresh-on-401/403 (force refresh once, else rotate)
    • secrets stored via Bun.secrets (service opencode), disk stores only metadata/pool state

    This avoids the larger auth-v2/UI/model-discovery/vault CLI scope.

  8. jroth1111 commented on Dec 18, 2025

    @jroth1111
    Author

    @rekram1-node quick update — I took your feedback and narrowed this down to the core value, while avoiding overlap with the keyring work in #4318.

    • Focused PR: auth: multi-account OAuth subscription failover #5754 (multi-account OAuth subscription failover)
    • Only the OAuth secrets go into Bun.secrets (service opencode); API-key / wellknown creds stay in auth.json for now
    • Robust refresh persistence: when providers/plugins persist refreshed tokens via /auth/:providerID, Auth.set() now updates the correct OAuth record by matching the refresh token (no API shape changes)
    • Rotation behavior is covered by focused tests (429 w/ Retry-After rotation + 401 refresh/rotate)

    No pressure to merge — if this isn’t the direction you want in core, totally fine to close; feel free to cherry-pick the rotation bits if they’re useful.

  9. jroth1111 commented on Jan 15, 2026

    @jroth1111
    Author

    Closing in favor of #8591 (OAuth Marathon).

    This proposal has been implemented with a simpler approach:

    What'''s included in #8591 / PR #8590:

    • ✅ Multi-record credential store (multiple OAuth accounts per provider)
    • ✅ Fetch-level rotation on 429 (Retry-After-aware) with retry within same request
    • ✅ Refresh on 401/403 before failover
    • ✅ Pool ordering + cooldowns persisted
    • ✅ Works for all OAuth providers (core + plugins) without modification

    What'''s not included (intentionally simplified):

    • ❌ Encrypted vault (uses file-based storage with file locking instead)
    • ❌ TUI credential manager
    • ❌ Model discovery via /models
    • ❌ Vault key CLI

    The simplified approach avoids complexity and potential conflicts with #4318'''s broader keyring work, while still delivering the core value of multi-account OAuth failover.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions