Files
zero/docs/oauth-subscriptions.md

11 KiB
Raw Permalink Blame History

OAuth logins & using ChatGPT / Claude subscriptions

Zero supports two distinct things people mean by "log in with OAuth":

  1. OAuth login for a provider/gateway that issues a standard bearer token — fully built in (zero auth login …). Zero attaches the token to model calls automatically.
  2. Using a ChatGPT or Claude subscription (Plus / Pro / Max) instead of a pay-per-token API key — only possible through a local proxy, for the reasons documented below. Zero ships a convenience preset and this recipe.

1. OAuth login for a provider or gateway (built in)

For any OAuth 2.0 / OIDC provider that returns a normal access token usable as Authorization: Bearer … on its API, configure it with ZERO_OAUTH_<NAME>_* env vars and log in:

export ZERO_OAUTH_ACME_CLIENT_ID=…
export ZERO_OAUTH_ACME_AUTHORIZE_URL=https://acme.example/oauth/authorize
export ZERO_OAUTH_ACME_TOKEN_URL=https://acme.example/oauth/token
export ZERO_OAUTH_ACME_SCOPES="openid profile"
zero auth login acme            # browser (loopback); --device for headless
zero auth status

When a login exists for a provider, the OpenAI and Anthropic providers send Authorization: Bearer <fresh-token> (auto-refreshed; one refresh-and-retry on a 401) instead of the API key. With no login they use the API key exactly as before. Tokens are stored 0600 (or the OS keyring with ZERO_OAUTH_STORAGE=keyring) and never logged. See zero auth --help.

In the setup wizard (/provider)

Running /provider opens a "How do you want to connect?" chooser:

❯ Sign in with OAuth                 One-click browser login (OpenRouter, xAI, ChatGPT, Hugging Face)
  Paste an API key / browse providers  Any of 20+ providers, local, or a proxy

Pick Sign in with OAuth → the list of providers that do real OAuth → choose one:

❯ OpenRouter      browser sign-in · creates a key
  xAI (Grok)      browser or device code
  ChatGPT         browser (Codex backend, ChatGPT Plus/Pro)
  Hugging Face    browser or device code
  • OpenRouter / xAI / ChatGPT / Hugging Face are real OAuth: your browser opens to approve → done (no key to paste). OpenRouter mints a key; xAI / ChatGPT / Hugging Face store a refreshable bearer. Hugging Face requires a one-time OAuth-app registration (no secret needed for "public" apps); the preset pre-fills scopes, endpoints, and the OIDC issuer. The same chooser appears in first-run onboarding. (xAI uses an opt-in preset — set ZERO_OAUTH_ALLOW_PRESETS=1 or your own ZERO_OAUTH_XAI_*; see below.)
  • Device code (headless / SSH): for a provider that supports it (xAI, Hugging Face), press d on the list to get a code to enter on another device instead of opening a browser. On an SSH session or headless Linux box (no DISPLAY) device code is used automatically; set ZERO_OAUTH_DEVICE=1 to force it anywhere. The CLI equivalent is zero auth login <name> --device.
  • ChatGPT / Claude are intentionally not in this list for the proxy path — use the dedicated chatgpt-proxy / custom-anthropic-compatible preset (see §2) for subscription-via-proxy. ChatGPT is a first-class OAuth provider in this version (routes to the Codex backend) — see "Built-in OAuth providers" below.

Built-in OAuth providers

  • OpenRouter (no env needed) — zero auth openrouter opens a browser, you approve, and it mints an OpenRouter API key (public PKCE flow, no client_id). In the interactive setup wizard, pick OpenRouter and press ctrl+o at the key step to do the same inline ("Log in with OAuth"). The minted key is saved to the provider profile and used normally.
  • xAI (Grok) — opt-in preset — xAI's flow needs an OAuth client_id. Zero ships a built-in preset for the public Grok-CLI client, but to keep third-party client identities out of the default credential path it is off by default. Enable it with export ZERO_OAUTH_ALLOW_PRESETS=1, then zero auth login xai (browser, or --device for headless) works one-click; the token is used directly on api.x.ai/v1. Without the opt-in, set ZERO_OAUTH_XAI_CLIENT_ID (and endpoints, or an issuer) yourself via ZERO_OAUTH_XAI_*. Either way the preset is fully overridable by ZERO_OAUTH_XAI_* (env wins), and it requires a SuperGrok / X Premium+ subscription; the client_id is an undocumented public Grok-CLI client that may change without notice.
  • ChatGPT (Codex) — opt-in preset — zero auth chatgpt opens a browser, you approve with your ChatGPT Plus/Pro/Business/Enterprise account, and the bearer is stored. The bearer routes to https://chatgpt.com/backend-api/codex/responses (the same endpoint the openai/codex CLI uses), with originator: codex_cli_rs and the chatgpt-account-id claim injected as headers on every request. The chatgpt-account-id is extracted from the OIDC ID token and stored alongside the bearer; if the claim is missing (older ChatGPT accounts, or a rotated authorization server), the Codex backend will 401 and zero auth status chatgpt will show the warning. Like xAI, the preset uses the publicly-shipped Codex CLI client identity (app_EMoamEEZ73f0CkXaXp7hrann) and is opt-in via ZERO_OAUTH_ALLOW_PRESETS=1. As of mid-2026 the Codex backend is Cloudflare-gated: requests from a non-Codex client can still be challenged, and the chatgpt-proxy route in §2 is the conservative fallback.
  • Hugging Face — opt-in preset, BYO client_id — zero auth login huggingface (or --device for headless) opens a Hugging Face OAuth flow. The bearer works on the OpenAI-compatible router at https://router.huggingface.co/v1 for hundreds of OSS models (Llama, Qwen, DeepSeek, Mistral, etc.). HF does not ship a globally-known client_id, so the preset ships endpoints + scopes + the OIDC issuer pre-filled; you must register a "public" OAuth app (no secret) at https://huggingface.co/settings/applications/new and set the resulting client_id via ZERO_OAUTH_HUGGINGFACE_CLIENT_ID. Enable the preset with ZERO_OAUTH_ALLOW_PRESETS=1 (or omit it — the BYO client_id path uses client_credentials = none and doesn't need the opt-in). Free tier has strict rate limits; Pro removes them.

