Configure Anthropic
Set up credential brokering so workloads authenticate to Claude API with Keycard-issued tokens instead of static API keys
Configure Workload Identity Federation (WIF) between Keycard and Anthropic. Your applications get Keycard OIDC tokens that Anthropic exchanges for short-lived API credentials — no static API keys anywhere.
Prerequisites
Section titled “Prerequisites”Before starting:
- A Keycard account
- Anthropic Organization Owner or Admin role with access to Workload Identity Federation settings
- Your Anthropic Organization ID (UUID) from Settings → Organization
Configure Keycard
Section titled “Configure Keycard”-
Create the Anthropic resource
In Keycard Console, navigate to Resources → Add Resource → Add Manually.
Field Value Identifier https://api.anthropic.comCredential Provider Zone Provider Credential Lifetime 1h(under Advanced options) -
Create an application
Navigate to Applications → Add Application.
Field Value Name e.g. anthropic-workloadIdentifier e.g. anthropic-workloadNote the Application Identifier. Keycard puts it in the
subandkeycard_app_idclaims of the tokens this Application requests for itself. It is not the ID in the browser URL. -
Link the resource
Open the application → Dependencies → Add dependency → select
https://api.anthropic.com. -
Create application credentials (local development only)
Open the application → Application Credentials → Add credential → Client ID & Secret.
Note the Client ID and Client Secret — the secret is shown once.
-
Note your Issuer URL
Copy your Issuer URL from Keycard Console under Settings → Connection. Anthropic needs this as the issuer URL.
Configure Anthropic
Section titled “Configure Anthropic”-
Create a service account
In Anthropic Platform Console, go to Settings → Service accounts → Create service account.
Field Value Name e.g. keycard-workloadNote the service account ID (
svac_...). -
Create a workspace
The Default Workspace has no ID and can’t be used with WIF.
Go to Settings → Workspaces → Create workspace.
Field Value Name e.g. keycard-workloadsNote the workspace ID (
wrkspc_...) from the workspaces list. -
Link the service account to the workspace
Select the workspace from the top navigation dropdown → Manage → Service accounts → Add service account → select the service account from step 1.
-
Register Keycard as an issuer
In Workload Identity Federation settings, on the Issuers tab → Create issuer.
Field Value Name e.g. keycard-prodIssuer URL your Keycard Issuer URL, e.g. <keycard-issuer-url>JWKS source discovery -
Create a federation rule
On the Rules tab → New Rule.
Field Value Issuer Select your Keycard issuer Match → Subject prefix Your Keycard Application Identifier (the subclaim). Anthropic treats the value as an exact match unless it ends in*.Target Your service account Workspaces Select the workspace from step 2 Scope workspace:developerToken lifetime 3600secondsNote the rule ID (
fdrl_...).
Use from code
Section titled “Use from code”Get a Keycard OIDC token and pass it to the Anthropic SDK for automatic WIF exchange.
from keycardai.oauth import Client, BasicAuthfrom anthropic import Anthropic, WorkloadIdentityCredentials
# 1. Get a Keycard OIDC token scoped to Anthropic.with Client( "<keycard-issuer-url>", # from Settings → Connection auth=BasicAuth("<your-client-id>", "<your-client-secret>"),) as kc: token = kc.client_credentials_grant( 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";import { ClientCredentialsClient } from "@keycardai/oauth";
// 1. Get a Keycard OIDC token scoped to Anthropic.const kc = new ClientCredentialsClient( "<keycard-issuer-url>", // from Settings → Connection { clientId: "<your-client-id>", clientSecret: "<your-client-secret>" },);const keycardToken = await kc.requestToken({ resource: "https://api.anthropic.com",});
// 2. Use it with the Anthropic SDK — WIF exchange happens automatically.const client = new Anthropic({ credentials: oidcFederationProvider({ identityTokenProvider: () => keycardToken.accessToken, 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/go-sdk/oauth")
func main() { ctx := context.Background()
// 1. Get a Keycard OIDC token scoped to Anthropic. kc := oauth.NewClientCredentialsClient( "<keycard-issuer-url>", // from Settings → Connection 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)}require "anthropic"require "keycardai/oauth"
# 1. Get a Keycard OIDC token scoped to Anthropic.kc = Keycardai::OAuth::ClientCredentialsClient.new( issuer: "<keycard-issuer-url>", # from Settings → Connection client_id: "<your-client-id>", client_secret: "<your-client-secret>",)keycard_token = kc.request_token(resource: "https://api.anthropic.com")
# 2. Use it with the Anthropic SDK. WIF exchange happens automatically.client = Anthropic::Client.new( credentials: Anthropic::Credentials::WorkloadIdentity.new( identity_token_provider: -> { keycard_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"}],)puts message.content[0].text# 1. Get a Keycard OIDC token scoped to Anthropic.# Replace <keycard-issuer-url> with your Issuer URL from Settings → Connection.KC_TOKEN=$(curl -sS "<keycard-issuer-url>/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”In Keycard Console — open Audit Log. Look for:
| Event | Description |
|---|---|
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 with your zone’s issuer URL and matched federation rule.
Troubleshooting
Section titled “Troubleshooting”Token exchange returns “service account not a member of workspace” The service account must be explicitly added to the workspace. Go to the workspace → Manage → Service accounts and add it.
“Invalid issuer” error
The issuer URL in Anthropic must match the Keycard zone URL exactly, including the protocol (https://) and no trailing slash.
Federation rule doesn’t match
Check that the Subject prefix in the rule matches your Keycard Application Identifier, not the ID in the browser URL. The sub claim in the Keycard-issued JWT contains the Identifier, and Anthropic treats the prefix as an exact, case-sensitive match unless it ends in *. A mismatch shows as match_subject_prefix in Anthropic’s authentication history.