Okta can act as a workload identity provider by issuing OIDC access tokens to a service application through the OAuth 2.0 client_credentials grant. Your workload authenticates to Okta (typically with private_key_jwt, so no shared secret is stored), receives a signed JSON Web Token (JWT), and exchanges that JWT with Anthropic for a short-lived access token.
The Okta authorization server's issuer URL takes the form https://<your-domain>.okta.com/oauth2/<auth-server-id>. If you use the built-in default server, the path is /oauth2/default.
You must use an Okta custom authorization server (including the default one). Tokens issued directly by the Okta org authorization server (the /oauth2/v1/token endpoint with no authorization server ID in the path) cannot be validated by external parties because Okta does not publish signing keys for them.
There are many ways to configure and authenticate to Okta that are outside the scope of this documentation. Ensure that your configuration and authentication mechanisms follow your company's guidance and security practices.
/v1/token endpoint and reach api.anthropic.com.At a high level you need to:
The exact navigation depends on your Okta org configuration and admin console version. The numbered steps below walk through one common path:
private_key_jwt) and register your workload's public JWK. Alternatively, use a client secret if your environment can store one securely. For the following example you may need to disable the DPoP requirement on the application; ensure that your production setup adheres to your organization's security requirements.https://api.anthropic.com so issued access tokens carry that aud claim. Anthropic validates aud against this fixed value.anthropic.access). Okta rejects client_credentials requests that do not include a granted scope.For a service app using client_credentials, Okta sets the sub claim of the issued access token to the application's Client ID, and iss to the authorization server's issuer URL.
Follow the setup walkthrough to register a federation issuer, create an Anthropic service account, and create a federation rule in the Claude Console. Use these Okta-specific values.
Federation issuer: Use your Okta custom authorization server URL and discovery mode. Anthropic reads Okta's .well-known/openid-configuration discovery document and fetches the JWKS from the jwks_uri it advertises.
{
"name": "okta-prod",
"issuer_url": "https://acme.okta.com/oauth2/aus1a2b3c4d5e6f7g8h9",
"jwks_source": "discovery"
}Federation rule: Match on the Okta sub claim, which is the service app's Client ID. If you defined custom claims in Okta, you can match on those instead with the claims map or a CEL condition.
{
"name": "okta-pipeline",
"issuer_id": "fdis_...",
"match": {
"subject_prefix": "0oa1b2c3d4e5f6g7h8i9",
"audience": "https://api.anthropic.com"
},
"target": { "type": "service_account", "service_account_id": "svac_..." },
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}Unlike platform-native providers (AWS, Google Cloud, Kubernetes), which make a token available inside the workload's runtime (through a projected file or local metadata endpoint), Okta does not. Your workload must call Okta's token endpoint to obtain a JWT, then pass that JWT to the Anthropic SDK as the identity token.
Each SDK tab shows the callable pattern: the Anthropic SDK calls your identity-token provider again whenever the Anthropic access token approaches expiry, so your Okta fetcher should return a fresh token on each call rather than caching one indefinitely. The ant CLI re-reads ANTHROPIC_IDENTITY_TOKEN_FILE on each exchange, so refresh that file on a timer for long-running shells.
A successful exchange returns an access_token beginning with sk-ant-oat01- and an expires_in value in seconds. On 400 invalid_grant, see Troubleshoot a failed exchange; the most common Okta-side cause is an issuer_url mismatch (it must include the /oauth2/<auth-server-id> path; the Okta org authorization server is not usable).
Multiple service apps under the same Okta authorization server share the same
issuer. A rule that omits subject_prefix matches every service app on that
server, so any team that can register one could obtain a federated Anthropic
token.
Lock the rule's match block to the narrowest scope that fits your use case:
subject_prefix to the service app's full Client ID with no trailing *.audience value you configured on the authorization server so tokens minted for a different audience are rejected.claims map or a CEL condition.Was this page helpful?
import os
import httpx
import anthropic
from anthropic import WorkloadIdentityCredentials
def fetch_okta_token() -> str:
response = httpx.post(
f"{os.environ['OKTA_ISSUER']}/v1/token",
data={
"grant_type": "client_credentials",
"scope": "anthropic.access",
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
# Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
"client_assertion": build_signed_client_assertion(),
},
)
response.raise_for_status()
return response.json()["access_token"]
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=fetch_okta_token,
federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
),
)
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(message.content[0].text)