Any field of a preset is overridable via ZERO_OAUTH_<NAME>_*. For a fully custom OAuth/OIDC provider, set those env vars (see zero auth --help) and zero auth login <name>.


2. ChatGPT / Claude subscriptions — why a proxy is required

We researched this carefully. As of mid-2026, a subscription OAuth token does not work as a drop-in bearer against the standard APIs:

  • OpenAI (ChatGPT): a "Sign in with ChatGPT" token only works against ChatGPT's own backend (chatgpt.com/backend-api/codex/responses, the Responses API), not api.openai.com. That backend is Cloudflare bot-protected — non-browser / headless clients get cf-mitigated: challenge → 403. It also requires mimicking the official Codex client (originator + account-id header). First-class path (this version): zero auth chatgpt does exactly that mimicking (originator: codex_cli_rs, chatgpt-account-id: <claim>) and routes requests to the Codex backend, no proxy required — see §1. The chatgpt-proxy route below is the conservative fallback when Cloudflare challenges become an issue, and is the only path that works without a browser-based ChatGPT OAuth login.
  • Anthropic (Claude): the Messages API rejects subscription OAuth tokens for third-party use unless the request spoofs the Claude Code identity (anthropic-beta: oauth-2025-04-20, claude-cli UA, and a verbatim "You are Claude Code…" system prompt) — and even then tool-using requests on Max plans are routed to a disabled billing lane and 400. Anthropic's policy prohibits subscription-token use outside Claude Code / claude.ai, and the timeline hardened through 2026: a Feb 19 2026 docs update spelled out that Free/Pro/Max OAuth tokens may not be used in third-party tools or the Agent SDK, then on April 4 2026 enforcement landed and subscription OAuth tokens stopped working in third-party harnesses (starting with OpenClaw, then the rest). As of mid-2026 the only supported ways to drive Claude from a third-party tool are a standard API key or pay-as-you-go "Extra Usage" billing — both per-token, not the flat subscription. The request to allow subscription use (claude-code #37205) was closed "not planned."

So Zero does not call those backends directly or spoof those clients — that would be fragile, account-risky, and (for Anthropic) against the vendor's terms. The robust, supported pattern is a local proxy that holds your subscription session and exposes a clean OpenAI- or Anthropic-compatible endpoint on 127.0.0.1. The proxy absorbs the Cloudflare / client-spoofing surface; Zero just points at it.

ChatGPT via a local proxy

Run a local ChatGPT OAuth proxy that exposes an OpenAI-compatible endpoint (these typically listen on 127.0.0.1:10531/v1). Then use the built-in chatgpt-proxy preset (no API key — the proxy authenticates):

// ~/.config/zero/config.json (or ./.zero/config.json)
{
  "activeProvider": "chatgpt",
  "providers": [
    {
      "name": "chatgpt",
      "catalogID": "chatgpt-proxy",     // OpenAI-compatible, local, no key
      "baseURL": "http://localhost:10531/v1", // override for your proxy's port
      "model": "gpt-5.5"                 // whatever model your proxy serves
    }
  ]
}
zero exec --prompt "say hi"   # routes through the proxy → your ChatGPT plan

Claude via a local proxy

There is no single canonical Claude OAuth-proxy port, so use the generic custom-anthropic-compatible entry pointed at your proxy's Anthropic-compatible endpoint:

{
  "activeProvider": "claude",
  "providers": [
    {
      "name": "claude",
      "catalogID": "custom-anthropic-compatible",
      "baseURL": "http://localhost:<port>",  // your Claude proxy
      "apiKey": "unused-by-proxy",
      "model": "claude-sonnet-4.5"
    }
  ]
}

3. Supported alternatives (no proxy)

  • API key (recommended, simplest): set OPENAI_API_KEY / ANTHROPIC_API_KEY (or per-profile apiKey) and use the openai / anthropic catalog providers. Bills as API usage.
  • Anthropic subscription automation, sanctioned: spawn the real claude CLI (e.g. claude -p …) as a subprocess — the only path Anthropic recognizes as a first-class subscription session.

Notes

  • The chatgpt-proxy base URL / port and model are defaults you override for your setup; they are not an endorsement of any specific proxy implementation.
  • Subscription-via-proxy depends on third-party tools and undocumented vendor backends; it can break without notice. The API-key path is the stable one.