docs: Improved openAI compatible provider OAuth documentation
+192
-56
@@ -210,20 +210,165 @@ clients:
|
||||
auth: oauth
|
||||
```
|
||||
|
||||
### OAuth for OpenAI-Compatible Providers
|
||||
## OAuth for OpenAI-Compatible Providers
|
||||
|
||||
For any provider that speaks the OpenAI API shape, you can enable OAuth on an `openai-compatible` client. Coyote resolves
|
||||
OAuth configuration in two layers:
|
||||
For any provider that speaks the OpenAI API shape (`type: openai-compatible`), you can enable OAuth by setting
|
||||
`auth: oauth` and configuring an `oauth:` block. This section is your reference for what fields each OAuth flow needs.
|
||||
|
||||
1. **Bundled defaults** in `models.yaml` under `provider: <name>` (Coyote ships defaults for providers like xAI).
|
||||
2. **Inline overrides** in your `config.yaml` under `clients[i].oauth`. You can either supply the whole block (for
|
||||
providers not shipped with Coyote) or override individual fields (e.g. to point at a corporate token endpoint).
|
||||
**Not needed for built-in providers** (Claude, Gemini, OpenAI, xAI-when-using-bundled-defaults): those work with just
|
||||
`auth: oauth`, meaning no fields are required. See the [provider-specific notes above](#openai-oauth-note) for details.
|
||||
|
||||
For each optional field, the user's `config.yaml` value wins over the `models.yaml` default. If neither source provides
|
||||
a required field (`client_id`, `token_url`), Coyote will bail with a clear error when you attempt to authenticate.
|
||||
### Which flow does my provider use?
|
||||
|
||||
**Example: inline OAuth for a private gateway:**
|
||||
Look at your provider's own login docs or CLI for signal words:
|
||||
|
||||
| What your provider does | Flow to configure | Section |
|
||||
|---|---|---|
|
||||
| Opens your browser, redirects to a `http://localhost` or `http://127.0.0.1` URL | Authorization Code + PKCE (loopback callback) | [Flow 1, Mode A](#flow-1-authorization-code--pkce) |
|
||||
| Opens your browser, redirects to a `https://...` URL you'd copy from the address bar | Authorization Code + PKCE (browser-paste) | [Flow 1, Mode B](#flow-1-authorization-code--pkce) |
|
||||
| Displays a short user code you enter at a URL, with no browser callback | Device Authorization Grant (RFC 8628) | [Flow 2](#flow-2-device-authorization-grant-rfc-8628) |
|
||||
| No user at all; just `client_id` + `client_secret` exchanged headlessly | Client Credentials (RFC 6749 §4.4) | [Flow 3](#flow-3-client-credentials-m2m) |
|
||||
|
||||
If your provider's OAuth app is already registered with a specific redirect URI, Coyote's routing follows it: put the
|
||||
value in `redirect_uri` and Coyote does the right thing based on the URL shape (loopback vs public).
|
||||
|
||||
### Flow 1: Authorization Code + PKCE
|
||||
|
||||
Standard OAuth 2.0 with PKCE (RFC 7636). Coyote opens the authorize URL in your browser, receives back an authorization
|
||||
code, and exchanges it for tokens. Refresh tokens are supported (silently refreshed on expiry).
|
||||
|
||||
Coyote picks between two delivery modes automatically based on what you set for the callback:
|
||||
|
||||
#### Mode A: Loopback callback (Coyote listens on your machine)
|
||||
|
||||
Coyote binds a TCP listener on 127.0.0.1 and the browser redirects there directly. This is the smoothest UX (i.e. no manual
|
||||
copy/paste), but requires that the provider's OAuth app allows a loopback redirect URI.
|
||||
|
||||
**Required fields:**
|
||||
- `flow: pkce` (or omit. This is the default)
|
||||
- `client_id`: your OAuth app's client ID
|
||||
- `authorize_url`: where the browser goes to authorize
|
||||
- `token_url`: where Coyote exchanges the code for tokens
|
||||
|
||||
**Callback config (pick one):**
|
||||
- `redirect_port: <u16>`: if the provider registered a specific port (e.g. `56121` for xAI). Coyote synthesizes
|
||||
`http://127.0.0.1:<port>/callback`.
|
||||
- `redirect_uri: http://127.0.0.1:<port>/<path>`: full custom loopback URL (overrides `redirect_port`).
|
||||
- Omit both: Coyote picks an ephemeral port on 127.0.0.1 (only works if the provider allows arbitrary loopback ports).
|
||||
|
||||
**Optional fields:**
|
||||
- `scopes: [scope1, scope2]`: space-joined into the `scope=` query param.
|
||||
- `extra_authorize_params: { key: value }`: extra query params on the authorize URL (e.g. xAI needs `plan: generic`).
|
||||
- `echo_pkce_in_token_exchange: true`: some providers (like xAI) require re-sending `code_challenge` on token exchange.
|
||||
- `include_state_in_token_exchange: false`: some providers reject `state` on the token endpoint (default `true`).
|
||||
- `token_request_format: form_url_encoded`: some providers require form-encoded token bodies (default is
|
||||
`form_url_encoded` for openai-compatible).
|
||||
- `client_secret`: rare with PKCE, but supported for hybrid setups.
|
||||
|
||||
**Example:**
|
||||
```yaml
|
||||
clients:
|
||||
- type: openai-compatible
|
||||
name: my-provider
|
||||
api_base: https://api.provider.com/v1
|
||||
auth: oauth
|
||||
oauth:
|
||||
client_id: '{{PROVIDER_CLIENT_ID}}'
|
||||
authorize_url: https://auth.provider.com/oauth/authorize
|
||||
token_url: https://auth.provider.com/oauth/token
|
||||
redirect_port: 8765
|
||||
scopes: [read, write]
|
||||
```
|
||||
|
||||
#### Mode B: Browser-paste (you paste the callback back into the terminal)
|
||||
|
||||
For providers whose OAuth app is registered with a **public HTTPS URL** (not loopback). The browser redirects to that
|
||||
URL, the provider's page displays the code (or you copy it from the URL bar), and you paste it back into Coyote.
|
||||
|
||||
Coyote detects Mode B automatically when `redirect_uri` is set to a **non-loopback URL** (anything other than 127.0.0.1,
|
||||
localhost, or ::1). No opt-in flag needed.
|
||||
|
||||
**Required fields:** same as Mode A above, except the callback config:
|
||||
- `redirect_uri: https://<provider-callback-host>/<path>`: the exact URL the provider is registered with.
|
||||
|
||||
**What Coyote accepts at the paste prompt:**
|
||||
1. **Full callback URL from the browser bar** (e.g. `https://provider.example.com/callback?code=abc&state=xyz`).
|
||||
Coyote parses `code` and `state` from the query string.
|
||||
2. **`<code>#<state>` fragment** (Anthropic-style; supported for provider pages that display both concatenated).
|
||||
3. **Bare code** (just the authorization code, no state). Accepted with a warning. CSRF state validation is
|
||||
skipped. Use this for providers whose callback page shows only the code.
|
||||
|
||||
**Example:**
|
||||
```yaml
|
||||
clients:
|
||||
- type: openai-compatible
|
||||
name: my-hosted-provider
|
||||
api_base: https://api.provider.com/v1
|
||||
auth: oauth
|
||||
oauth:
|
||||
client_id: '{{PROVIDER_CLIENT_ID}}'
|
||||
authorize_url: https://auth.provider.com/oauth/authorize
|
||||
token_url: https://auth.provider.com/oauth/token
|
||||
redirect_uri: https://provider.com/oauth/callback # public URL -> paste mode
|
||||
scopes: [read]
|
||||
```
|
||||
|
||||
### Flow 2: Device Authorization Grant (RFC 8628)
|
||||
|
||||
For CLIs and headless environments: **no browser callback required**. Coyote displays a short `user_code` and a
|
||||
`verification_uri`. You visit the URL on any device (phone, another laptop, or the same browser Coyote tries to open)
|
||||
and enter the code to approve. Coyote polls the token endpoint until you approve or the code expires. Refresh tokens
|
||||
are supported.
|
||||
|
||||
**Required fields:**
|
||||
- `flow: device_code`
|
||||
- `client_id`: your OAuth app's client ID
|
||||
- `device_authorization_url`: where Coyote requests the initial `user_code`
|
||||
- `token_url`: where Coyote polls for the token
|
||||
|
||||
**Optional fields:**
|
||||
- `scopes: [scope1, scope2]`
|
||||
- `use_pkce_in_device_flow: true`: some providers (like MiniMax) require PKCE with device flow. Coyote generates a
|
||||
fresh `code_verifier` + `code_challenge` (S256) on each authentication.
|
||||
- `token_request_format: form_url_encoded`: usually needed for device flow (default for openai-compatible).
|
||||
- `extra_token_headers: { key: value }`: custom headers applied to both device and token endpoints.
|
||||
- `client_secret`: rare but supported.
|
||||
|
||||
**Example:**
|
||||
```yaml
|
||||
clients:
|
||||
- type: openai-compatible
|
||||
name: my-device-flow-provider
|
||||
api_base: https://api.example.com/v1
|
||||
auth: oauth
|
||||
oauth:
|
||||
client_id: '{{MY_CLIENT_ID}}'
|
||||
device_authorization_url: https://auth.example.com/oauth/device_authorization
|
||||
token_url: https://auth.example.com/oauth/token
|
||||
flow: device_code
|
||||
scopes: [openid, profile]
|
||||
```
|
||||
|
||||
**Sandbox bonus:** when `IS_SANDBOX=1` (set automatically by `coyote --sandbox`), Coyote renders a scannable QR code
|
||||
in the terminal so you can point your phone camera at the verification URL and complete the flow in seconds.
|
||||
|
||||
### Flow 3: Client Credentials (M2M)
|
||||
|
||||
For headless machine-to-machine authentication. No user, no browser. Coyote POSTs directly to the token endpoint with
|
||||
`client_id` + `client_secret`. Best for CI, servers, agent runtimes.
|
||||
|
||||
**Required fields:**
|
||||
- `flow: client_credentials`
|
||||
- `client_id`
|
||||
- `client_secret`
|
||||
- `token_url`
|
||||
|
||||
**Optional fields:**
|
||||
- `scopes: [scope1, scope2]`
|
||||
- `token_request_format`
|
||||
- `extra_token_headers: { key: value }`
|
||||
|
||||
**Example:**
|
||||
```yaml
|
||||
clients:
|
||||
- type: openai-compatible
|
||||
@@ -238,49 +383,32 @@ clients:
|
||||
flow: client_credentials
|
||||
```
|
||||
|
||||
### client_credentials Flow
|
||||
**Note on refresh:** RFC 6749 forbids refresh tokens for this flow. When the access token expires, Coyote
|
||||
silently re-runs the client credentials exchange (still no browser or user interaction).
|
||||
|
||||
For headless machine-to-machine (M2M) authentication (i.e. no browser, no user interaction), set `flow: client_credentials`
|
||||
in your `oauth` block. Coyote will `POST` directly to the token endpoint with `client_id` and `client_secret` and cache
|
||||
the returned access token.
|
||||
### Providers with Bundled OAuth Defaults
|
||||
|
||||
RFC 6749 forbids refresh tokens for the client credentials flow, so when the token expires Coyote silently
|
||||
re-runs the client credentials exchange (still no browser). This makes it suitable for CI, servers, and other
|
||||
non-interactive environments.
|
||||
Coyote ships OAuth defaults for the following providers, so you only need `auth: oauth` in your config, with no fields
|
||||
required:
|
||||
|
||||
### Device Authorization Grant (Device Flow, RFC 8628)
|
||||
* **xAI:** SuperGrok / X Premium+ subscribers. See [xAI OAuth Note](#xai-oauth-note) above for behavior details.
|
||||
|
||||
For CLIs and headless environments; no browser callback required. Coyote displays a short
|
||||
`user_code` and a `verification_uri`. You visit the URL on any device (your phone, another
|
||||
computer, or the same browser Coyote tries to open) and enter the code to approve. Coyote
|
||||
polls the token endpoint until you approve or the code expires.
|
||||
To bundle defaults for additional providers locally, edit your `models.yaml` (see [Local Models Override](#local-models-override))
|
||||
and add an `oauth:` block under the provider entry using the field reference for the appropriate flow above.
|
||||
|
||||
To enable, set `flow: device_code` in your `oauth:` block and provide a `device_authorization_url`:
|
||||
### Configuration Precedence
|
||||
|
||||
clients:
|
||||
- type: openai-compatible
|
||||
name: my-device-flow-provider
|
||||
api_base: https://api.example.com/v1
|
||||
auth: oauth
|
||||
oauth:
|
||||
client_id: '{{MY_CLIENT_ID}}'
|
||||
device_authorization_url: https://auth.example.com/oauth/device_authorization
|
||||
token_url: https://auth.example.com/oauth/token
|
||||
flow: device_code
|
||||
Coyote resolves OAuth configuration in two layers:
|
||||
|
||||
**Optional:** some providers require PKCE with device flow. Set `use_pkce_in_device_flow: true` to opt
|
||||
in. Coyote generates a fresh `code_verifier` and `code_challenge` on each authentication.
|
||||
1. **Bundled defaults** in `models.yaml` under `provider: <name>`.
|
||||
2. **Inline overrides** in your `config.yaml` under `clients[i].oauth`.
|
||||
|
||||
Token refresh works the same as the Auth Code + PKCE flow: `refresh_token` is silently
|
||||
exchanged for a new `access_token` when the current one expires.
|
||||
Per-field: `config.yaml` wins over `models.yaml`. If neither source provides a required field for your flow (see the
|
||||
"Required fields" list in each flow section above), Coyote bails with a clear error at authentication time telling you
|
||||
which field is missing.
|
||||
|
||||
**Sandbox / headless environments:** when Coyote runs inside a sandbox (`coyote --sandbox`),
|
||||
your host OAuth tokens (`~/.cache/coyote/oauth/`) are copied into the container so agents can
|
||||
call OAuth-authenticated providers without re-authenticating. If you do need to authenticate
|
||||
from inside a sandbox, Device Flow works out of the box: Coyote prints a QR code you can
|
||||
scan with your phone camera to visit the verification URL. Auth Code + PKCE flows (like xAI's)
|
||||
generally cannot complete inside a sandbox because they require an inbound callback listener;
|
||||
authenticate on the host first and let the token copy carry you through.
|
||||
You can override individual fields. For example, if the bundled default has the wrong token URL for your corporate
|
||||
gateway, just set `oauth.token_url` inline and the rest of the bundled defaults still apply.
|
||||
|
||||
### Known Limitation: Token Namespacing
|
||||
|
||||
@@ -288,27 +416,35 @@ OAuth tokens are stored on disk at `~/.cache/coyote/oauth/{client_name}_oauth_to
|
||||
field only (not by provider type). Two clients with the same `name`, even if they use different provider types, will
|
||||
overwrite each other's tokens.
|
||||
|
||||
To prevent accidental collisions, Coyote now rejects configuration files with duplicate `name` values across all
|
||||
To prevent accidental collisions, Coyote rejects configuration files with duplicate `name` values across all
|
||||
`clients[]` entries at load time. Fixing the underlying single-namespace layout is future work.
|
||||
|
||||
### OpenAI-Compatible Providers with Bundled OAuth Defaults
|
||||
### Sandbox and headless environments
|
||||
|
||||
Coyote ships OAuth defaults for the following providers, so you only need `auth: oauth` in your config:
|
||||
When Coyote runs inside a sandbox (`coyote --sandbox`), your host OAuth tokens (`~/.cache/coyote/oauth/`) are copied
|
||||
into the container so agents can call OAuth-authenticated providers without re-authenticating. If you do need to
|
||||
authenticate from inside a sandbox:
|
||||
|
||||
* **xAI:** SuperGrok / X Premium+ subscribers
|
||||
- **Flow 2 (Device Grant)** works out of the box. Coyote prints a QR code you can scan with your phone camera.
|
||||
- **Flow 3 (Client Credentials)** works fine (no browser or callback needed).
|
||||
- **Flow 1 Mode A (loopback callback)** generally cannot complete because the loopback listener binds inside the
|
||||
sandbox's network namespace, unreachable from the host browser.
|
||||
- **Flow 1 Mode B (browser-paste)** works if you can open the authorize URL in a browser somewhere and paste back.
|
||||
|
||||
To bundle defaults for additional providers locally, edit your `models.yaml` (see [Local Models Override](#local-models-override))
|
||||
and add an `oauth:` block under the provider entry.
|
||||
Recommendation: authenticate on the host first and let the sandbox token copy carry you through.
|
||||
|
||||
## Providers That Support OAuth
|
||||
* Claude
|
||||
* Gemini
|
||||
* OpenAI (requires ChatGPT Plus or Pro subscription)
|
||||
* xAI (requires SuperGrok or X Premium+ subscription; configured via `type: openai-compatible`)
|
||||
* Any OpenAI-compatible provider via an inline `oauth:` block (see [OAuth for OpenAI-Compatible Providers](#oauth-for-openai-compatible-providers))
|
||||
|
||||
> Additional providers using Device Authorization Grant (RFC 8628) can be configured inline via
|
||||
> `flow: device_code` in the `oauth:` block. See [Device Authorization Grant](#device-authorization-grant-device-flow-rfc-8628).
|
||||
**Built-in providers:** (just set `auth: oauth`, no fields required):
|
||||
* **Claude:** See [Claude OAuth Note](#oauth-authentication)
|
||||
* **Gemini:** See [Gemini OAuth Note](#gemini-oauth-note)
|
||||
* **OpenAI:** See [OpenAI OAuth Note](#openai-oauth-note) (requires ChatGPT Plus or Pro subscription)
|
||||
* **xAI:** See [xAI OAuth Note](#xai-oauth-note) (requires SuperGrok or X Premium+ subscription; configured via `type: openai-compatible`, uses bundled defaults)
|
||||
|
||||
**Any OpenAI-compatible provider:** Configured inline via an `oauth:` block. Coyote supports all four common flows:
|
||||
Authorization Code + PKCE (loopback callback OR browser-paste), Device Authorization Grant (RFC 8628), and Client
|
||||
Credentials. See [OAuth for OpenAI-Compatible Providers](#oauth-for-openai-compatible-providers) for the field reference
|
||||
for each flow.
|
||||
|
||||
# Extra Settings
|
||||
Coyote also lets you customize some extra settings for interacting with APIs:
|
||||
|
||||
Reference in New Issue
Block a user