Anthropic
Set up credential brokering for Anthropic APIs so your workloads authenticate with Keycard-issued OIDC tokens instead of static API keys
Your application authenticates to Keycard with workload identity and exchanges its token for a Keycard-issued OIDC JWT scoped to Anthropic. The Anthropic SDK uses that JWT to perform Workload Identity Federation (WIF) and get a short-lived access token. No static API keys anywhere in the chain.
Prerequisites
Section titled “Prerequisites”- A Keycard zone
- An admin role in your Anthropic organization with permission to manage Workload Identity Federation (Organization Owner or Organization Admin)
- Organization ID (UUID) — found in small print on Settings → Organization
Keycard setup
Section titled “Keycard setup”Create an application, resource, and link them together.
-
Create the Anthropic resource
In Keycard Console, go to your zone and navigate to Resources.
- Click Add Resource
- Set the Resource Identifier to
https://api.anthropic.com - Select your Zone Provider as the credentials provider — this tells Keycard to issue OIDC tokens signed by the zone itself rather than brokering through an external OAuth flow
- Under Advanced Settings, set the Credential Lifetime to
1h
-
Create an application and link the resource
Navigate to Applications.
- Click Add Application
- Give it a name (e.g.
anthropic-workload) - Note the Application ID — not yet shown in the UI; open the application and copy the ID from the browser URL bar
Then open the application and go to Dependencies.
- Click Add Dependency
- Select the
https://api.anthropic.comresource
This authorizes the application to request tokens scoped to the Anthropic API.
-
Create application credentials (local development)
For local development with
keycard run, you need a client ID and secret. In production, applications authenticate with workload identity instead.Open your application and go to Application Credentials.
- Click Add Credential → Client ID & Secret
- Note the Client ID and Client Secret — the secret is only shown once
-
Note the zone’s OIDC issuer URL
Find your zone URL on the zone settings page in Keycard Console. Anthropic needs this as the issuer URL when you register the federation rule in the next section.
Your zone serves standard OIDC discovery at
https://<zone-id>.keycard.cloud/.well-known/openid-configuration— Anthropic fetches this automatically to discover the JWKS and verify token signatures.
Anthropic setup
Section titled “Anthropic setup”Register Keycard as a trusted issuer and create a federation rule for your workload.
-
Create a service account
In the Anthropic Platform Console, go to Settings → Service accounts → Create service account.
Give it a name (e.g.
keycard-workload). Note the service account ID (svac_...). -
Create a workspace and link the service account
The Default Workspace has no ID and can’t be used with WIF. Create a dedicated workspace for your Keycard workloads.
Go to Settings → Workspaces → Create workspace. Give it a name (e.g.
keycard-workloads).Note the workspace ID (
wrkspc_...) from the workspaces list. You’ll need it in the code examples below.Once created, select the workspace from the dropdown in the top navigation, then go to Manage → Service accounts → Add service account and add the service account from step 1. This links it to the workspace and determines which models and rate limits it can use.
-
Register Keycard as an issuer
In the org-level Workload Identity Federation settings, on the Issuers tab, click Create issuer.
Field Value Name A label, e.g. keycard-prodIssuer URL https://<zone-id>.keycard.cloud— must match theissclaim in the Keycard-issued JWT exactlyJWKS source discovery— Keycard zones serve.well-known/openid-configurationpublicly -
Create a federation rule
On the Rules tab, click New Rule.
Section Value Issuer Select the keycard-prodissuer from step 3Match → Subject prefix The Application ID from Keycard (the subclaim in the OIDC token)Target The service account from step 1 Workspaces Select the workspaces this rule can mint tokens for. The service account from step 1 must be a member of each selected workspace, otherwise token exchanges will fail Scope workspace:developer(default — grants the same access as an API key)Token lifetime 3600seconds (default) — adjust based on your security requirementsNote the rule ID (
fdrl_...). Your workload passes this in every token exchange request.
Use from code
Section titled “Use from code”Get a Keycard OIDC token and pass it to the Anthropic SDK for automatic WIF exchange.
Your application does two things at runtime:
- Gets a Keycard OIDC token scoped to
https://api.anthropic.comviaclient_credentialswith aresourceparameter - Passes that token to the Anthropic SDK, which handles the WIF exchange and refresh automatically
from keycardai.oauth import Client, BasicAuthfrom anthropic import Anthropic, WorkloadIdentityCredentials
# 1. Get a Keycard OIDC token scoped to Anthropic.with Client( "https://<zone-id>.keycard.cloud", auth=BasicAuth("<your-client-id>", "<your-client-secret>"),) as kc: token = kc.exchange_token( grant_type="client_credentials", resource="https://api.anthropic.com", )
# 2. Use it with the Anthropic SDK — WIF exchange happens automatically.client = Anthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=lambda: token.access_token, federation_rule_id="<fdrl_...>", organization_id="<anthropic-org-id>", service_account_id="<svac_...>", workspace_id="<wrkspc_...>", ),)
message = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello from a Keycard workload"}],)print(message.content[0].text)import Anthropic from "@anthropic-ai/sdk";import { oidcFederationProvider } from "@anthropic-ai/sdk/lib/credentials/oidc-federation";
/** Get a resource-scoped JWT via client_credentials grant. */async function keycardClientCredentials( tokenUrl: string, clientId: string, clientSecret: string, resource: string,): Promise<string> { const resp = await fetch(tokenUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Authorization: `Basic ${btoa(`${clientId}:${clientSecret}`)}`, }, body: new URLSearchParams({ grant_type: "client_credentials", resource }), }); if (!resp.ok) throw new Error(`client_credentials failed: ${await resp.text()}`); const data = (await resp.json()) as { access_token: string }; return data.access_token;}
// 1. Get a Keycard OIDC token scoped to Anthropic.const tokenUrl = "https://<zone-id>.keycard.cloud/oauth/2/token";const keycardJwt = await keycardClientCredentials( tokenUrl, "<your-client-id>", "<your-client-secret>", "https://api.anthropic.com",);
// 2. Use it with the Anthropic SDK — WIF exchange happens automatically.const client = new Anthropic({ credentials: oidcFederationProvider({ identityTokenProvider: () => keycardJwt, federationRuleId: "<fdrl_...>", organizationId: "<anthropic-org-id>", serviceAccountId: "<svac_...>", workspaceId: "<wrkspc_...>", baseURL: "https://api.anthropic.com", fetch, }),});
const message = await client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 1024, messages: [{ role: "user", content: "Hello from a Keycard workload" }],});console.log(message.content[0].text);package main
import ( "context" "fmt" "log"
"github.com/anthropics/anthropic-sdk-go" "github.com/anthropics/anthropic-sdk-go/option" "github.com/keycardai/credentials-go/oauth")
func main() { ctx := context.Background()
// 1. Get a Keycard OIDC token scoped to Anthropic. kc := oauth.NewClientCredentialsClient( "https://<zone-id>.keycard.cloud", oauth.WithCCBasicAuth("<your-client-id>", "<your-client-secret>"), )
keycardToken, err := kc.RequestToken(ctx, oauth.ClientCredentialsRequest{ Resource: "https://api.anthropic.com", }) if err != nil { log.Fatal(err) }
// 2. Use it with the Anthropic SDK — WIF exchange happens automatically. client := anthropic.NewClient( option.WithFederationTokenProvider( func(_ context.Context) (string, error) { return keycardToken.AccessToken, nil }, option.FederationOptions{ FederationRuleID: "<fdrl_...>", OrganizationID: "<anthropic-org-id>", ServiceAccountID: "<svac_...>", WorkspaceID: "<wrkspc_...>", }, ), )
message, err := client.Messages.New(ctx, anthropic.MessageNewParams{ Model: "claude-sonnet-4-6", MaxTokens: 1024, Messages: []anthropic.MessageParam{ anthropic.NewUserMessage(anthropic.NewTextBlock("Hello from a Keycard workload")), }, }) if err != nil { log.Fatal(err) } fmt.Println(message.Content[0].Text)}# 1. Get a Keycard OIDC token scoped to Anthropic.KC_TOKEN=$(curl -sS "https://<zone-id>.keycard.cloud/oauth/2/token" \ -u "<your-client-id>:<your-client-secret>" \ -d "grant_type=client_credentials" \ -d "resource=https://api.anthropic.com" \ | jq -r .access_token)
# 2. Exchange the Keycard token at Anthropic's WIF endpoint.ANTHROPIC_TOKEN=$(curl -sS https://api.anthropic.com/v1/oauth/token \ -H "content-type: application/json" \ -d "{ \"grant_type\": \"urn:ietf:params:oauth:grant-type:jwt-bearer\", \"assertion\": \"${KC_TOKEN}\", \"federation_rule_id\": \"<fdrl_...>\", \"organization_id\": \"<anthropic-org-id>\", \"service_account_id\": \"<svac_...>\", \"workspace_id\": \"<wrkspc_...>\" }" | jq -r .access_token)
# 3. Call the API.curl -sS https://api.anthropic.com/v1/messages \ -H "authorization: Bearer ${ANTHROPIC_TOKEN}" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello from a Keycard workload"}] }' | jq -r '.content[0].text'Verify
Section titled “Verify”Confirm the federation chain works end-to-end.
In Keycard Console — open Audit Log. You should see:
credentials:issue | OIDC token issued for https://api.anthropic.com |
In Anthropic Platform Console — go to Settings → Workload identity → Authentication events. You should see the exchange attempt with your zone’s issuer URL and the matched federation rule.
Related
Section titled “Related”- Providers (concepts) — the trust model behind identity and access federation
- Access policies — control which applications can access the Anthropic resource
- Run apps without static secrets — deploy with workload identity on Fly.io
- Anthropic WIF documentation — Anthropic’s setup reference