DurinDoor
Providers

Connecting accounts

Connect OAuth, device-code, imported-token, and cookie accounts, then verify routing.

A connection is one credential row for one provider. Claude Code, Codex, Gemini CLI, and GitHub use OAuth. ChatGPT Web and other webCookie registry entries use a browser session cookie; see Web cookie providers. The same provider can hold several connections so you can rotate quota or keep personal and team accounts apart.

Open Dashboard, then Providers, then the provider card. Connect starts the flow that provider supports.

Browser OAuth

Claude Code and Codex use an authorization-code flow with PKCE (details: Claude Code, OpenAI Codex). Open the provider's authorization URL and complete its consent flow. Claude Code shows a code on an Anthropic-hosted callback page for you to paste into DurinDoor. Codex uses its fixed loopback callback or a pasted callback URL. DurinDoor then exchanges the code for tokens. Gemini CLI and Antigravity use the same shape against Google's OAuth client. GitLab Duo uses PKCE against GITLAB_DUO_BASE_URL or GITLAB_BASE_URL, defaulting to https://gitlab.com.

Some providers pin a loopback callback port. If the dashboard is not on a loopback host, the OAuth modal may run the callback on the server instead of in the browser tab.

OrcaRouter uses an out-of-band code. Sign in through the link and paste the code shown on its consent page into the modal. It issues a durable API key instead of refreshable OAuth tokens. Operators can configure its auth and API origins through the environment variables.

Device code

GitHub, GitHub Enterprise Copilot, Qwen, Kiro, Amazon Q, Grok CLI, Qoder, Kimi Coding, Kilo Code, CodeBuddy CN, and Muse Code request a device code from /api/oauth/[provider]/device-code. You open the verification URL, enter the user code, and DurinDoor polls until the provider issues tokens.

For GitHub Enterprise Copilot, enter an HTTPS enterprise URL before starting device login. Requests use that enterprise's Copilot host. The model list is seeded rather than discovered live.

Amazon Q uses AWS Builder ID or IAM Identity Center device login. It does not offer Kiro's API-key, social-login, or token-import methods.

Muse Code uses Meta device login or a pasted META_API_KEY. Device login can mint an inference key before the first request. If the durable login token is revoked, sign in again.

Grok CLI also accepts a pasted ~/.grok/auth.json object or a bulk import of an array / accounts object. Imports stay serial so connection priorities keep the input order.

Cursor, Windsurf, Trae, and Devin CLI do not start a browser authorize step from the dashboard. You import a token the IDE or CLI already issued. Cursor has no public refresh endpoint; reconnect with a fresh import when the session dies. See Cursor.

iFlow can take a browser cookie instead of the PKCE path. POST /api/oauth/iflow/cookie requires a BXAuth= field.

Web-session cards use pasted session values rather than an OAuth login. You paste the cookie the dashboard's auth hint names, for example ChatGPT Web's __Secure-next-auth.session-token (or its chunked .0, .1 cookies, see ChatGPT Web). Sessions expire when the upstream account or browser session does. Treat these as personal or experimental unless your team has reviewed the risk.

Cookie-backed providers send whatever the upstream web app would send. Do not put them on a shared gateway that other people can drive unless you accept that those people can spend that browser session.

Kimi Web

Kimi Web uses access and refresh tokens from localStorage, rather than a browser cookie. Open the Kimi Web guide for the copy snippet, deployment origin, model limitations, and refresh behavior.

Token refresh

DurinDoor refreshes supported OAuth credentials before expiry, both on requests and in the background. Refresh is provider-specific and can fail. Imported Cursor, Windsurf, Trae, and Devin tokens have no public refresh flow. Qoder refresh can return 403 and require a new login.

An unrecoverable grant error, such as invalid_grant or a reused refresh token, requires reconnecting. Revoked Codex and OrcaRouter credentials can be quarantined as reauth_required; sign in again or replace the credential. Quota failures are separate from credential revocation.

Gemini CLI and Antigravity need operator-supplied OAuth client IDs and secrets. Set GEMINI_OAUTH_CLIENT_ID and GEMINI_OAUTH_CLIENT_SECRET, or ANTIGRAVITY_OAUTH_CLIENT_ID and ANTIGRAVITY_OAUTH_CLIENT_SECRET, then restart before connecting. Existing accounts may stop refreshing if you change their OAuth client.

Multiple accounts and priority

