Authenticate gateway requests with an external IdP
A request to a data plane authenticates with an API key issued for the project, or carries the caller's own provider credential on the passthrough path. From 0.7.0 an application can instead present a token minted by the organization's own identity provider, verified at the gateway against a set of registered issuers. That token path is a preview: it works end to end, budgets and rate limits do not yet apply to it, and a worked example for one provider is forthcoming.
The token path shipped in two steps. 0.6.0 stores gateway auth policies and verifies a token sent beside an API key. 0.7.0 serves a call made with a token alone, provided every data plane serving the project is on 0.7.0 or later. Budgets and rate limits are recorded per API key and do not yet apply to a caller admitted by a token, so cap that usage at the identity provider until they do. A worked configuration for one real provider, with a real token shape, is forthcoming and is published when the customer-side claim decisions it depends on are settled. Until then, the management API reference linked below is the complete field list.
What authenticates a request at 0.7.0
A request reaches a project through a hostname the data plane declares, and every project the data plane carries a project label for is served on every one of its hostnames. Nothing about the hostname selects the project: the credential does. Three credentials exist.
- An API key issued for the project, on the
Authorizationheader. The key names its project, so the request needs no other identifier, and a key from a project the data plane does not serve is refused with403. Keys are issued by developers in the Console and governed organization-wide by operators; issuing, auditing, and revoking them is covered in Onboard developers and issue keys and Issue a project API key. - The caller's own provider credential, on the passthrough path: the request carries a credential for the upstream provider itself (an Anthropic subscription token, for example), and the gateway forwards rather than substitutes it. The behavior and its routing consequences are covered in Use your own provider credentials and, for the operator-side setup, Enable passthrough for Claude Code.
- A token from the organization's own identity provider, in preview from 0.7.0, sent in the standard
Authorization: Bearerheader together with a header naming the project. The rest of this page describes it.
Single sign-on is a different surface: SSO authenticates people into the consoles, and its identities can then issue API keys. It never authenticates a request to a data plane.
How a token call works
An API key names exactly one project. A token names an application, and an application may be entitled to several projects, so a token call carries one extra header naming the project the request runs as. The data plane takes that header as a candidate, never as a decision: the configuration the project publishes decides whether this token may use it.
Two configurations admit a token, and they are shaped for different situations.
| Configuration | Where it is written | Who it suits |
|---|---|---|
| A gateway auth policy, with identity mapping rules, on a gateway | The GatewayAuthPolicyService of the management API, one policy per trusted issuer, up to four enabled per gateway | An operator registering a set of issuers once for every project served on that gateway, with per-issuer key sources, audiences, and required scopes |
| A project OIDC contract, on one project | UpsertProjectOIDCConfig, one contract per project | A project team naming the one issuer, audience, scope, and optional entitlement its own tokens carry |
On either path the data plane verifies the token's signature against the issuer's key set, its expiry, and its audience, then checks what the configuration requires of its claims, and only then resolves the project. A token from an issuer the gateway does not register is refused rather than tried against another issuer's keys.
The set of trusted issuers
One gateway carries several trusted identity providers, each with its own key source and its own accepted audiences. Every policy names its issuer, and one gateway takes at most one enabled policy per issuer, so the gateway selects the verifier from the issuer the token names and nothing else. The policy fields:
| Field | What it does |
|---|---|
issuer | The value the token's iss claim must carry. Required, and unique among the gateway's enabled policies. |
jwks_uri or jwks_inline | Where the signing keys come from. A jwks_uri must name the provider by a public hostname, carry no user name or password, and not resolve to an internal address. An inline key set holds at most 32 keys and 16 KiB; serve a larger set from a jwks_uri. |
audiences | The audiences a token may carry, up to eight. |
required_scopes | Scopes the token must carry, every one of them, in its scope or scp claim, matched case-sensitively. A token missing one gets 403. Updates replace the list, so omitting it removes the requirement. |
extraction_header | The header the token is read from when it is not sent as a Bearer value. Defaults to x-tars-idp-token. Authorization is refused, as is any header the gateway cannot remove after verification, such as x-api-key or cookie. |
environment_claim | The claim carrying the environment the token was issued for, compared against the environment the gateway declares. An enabled policy naming one is refused while the gateway declares no environment, and the gateway refuses a token whose claim does not name its environment. |
Identity mapping rules, under the same service, say which claim identifies the application and whether an application unseen before is given an identity on its first call (jit, bounded by a per-project cap) or must be pre-registered. A jit rule names the project the application is created in, and that project has to be attached to the policy's gateway before the rule is written.
Which credentials a project accepts
Each project's membership of a gateway records accepted_credentials, written with UpdateProjectGatewayProject. A new attachment starts at ACCEPTED_CREDENTIALS_API_KEY, which is what every project did before the field existed. ACCEPTED_CREDENTIALS_BOTH admits either credential, and ACCEPTED_CREDENTIALS_TOKEN makes the data plane refuse API keys for that project. Both are refused while the project holds no enabled policy on that gateway, and the project's last enabled policy cannot be disabled or deleted while the membership declares either one. A data plane older than 0.7.0 authenticates API keys alone, whatever the membership declares.
What an application sends
The same base URL, endpoints, and model names as an API-key caller, plus the project header:
curl https://gw.example.com/v1/chat/completions \
-H "Authorization: Bearer $IDP_TOKEN" \
-H "x-tars-project: payments-assist" \
-d '{"model": "claude-sonnet-4-5", "messages": [{"role": "user", "content": "Hello"}]}'
The header is x-tars-project on the gateway auth policy path and X-TARS-PROJECT-ID on the project OIDC contract path; both take the project's name. The data plane removes either header, and the token, before anything is forwarded to a model provider or an MCP server. A request that carries both a token and an x-api-key header is refused as a credentials conflict rather than guessed at.
On the gateway auth policy path, the first call from an application creates the application's own API key when a mapping rule allows it, and later calls reuse it. Request logs and usage are attributed to that key, which is how a token call appears in the Console today.
What a refusal means
Each refusal carries its own reason in the error body, and the data plane records the reason in its metrics. The ones a caller is most likely to meet:
| Status | Reason | Meaning |
|---|---|---|
400 | missing_project or project_header_required | The request carries a token and no project header. |
400 | credentials_conflict | The request carries both a Bearer token and an x-api-key header. |
401 | unregistered_issuer | The token's iss names an issuer this gateway does not register. |
401 | bad_signature, expired, wrong_audience | The token did not verify against the issuer's keys, has expired, or was not issued for an audience the gateway accepts. |
403 | project_not_served | The gateway does not serve the project the header names. |
403 | token_not_accepted | The project named accepts API keys only on this gateway. |
403 | issuer_not_accepted | The issuer is registered on the gateway, but the project named does not accept tokens from it. |
403 | missing_scope or insufficient_scope | The token lacks a scope the policy or contract requires. |
403 | environment_mismatch | The token's environment claim does not name the gateway's environment. |
503 | keys_unavailable | The gateway does not hold the issuer's keys right now. Retry. |
A 403 on the project contract path that names nothing is deliberate: a project a token has no relationship with confirms neither that the project exists nor whose token this is. Every refusal carries a reference_id to quote to support.
What is refused when a policy is written
The management API refuses a gateway auth policy write, with the reason in the response, when:
jwks_uricarries a user name or password, writes an IPv4 address as anything other than a dotted quad, or names or resolves to a carrier-grade NAT, multicast, reserved, or other internal address;jwks_inlineholds more than 32 keys or more than 16 KiB;extraction_headeris one the gateway cannot remove after verifying the token, which rules out the identity headers the gateway sets and credential headers such asx-api-key,cookie, andproxy-authorization;- an identity mapping rule names a
jit_project_idthat is not a project of the policy's organization or is not attached to the policy's gateway, setsjit_principal_capon apre_registeredrule, or sets aprincipal_typeother thanexternalon ajitrule; - the gateway already carries four enabled policies, or another enabled policy names the same issuer.
An unrelated update does not recheck an unchanged stored value, so an existing policy keeps working. An enabled policy that still reads its token from a header the gateway cannot strip produces the warning gateway auth policy reads its token from a header the gateway cannot keep for verification and remove before the upstream in the data plane and management plane logs; move each policy it names to x-tars-idp-token or another dedicated header.
The project OIDC contract has one refusal of its own: the management plane reads the issuer's discovery document when the contract is written, and refuses an issuer on a loopback, link-local, private, carrier-grade NAT, unique-local, or otherwise non-public address. An identity provider inside the organization's own network, such as a Keycloak install, needs OIDC_DISCOVERY_ALLOW_PRIVATE_ADDRESSES=true on the management plane before its contract is written.
Upgrading a data plane that carries a policy
Upgrade the management plane before the data planes. From 0.7.0 the data plane removes x-tars-idp-token and every other identity header it sets from the requests a caller sends, and a data plane upgraded ahead of its management plane removes that header before the gateway can verify it on a gateway with an existing policy. The data plane upgrade guide covers the order; the headers the data plane strips are listed under request normalization.
Where to go next
Management API reference
The complete field list for gateway auth policies, identity mapping rules, and project OIDC contracts, generated from the 0.7.0 specifications.
Serve a project from a data plane
The project label and the hostnames these credentials authenticate against.
Onboard developers and issue keys
The credential most requests authenticate with, from issuance to organization-wide revocation.
Configure SSO
External identity for console sign-in, which is a separate surface from a request to a data plane.