Claude Apps Gateway: SSO, Group Policy, and Spend Caps
Organisations that run Claude through Amazon Bedrock, Google Cloud, or Microsoft Foundry, often for data-residency reasons, used to face a choice: give every developer cloud credentials, or build their own proxy and keep it compatible with every Claude Code release. The Claude apps gateway is Anthropic's answer. It is a self-hosted server built into the claude binary itself (claude gateway --config gateway.yaml), tested with each Claude Code release, that signs developers in with your corporate identity provider and forwards their requests to your provider.
This guide follows the official gateway documentation: what the gateway gives you over a generic proxy, a minimal gateway.yaml, how developers sign in, per-group model access and managed settings, spend caps, failover, and the features that do not work through it. Every config example here is from Anthropic's docs; check the current configuration reference before production, because the gateway is still gaining options with each release.
If you already run an LLM gateway that works, keep it; Claude Code LLM Gateway covers connecting Claude Code to one. For managed settings without a gateway, see Claude Code Enterprise Setup.
What the gateway does that a generic proxy does not
Claude Code clients send ordinary Anthropic Messages requests to the gateway, carrying a short-lived gateway token instead of an API key. The gateway checks the developer's policy and spend, translates the model ID for the upstream (for example claude-opus-4-8 to us.anthropic.claude-opus-4-8 on Bedrock), and forwards the request with the organisation's credential. It provides:
- Credentials: the upstream key or cloud role lives only on the gateway. Developers sign in with corporate SSO. Deprovision someone in the IdP and their access ends within the session lifetime, one hour by default.
- Access control: IdP groups map to model allowlists, enforced on the server, and to managed settings that the CLI applies at the top of its settings hierarchy.
- Settings delivery: the gateway serves managed settings to signed-in clients, taking the place of server-managed settings from the claude.ai console.
- Telemetry: OTLP metrics with tokens, model, user identity, and latency go to your collectors.
- Upstream routing: Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, Microsoft Foundry, or the Anthropic API, with failover between them.
The main difference from a third-party gateway is compatibility. Because it ships with Claude Code, it forwards the anthropic-beta values and request fields each release sends, and moves them into the request body where Bedrock expects them, so you do not maintain an allowlist.
Quick reference
- The gateway's data plane sends nothing to Anthropic unless the Anthropic API is one of your upstreams. Telemetry, audit logs, settings, and identity stay with you.
- The gateway does not log or store prompt or completion content.
- If you do not need your own cloud provider, Anthropic suggests Claude Enterprise may fit better, since it adds features such as SCIM provisioning and Claude Code on the web and mobile.
Remember this
It is a release-matched proxy with SSO, group policy, and spend caps, aimed at organisations that must route through their own cloud.
A minimal gateway.yaml
Prerequisites: Claude Code v2.1.195 or later on the gateway server and on developer machines, an OIDC identity provider (SAML and LDAP are not supported), PostgreSQL 11 or later, an upstream credential, HTTPS, and a hostname that resolves only to private addresses. The server runs on the Linux binary; macOS works for local development.
The minimal file has five sections. Secrets use ${ENV_VAR} expansion so the file can live in version control:
1listen:2 host: 0.0.0.03 port: 80804 public_url: https://claude-gateway.internal.example.com5 6oidc:7 issuer: https://login.example.com8 client_id: 0oa1example29 client_secret: ${OIDC_CLIENT_SECRET}10 allowed_email_domains: [example.com]11 12session:13 jwt_secret: ${GATEWAY_JWT_SECRET} # openssl rand -base64 3214 ttl_hours: 1 # also bounds revocation after deprovisioning15 16store:17 postgres_url: ${GATEWAY_POSTGRES_URL}18 19upstreams:20 - provider: bedrock21 region: us-east-122 auth: {} # AWS default credential chain23 24auto_include_builtin_models: trueRegister an OIDC web application in your IdP with the redirect URI https://<public_url host>/oauth/callback. Run the gateway with claude gateway --config gateway.yaml, or in a container image you build around the claude binary. Boot is fail-closed: if the config, Postgres, OIDC discovery, or upstream client setup fails, the gateway exits with an error naming the problem rather than serving in a degraded state.
Quick reference
- The Bedrock principal needs
bedrock:InvokeModelandbedrock:InvokeModelWithResponseStreamon both theinference-profile/us.anthropic.*andfoundation-model/anthropic.*ARNs. Use IRSA, an ECS task role, or an instance profile rather than static keys. - A successful boot does not prove inference works: Bedrock and Google Cloud credentials resolve on the first request.
- Expect a warning that
access_control.allow_cidrsis empty; set it to your client ranges before production. - The private-address rule exists because a trusted gateway can push settings that run commands on developer machines. Put it behind an internal load balancer or VPN.
Remember this
Five sections get a working sign-in loop; boot fails loudly on config, database, or IdP errors.
How developers sign in
Developers do not need a claude.ai account, an API key, or a subscription. Push three keys in the managed settings file on each machine:
1{2 "forceLoginMethod": "gateway",3 "forceLoginGatewayUrl": "https://claude-gateway.internal.example.com",4 "parentSettingsBehavior": "merge"5}The developer runs /login, presses Enter on the Cloud gateway screen, and completes an OAuth device flow in the browser. On first connect, Claude Code shows the first 16 hex characters of the gateway's TLS certificate fingerprint and pins it per hostname, so publish the expected fingerprint next to the URL. Sessions refresh silently before ttl_hours expires; after IdP deprovisioning the refresh fails and the developer is asked to log in again.
Before sharing the gateway, check it with three requests: GET /.well-known/oauth-authorization-server (boot succeeded), POST /oauth/device_authorization (Postgres reachable and writable), and opening the returned verification_uri_complete in a browser (the IdP redirect works). The first failing check tells you where to look.
Quick reference
- The device flow works over SSH: run
/loginon a remote box and open the link on your laptop. - There is no service-token flow. A CI job with nobody to approve a browser sign-in cannot use the gateway; configure CI against the provider directly.
- Certificate rotation makes every developer see the trust prompt again, so treat it as a planned event and republish the fingerprint.
- If an HTTPS proxy sits between laptops and the gateway, add the gateway host to
NO_PROXY, or the pin check is skipped and/loginmay reject the proxy's public address.
Remember this
Managed settings point /login at the gateway; the device flow and a pinned certificate do the rest.
Per-group models and managed settings
The managed.policies list maps IdP groups to the settings each developer gets. Policies are evaluated in order, the first match is selected, and it is merged onto the match: {} catch-all:
1managed:2 policies:3 - match: { groups: [eng-contractors] }4 cli:5 availableModels: [claude-sonnet-4-6]6 permissions: { deny: ["WebFetch", "WebSearch"] }7 - match: {}8 cli:9 availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]10 enforceAvailableModels: trueThe gateway enforces model access itself: a request for a model the policy does not grant returns 400, and the /model picker only shows the policy's allowlist. Everything under cli is applied by the client as managed settings, at startup and on each hourly poll, and locked keys cannot be overridden locally.
Group matching reads the token's groups claim and is case-sensitive. Microsoft Entra ID puts app roles under roles, so set oidc.groups_claim: roles there.
Quick reference
- An authenticated user who matches no policy gets the gateway defaults: every model in the catalog and no managed settings. Always keep a
match: {}catch-all. - The login keys (
forceLoginMethod,forceLoginGatewayUrl) belong in the file you push to machines, not inmanaged.policies[].cli, which only reaches clients already connected. - Signed-in sessions exit at startup after about 10 seconds if the gateway is unreachable, rather than running without their settings.
Remember this
One ordered list of group policies controls models and settings; keep the catch-all last and check the group claim name.
Spend caps and upstream failover
With an admin: block in gateway.yaml, the gateway serves an admin API at /v1/organizations/spend_limits. Caps are set through the API, not the YAML. Amounts are whole-number strings of US cents:
1# $500 per month default for every developer2curl -sS https://claude-gateway.internal.example.com/v1/organizations/spend_limits \3 -H "x-api-key: $GATEWAY_ADMIN_WRITE_KEY" \4 -H "Content-Type: application/json" \5 -d '{"scope": {"type": "organization"}, "amount": "50000", "period": "monthly"}'Scopes are user (by OIDC sub), rbac_group, or organization, and periods are daily, weekly, or monthly. A group or organisation cap is a per-person default, not a shared pool. A developer over any cap gets 429 with error.type: billing_error and a message naming the period and reset time. Claude Code warns them at 75% and 95%.
For capacity, list several upstreams and map each model per upstream. The gateway tries them in order and fails over on 5xx, 429, 401, 403, 404, timeouts, and 501, but not on other 4xx errors:
1upstreams:2 - name: bedrock-east3 provider: bedrock4 region: us-east-15 auth: {}6 - name: bedrock-west7 provider: bedrock8 region: us-west-29 auth: {}10models:11 - id: claude-opus-4-812 upstream_model:13 bedrock-east: us.anthropic.claude-opus-4-814 bedrock-west: us.anthropic.claude-opus-4-8Quick reference
- Spend is a USD estimate from list prices (or your
pricing.overrides), a circuit breaker rather than an invoice. Model IDs the meter cannot place are charged at $5/$25 per million input/output tokens so they are never free. - If Postgres is unreachable, enforcement fails open by default;
enforcement.fail_closed_on_error: trueblocks instead, trading an outage for no unmetered spend. - Aborted streams are still billed with a floor estimate, so cancelling requests early does not evade a cap.
- The
principal_emailstable holds email, name, and groups. Delete a person's row directly for offboarding or a data subject access request.
Remember this
Caps are per person, enforced live, and estimated; failover is an ordered list with explicit per-upstream model IDs.
What does not work through the gateway
The gateway trades some first-party features for control. Because Claude Code cannot see which upstream will serve a request, it turns off anything that depends on a specific provider: server-side web search, the one-hour cache TTL, and first-party optimisations such as global cache scope. Remote Control and the design-sync commands need claude.ai, which gateway sessions do not contact. Auto mode works but follows the third-party provider rules for which models are eligible.
Operationally, there is no admin UI and no Helm chart. Configuration is the YAML file under version control, and the gateway runs as a stateless deployment behind your load balancer, with Postgres as its only state.
| Feature | Status | Why or alternative |
|---|---|---|
| Web search tool | Not available | The client cannot verify the upstream supports it, so WebSearch is disabled |
| Remote Control | Not available | Needs the Anthropic API |
| 1-hour prompt cache TTL | Not available | Not every upstream supports it; caching uses the 5-minute TTL |
| CI and unattended runs | No service tokens | Configure CI against the provider directly |
| SAML, LDAP | Not supported | OIDC only; front with an OIDC bridge |
| Multiple OIDC issuers | Not supported | Run one gateway per issuer |
| OTLP over gRPC | Not supported | OTLP over HTTP only |
| Admin UI, Helm chart | Not available | Edit the YAML and redeploy; run as a standard Deployment |
Quick reference
- Check the current availability table before rollout; entries change as releases add support.
- Claude Desktop can route its Cowork and Code tabs (and optionally Chat) through the gateway once a policy opts in with a
desktopkey. - Developers who set a provider variable such as
CLAUDE_CODE_USE_BEDROCKbypass the gateway sign-in requirement; block that with managed settings and network rules if it matters.
Remember this
Expect no web search, no Remote Control, a 5-minute cache TTL, and no CI support; plan alternatives before rollout.
Hands-on: a local gateway with your own IdP
Use a test IdP tenant (Keycloak or Dex in a container works) and a test Bedrock or Anthropic API credential.
- Register an OIDC client with redirect URI
http://localhost:8080/oauth/callback, start Postgres, and write the minimalgateway.yamlwithpublic_url: http://localhost:8080. - Run
claude gateway --config gateway.yamland watch stderr untilclaude gateway listening on. - Run the three checks: the discovery document, a device authorization, and the browser sign-in.
- On the same machine, add
forceLoginMethod: "gateway"andforceLoginGatewayUrlto~/.claude/settings.json(user-settings sign-in needs v2.1.295 or later), run/login, and sign in. Run/modeland confirm the picker matches the policy. - Add an
eng-contractorspolicy limited to one model, put your test user in that group, sign in again, and request a different model.
Pass criterion: sign-in completes through your IdP, /model shows only the policy's models, and the request for a non-granted model is refused with 400.
Quick reference
- Loopback (
localhost) is the only host allowed over plain HTTP; anything else needs HTTPS and a private address. - If boot exits, the last stderr line names the cause: Postgres unreachable, missing DDL rights, bad OIDC discovery, or a config field path.
- Read the gateway's audit log for sign-in rejections, such as
email domain not allowed.
Remember this
Prove sign-in, the group policy, and a refused model locally before a production deployment.
Key takeaway
The Claude apps gateway is Anthropic's self-hosted gateway for organisations that route Claude through their own cloud. It runs from the claude binary with a gateway.yaml, signs developers in through OIDC with a device flow and a pinned certificate, maps IdP groups to model allowlists and managed settings, enforces per-person spend caps through an admin API, and fails over across upstreams in order. Because it ships with Claude Code, it stays compatible with new request fields without an allowlist.
It is not a fit for everything: there are no service tokens for CI, OIDC is the only identity option, and web search, Remote Control, and the one-hour cache TTL are unavailable. For a gateway you already run, see Claude Code LLM Gateway; for the sandbox and permission rules that apply to the commands Claude runs, see Claude Code Permissions.
Practice (60 min): run the local exercise above, then write the managed.policies list for your own organisation's groups, with a catch-all last.
References
- Claude Code docs: Claude apps gateway — checked Oct 17, 2026
- Claude Code docs: Claude apps gateway configuration reference — checked Oct 17, 2026
- Claude Code docs: Claude apps gateway spend limits — checked Oct 17, 2026
Shoaib Hossain
Author, Core Concept LearningRelated Articles
Explore this topic