okta changedmanage-claude/wif-providers/okta
Nearest release: v2.1.309, published 3 hours before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Recorded here
Lines+101added
Lines−101removed
From line
113
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits14to this page, all time
The whole hunk
from line 113, old and new numbered
/
from line 113
113113 }'
114114 ```
115115
116 ```bash CLI
117 # 1. Request an access token from Okta and write it to a temp file.
118 export ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
119 curl -sS "$OKTA_ISSUER/v1/token" \
120 -d grant_type=client_credentials \
121 -d scope=anthropic.access \
122 -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
123 --data-urlencode client_assertion="$SIGNED_CLIENT_ASSERTION" \
124 | jq -r .access_token > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
125
126 # 2. Call the Claude API. The CLI reads ANTHROPIC_FEDERATION_RULE_ID,
127 # ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and
128 # ANTHROPIC_IDENTITY_TOKEN_FILE and performs the exchange.
129 ant messages create \
130 --model claude-opus-5-5 \
131 --max-tokens 1024 \
132 --message '{role: user, content: "Hello, Claude"}'
133 ```
134
116135 ```python Python
117136 import os
118137 import httpx2
from line 216
197216 }
198217 ```
199218
219 ```csharp C#
220 using Anthropic.Credentials;
221 // ...
222
223 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
224 {
225 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
226 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
227 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
228 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
229 IdentityTokenProvider = new OktaTokenProvider(),
230 });
231 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
232
233 var message = await client.Messages.Create(new()
234 {
235 Model = Model.ClaudeOpus5_5,
236 MaxTokens = 1024,
237 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
238 });
239 foreach (var block in message.Content)
240 {
241 if (block.Value is TextBlock textBlock)
242 {
243 Console.WriteLine(textBlock.Text);
244 }
245 }
246
247 class OktaTokenProvider : IIdentityTokenProvider
248 {
249 private static readonly HttpClient Http = new();
250
251 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
252 {
253 var form = new FormUrlEncodedContent(new Dictionary<string, string>
254 {
255 ["grant_type"] = "client_credentials",
256 ["scope"] = "anthropic.access",
257 ["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
258 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
259 ["client_assertion"] = BuildSignedClientAssertion(),
260 });
261 var response = await Http.PostAsync(
262 $"{Environment.GetEnvironmentVariable("OKTA_ISSUER")}/v1/token", form, ct);
263 response.EnsureSuccessStatusCode();
264 using var json = await JsonDocument.ParseAsync(
265 await response.Content.ReadAsStreamAsync(ct), default, ct);
266 return json.RootElement.GetProperty("access_token").GetString()!;
267 }
268 }
269 ```
270
200271 ```go Go
201272 package main
202273
from line 359
288359
289360 void main() throws Exception {
290361 var form = Map.of(
291 "grant_type", "client_credentials",
292 "scope", "anthropic.access",
293 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
294 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
295 "client_assertion", buildSignedClientAssertion())
296 .entrySet().stream()
297 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
298 .collect(Collectors.joining("&"));
362 "grant_type", "client_credentials",
363 "scope", "anthropic.access",
364 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
365 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
366 "client_assertion", buildSignedClientAssertion())
367 .entrySet().stream()
368 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
369 .collect(Collectors.joining("&"));
299370 var request = HttpRequest.newBuilder(URI.create(System.getenv("OKTA_ISSUER") + "/v1/token"))
300 .header("content-type", "application/x-www-form-urlencoded")
301 .POST(HttpRequest.BodyPublishers.ofString(form))
302 .build();
371 .header("content-type", "application/x-www-form-urlencoded")
372 .POST(HttpRequest.BodyPublishers.ofString(form))
373 .build();
303374
304375 // The Java SDK takes the token itself or a file path, not a function; with
305376 // source("value"), path(...) holds the token. The SDK sends that same token
from line 382
311382 String oktaToken = new ObjectMapper().readTree(response.body()).get("access_token").asText();
312383
313384 AnthropicClient client = AnthropicOkHttpClient.builder()
314 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
315 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
316 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
317 .authentication(AuthenticationConfig.builder()
318 .type(AuthenticationType.OIDC_FEDERATION)
319 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
320 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
321 .identityToken(IdentityTokenConfig.builder()
322 .source("value")
323 .path(oktaToken)
324 .build())
325 .build())
326 .build()))
327 .build();
385 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
386 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
387 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
388 .authentication(AuthenticationConfig.builder()
389 .type(AuthenticationType.OIDC_FEDERATION)
390 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
391 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
392 .identityToken(IdentityTokenConfig.builder()
393 .source("value")
394 .path(oktaToken)
395 .build())
396 .build())
397 .build()))
398 .build();
328399
329400 var message = client.messages().create(MessageCreateParams.builder()
330 .model(Model.CLAUDE_OPUS_5_5)
331 .maxTokens(1024)
332 .addUserMessage("Hello, Claude")
333 .build());
401 .model(Model.CLAUDE_OPUS_5_5)
402 .maxTokens(1024)
403 .addUserMessage("Hello, Claude")
404 .build());
334405
335406 IO.println(message.content());
336407 }
337408 ```
338409
339 ```csharp C#
340 using Anthropic.Credentials;
341 // ...
342
343 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
344 {
345 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
346 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
347 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
348 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
349 IdentityTokenProvider = new OktaTokenProvider(),
350 });
351 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
352
353 var message = await client.Messages.Create(new()
354 {
355 Model = Model.ClaudeOpus5_5,
356 MaxTokens = 1024,
357 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
358 });
359 foreach (var block in message.Content)
360 {
361 if (block.Value is TextBlock textBlock)
362 {
363 Console.WriteLine(textBlock.Text);
364 }
365 }
366
367 class OktaTokenProvider : IIdentityTokenProvider
368 {
369 private static readonly HttpClient Http = new();
370
371 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
372 {
373 var form = new FormUrlEncodedContent(new Dictionary<string, string>
374 {
375 ["grant_type"] = "client_credentials",
376 ["scope"] = "anthropic.access",
377 ["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
378 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
379 ["client_assertion"] = BuildSignedClientAssertion(),
380 });
381 var response = await Http.PostAsync(
382 $"{Environment.GetEnvironmentVariable("OKTA_ISSUER")}/v1/token", form, ct);
383 response.EnsureSuccessStatusCode();
384 using var json = await JsonDocument.ParseAsync(
385 await response.Content.ReadAsStreamAsync(ct), default, ct);
386 return json.RootElement.GetProperty("access_token").GetString()!;
387 }
388 }
389 ```
390
391 ```bash CLI
392 # 1. Request an access token from Okta and write it to a temp file.
393 export ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
394 curl -sS "$OKTA_ISSUER/v1/token" \
395 -d grant_type=client_credentials \
396 -d scope=anthropic.access \
397 -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
398 --data-urlencode client_assertion="$SIGNED_CLIENT_ASSERTION" \
399 | jq -r .access_token > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
400
401 # 2. Call the Claude API. The CLI reads ANTHROPIC_FEDERATION_RULE_ID,
402 # ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and
403 # ANTHROPIC_IDENTITY_TOKEN_FILE and performs the exchange.
404 ant messages create \
405 --model claude-opus-5-5 \
406 --max-tokens 1024 \
407 --message '{role: user, content: "Hello, Claude"}'
408 ```
409
410410 ```php PHP
411411 use Anthropic\Client;
412412 use Anthropic\Lib\Credentials\CredentialResult;
from line 496
496496
497497## Verify the setup
498498
499A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#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).
499A successful exchange returns an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#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).
500500
501501## Scope your rule
502502
No line in this hunk matches that.