New connections are placed after the provider's existing accounts unless you choose a priority. Lower priority numbers run first. The default selector is fill-first: it takes the first eligible row in that order. Round-robin and Cache Affinity are settings strategies; round-robin still breaks ties with priority when lastUsedAt is missing, and Cache Affinity pins by conversation instead of rotating. See Smart routing.

You can pin a preferred connection id. Session-sticky round-robin remembers the last account for a session. Quota preflight can skip an account that is already exhausted and pick the next one.

Labels (name) matter once you have more than one row. OAuth connections without a name become Account N or a derived identity (GitHub prefers login, then email).

Account fallback

Account fallback stays inside the same provider and model. If connection A returns a fallback-worthy error (auth failure, rate limit, quota, some 5xx), DurinDoor marks that account unavailable and tries connection B. Combo fallback is separate: it moves to the next model in a combo chain after every account for the current member has failed. See Combos.

Client asks for openai/gpt-4.1
Connection A is rate limited
DurinDoor tries connection B for openai/gpt-4.1
If every OpenAI connection fails, a combo may try the next model

A connection can show last error, lock-until, model lock, refresh failure, or missing fields. Testing a connection from the dashboard stops after 15 seconds. Validation probes used while adding a credential stop after 10 seconds. Custom URLs still pass the outbound SSRF guard.

Hidden providers

Some registry entries set hidden: true because the generic add form cannot collect the fields they need. They do not appear in the Providers add selector, and POST /api/providers rejects them with Invalid provider. Existing connections and custom nodes are unchanged. Qwen Code is one example; use the dedicated flow the dashboard offers for that family. The full list is on Hidden and retired providers.

Dual-auth providers

A few OAuth-category providers also accept an API key (authModes includes apikey). CodeBuddy CN, xAI, and OrcaRouter offer both methods. Use Connect for OAuth, or add a key on the same provider page. The OrcaRouter modal shows both as tabs, and either path ends as the same sk-orca- key on one connection. The provider page also carries a capability-scoped model picker backed by GET https://api.orcarouter.ai/v1/models; when that call fails, the list falls back to a small verified seed and is labelled as offline rather than shown as live discovery.

Xiaomi MiMo

Xiaomi MiMo takes an sk- API key for the cloud API, or a Xiaomi account for the weekly MiMo Desktop quota. Add API Key keeps working as before. Connect opens a modal with two account paths:

  • Local credentials. When MiMo Desktop is installed on the DurinDoor host, GET /api/oauth/xiaomi-mimo/auto-import reads ~/.local/share/mimocode/auth.json and the Desktop cookie store. That route is local-only and needs dashboard auth. The import stores the key plus the account passToken. Desktop locks its cookie database while it runs, so close it first if the token is missing.
  • Browser login. No Desktop needed. Pick the account cluster (cn, sgp, ams, ru, or in). POST /api/oauth/xiaomi-mimo/login/start opens the real Xiaomi login page in a popup, served through DurinDoor on your own origin. The temporary browser login session forwards Xiaomi account pages through the gateway. Only a request with management access gets through, and the dashboard's own credentials are never forwarded. Once the page yields a passToken, the modal saves a session-only connection on that cluster. Non-CN clusters use MIMO_LOGIN_PROXY for the login egress when it is set; otherwise a local HTTP proxy on a common port is used if one answers. CN always connects directly.

With a passToken, mimo-v2.6-pro, mimo-v2.6-flash, and mimo-v2.6-pro-ultraspeed run on the cluster's account route (mimo-server-<cluster>.xiaomimimo.com) and bill the account's weekly quota. DurinDoor swaps the token for a service cookie through MiMo Desktop's two-step serviceLogin handshake and caches the cookie for 30 minutes per token and cluster. When the handshake fails, those models fall back to the cloud API with the key. Other models always use the cloud API. The connection test and the usage card use the same handshake.

Drag-reorder and the row up/down arrows on the provider page both persist the full connection id list through PUT /api/providers/reorder in one transaction. The body must be exactly that provider's ids, no extras and none missing, or the route returns 409 and writes nothing. Per-connection writes for a single move are not used: two independent PUT /api/providers/:id calls race under the server's priority-renumbering tiebreak, so a move could land in the wrong position or fail silently.

After connecting, send a short request with an ID from GET /v1/models and check Usage. A successful login proves token acquisition, not inference access. If login stalls, check callback reachability, an occupied callback port, the selected account region, and any required OAuth client variables.

On this page

Edit on GitHub