Skip to content

core: support dynamic auth tokens for model providers#16267

Closed
bolinfest wants to merge 1 commit intopr16275from
pr16267
Closed

core: support dynamic auth tokens for model providers#16267
bolinfest wants to merge 1 commit intopr16275from
pr16267

Conversation

@bolinfest
Copy link
Copy Markdown
Collaborator

@bolinfest bolinfest commented Mar 30, 2026

Summary

Fixes #15189.

Custom model providers that set requires_openai_auth = false could only use static credentials via env_key or experimental_bearer_token. That is not enough for providers that mint short-lived bearer tokens, because Codex had no way to resolve a provider token dynamically, cache it briefly in memory, and refresh it after a 401.

This PR adds command-backed provider auth on top of #16277 and #16275. The key design point is that request paths still do not get a second auth abstraction: provider traffic uses a provider-scoped AuthManager, so both token resolution and 401 recovery continue to flow through AuthManager.auth() and UnauthorizedRecovery.

Example usage

model_provider = "corp-openai"

[model_providers.corp-openai]
name = "Corp OpenAI"
base_url = "https://gateway.example.com/openai"
requires_openai_auth = false

[model_providers.corp-openai.auth]
command = "gcloud"
args = ["auth", "print-access-token"]
timeout_ms = 5000
refresh_interval_ms = 300000

The command contract is intentionally small:

  • write the bearer token to stdout
  • exit 0
  • any leading or trailing whitespace is trimmed before the token is used

What changed

  • add ModelProviderAuthInfo and model_providers.<id>.auth to the config model and generated schema
  • validate that command-backed provider auth is mutually exclusive with env_key, experimental_bearer_token, and requires_openai_auth
  • add core/src/provider_auth.rs, which implements a command-backed ExternalAuthRefresher with in-memory TTL caching, timeout handling, and cwd support
  • wrap the request-facing AuthManager instances used by ModelClient and ModelsManager when a provider configures auth, so provider requests use the external bearer source without affecting the base session auth manager
  • let /models refresh online for command-auth providers and retry once after 401 through UnauthorizedRecovery
  • route realtime auth through the model client's scoped auth manager so websocket connects use the same provider token source as normal requests
  • keep auth.cwd available as an advanced escape hatch and include it in the generated config schema

Testing

  • CARGO_TARGET_DIR=/tmp/codex-core-provider-auth cargo check -p codex-core
  • CARGO_TARGET_DIR=/tmp/codex-core-provider-auth-tests cargo test -p codex-core provider_auth::tests::
  • CARGO_TARGET_DIR=/tmp/codex-core-provider-auth-tests cargo test -p codex-core provider_auth_command_supplies_bearer_token
  • CARGO_TARGET_DIR=/tmp/codex-core-provider-auth-tests cargo test -p codex-core provider_auth_command_refreshes_after_401
  • CARGO_TARGET_DIR=/tmp/codex-core-provider-auth-tests cargo test -p codex-core refresh_available_models_refreshes_provider_auth_after_401
  • CARGO_TARGET_DIR=/tmp/codex-core-provider-auth-tests cargo test -p codex-core test_deserialize_provider_auth_config_defaults
  • CARGO_TARGET_DIR=/tmp/codex-core-provider-auth-tests cargo test -p codex-core rejects_provider_auth_with_env_key

Docs

  • developers.openai.com/codex should document the new [model_providers.<id>.auth] block and the token-command contract

Stack created with Sapling. Best reviewed with ReviewStack.

@bolinfest bolinfest changed the title add support for dynamic auth token for model provider core: support dynamic auth tokens for model providers Mar 30, 2026
Copy link
Copy Markdown
Contributor

@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: be08ad0e4b

ℹ️ 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".


/// Working directory used when running the token command.
#[serde(default = "default_provider_auth_cwd")]
#[schemars(skip)]
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.

P2 Badge Expose auth.cwd in schema or stop serializing it

ModelProviderAuthInfo marks cwd with #[schemars(skip)], but the type is still deserialized/serialized and ModelProviderAuthInfo uses additionalProperties: false in the generated schema. This creates a mismatch where valid runtime config (or config emitted by serialization that includes auth.cwd) is treated as invalid by schema-based tooling and editors, which can block users from persisting or validating working provider auth configs.

Useful? React with 👍 / 👎.

@bolinfest bolinfest force-pushed the pr16267 branch 3 times, most recently from 807527f to bcacd8d Compare March 30, 2026 20:50
@bolinfest bolinfest changed the base branch from main to pr16275 March 30, 2026 20:50
@bolinfest bolinfest force-pushed the pr16275 branch 2 times, most recently from 65a3e63 to d4cf2fa Compare March 30, 2026 21:02
@bolinfest bolinfest force-pushed the pr16267 branch 2 times, most recently from 056d9e4 to 84ed96f Compare March 30, 2026 21:41
@bolinfest bolinfest changed the base branch from pr16275 to pr16277 March 30, 2026 21:41
@bolinfest bolinfest changed the base branch from pr16277 to pr16275 March 30, 2026 23:51
@bolinfest
Copy link
Copy Markdown
Collaborator Author

Closing in favor of #16288.

@bolinfest bolinfest closed this Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support dynamic bearer token refresh for custom model providers

1 participant