---
title: Utiliser WIF avec AWS
url: https://platform.claude.com/docs/fr/manage-claude/wif-providers/aws
description: Authentifiez les charges de travail AWS sur Lambda, EC2, ECS ou EKS auprès de l'API Claude avec Workload Identity Federation et des jetons d'identité émis par STS.
---

Les charges de travail AWS peuvent s'authentifier auprès de l'API Claude sans clés API statiques en échangeant un jeton d'identité OIDC signé par AWS. La méthode recommandée appelle l'API AWS STS [`GetWebIdentityToken`](https://docs.aws.amazon.com/STS/latest/APIReference/API_GetWebIdentityToken.html), qui fonctionne partout où la charge de travail dispose d'identifiants AWS : Lambda, EC2, ECS et EKS. Les charges de travail EKS peuvent également utiliser la [méthode du jeton projeté Kubernetes](https://platform.claude.com/docs/fr/manage-claude/wif-providers/aws#use-eks-projected-service-account-tokens), qui comporte moins d'étapes de configuration mais ne fonctionne qu'à l'intérieur d'un pod.

Ce guide présente les deux méthodes. Pour les concepts sous-jacents (comptes de service, émetteurs de fédération et règles de fédération), consultez [Workload Identity Federation](https://platform.claude.com/docs/fr/manage-claude/workload-identity-federation).

## Prérequis

* Une familiarité avec les [concepts WIF](https://platform.claude.com/docs/fr/manage-claude/workload-identity-federation#concepts) : comptes de service, émetteurs de fédération et règles de fédération.
* Une charge de travail AWS (pod EKS, tâche ECS, fonction Lambda ou instance EC2) avec un rôle IAM attaché.
* La CLI `aws` ou un SDK AWS disponible dans la charge de travail.
* L'autorisation de créer des comptes de service, des émetteurs de fédération et des règles de fédération dans la Claude Console pour votre organisation Anthropic.

## Utiliser les jetons d'identité web STS (recommandé)

L'API AWS STS `GetWebIdentityToken` renvoie un jeton OIDC signé par AWS qui atteste l'identité IAM de l'appelant. Comme elle utilise les identifiants AWS ambiants de la charge de travail, la même intégration couvre Lambda, EC2, ECS et EKS.

### Configurer AWS

<Steps>
  <Step title="Activer la fédération d'identité web sortante pour le compte">
    Il s'agit d'un indicateur au niveau du compte, désactivé par défaut. Dans la console AWS, ouvrez **IAM**, choisissez **Account settings** et activez **Outbound web identity federation**. Pour l'activer par programmation :

    ```bash
    python3 -c "import boto3; boto3.client('iam').enable_outbound_web_identity_federation()"
    ```

    Si cette option n'est pas activée, les appels à `GetWebIdentityToken` échouent avec `OutboundWebIdentityFederationDisabledException`.
  </Step>

  <Step title="Accorder au rôle IAM de la charge de travail l'autorisation d'appeler l'API">
    Attachez cette politique au rôle IAM sous lequel s'exécute votre fonction Lambda, votre instance EC2 ou votre tâche ECS :

    ```json
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["sts:GetWebIdentityToken"],
          "Resource": "*"
        }
      ]
    }
    ```
  </Step>

  <Step title="Trouver l'URL de l'émetteur STS de votre compte">
    Après avoir activé la fédération sortante, la page **IAM > Account settings** affiche un champ **Get Token Issuer URL** dont la valeur a la forme `https://<uuid>.tokens.sts.global.api.aws`. Cette URL est propre à votre compte AWS ; copiez-la pour l'étape suivante. Pour la récupérer par programmation :

    ```bash
    python3 -c "import boto3; print(boto3.client('iam').get_outbound_web_identity_federation_info())"
    ```
  </Step>
</Steps>

### Configurer Anthropic

Dans la Claude Console, accédez à la page [Workload identity](https://platform.claude.com/settings/workload-identity-federation) (`platform.claude.com/settings/workload-identity-federation`), cliquez sur **Connect workload** et sélectionnez la tuile **AWS**. L'assistant vous guide dans l'enregistrement de l'émetteur, la création d'un compte de service et la création d'une règle de fédération.

L'assistant crée ces ressources pour vous. Utilisez les valeurs suivantes, que vous les saisissiez dans l'assistant ou que vous les envoyiez à l'[Admin API](https://platform.claude.com/docs/fr/manage-claude/wif-admin-api) :

**Émetteur de fédération :** enregistrez l'URL de l'émetteur STS propre au compte que vous avez copiée à l'étape précédente. Elle expose un point de terminaison JWKS public, utilisez donc le mode découverte.

```json
{
  "name": "aws-sts",
  "issuer_url": "https://<uuid>.tokens.sts.global.api.aws",
  "jwks": { "type": "discovery" }
}
```

**Règle de fédération :** faites correspondre l'audience que vous transmettez à `GetWebIdentityToken` et l'ARN du rôle IAM appelant dans la revendication `sub`. La valeur `sub` est l'ARN du rôle IAM de la charge de travail qui a appelé l'API, sous la forme `arn:aws:iam::<account>:role/<role-name>`. Le jeton contient également une revendication `https://sts.amazonaws.com/` avec `aws_account`, `org_id`, `principal_id` et tous les `request_tags` que vous avez transmis ; vous pouvez établir une correspondance sur ceux-ci avec la table `claims` de la règle ou une `condition` CEL pour un contrôle plus fin.

```json
{
  "name": "prod-inference",
  "issuer_id": "fdis_...",
  "match": {
    "subject_prefix": "arn:aws:iam::123456789012:role/inference-worker",
    "audience": "https://api.anthropic.com"
  },
  "target": { "type": "service_account", "service_account_id": "svac_..." },
  "workspace_id": "wrkspc_...",
  "oauth_scope": "workspace:developer",
  "token_lifetime_seconds": 600
}
```

Soyez aussi précis que la charge de travail le permet. Faites correspondre l'ARN exact du rôle et n'élargissez `subject_prefix` (par exemple, à `arn:aws:iam::123456789012:role/*`) que si plusieurs rôles IAM doivent correspondre au même compte de service Anthropic.

### Obtenir et utiliser le jeton

Appelez `GetWebIdentityToken` avec `https://api.anthropic.com` comme audience, puis transmettez le résultat aux identifiants de fédération du SDK via `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`). Sauf en Java (voir le commentaire dans cet exemple), cette option accepte un fournisseur de jetons, de sorte que le SDK appelle à nouveau STS à chaque actualisation.

<Note>
  `GetWebIdentityToken` n'est disponible que sur les points de terminaison STS régionaux. Si vous recevez `'STS' object has no attribute 'get_web_identity_token'` ou une erreur similaire, fixez votre client STS sur une région (par exemple, `boto3.client("sts", region_name="us-east-1")`) et assurez-vous que votre SDK AWS est suffisamment récent pour inclure l'API.
</Note>

<CodeGroup>
  ```bash cURL
  JWT=$(aws sts get-web-identity-token \
    --region us-east-1 \
    --audience "https://api.anthropic.com" \
    --signing-algorithm RS256 \
    --duration-seconds 900 \
    --query WebIdentityToken --output text)

  RESPONSE=$(curl -sS https://api.anthropic.com/v1/oauth/token \
    -H "content-type: application/json" \
    --data @- <<JSON
  {
    "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "assertion": "$JWT",
    "federation_rule_id": "$ANTHROPIC_FEDERATION_RULE_ID",
    "organization_id": "$ANTHROPIC_ORGANIZATION_ID",
    "service_account_id": "$ANTHROPIC_SERVICE_ACCOUNT_ID",
    "workspace_id": "$ANTHROPIC_WORKSPACE_ID"
  }
  JSON
  )

  ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r .access_token)

  curl https://api.anthropic.com/v1/messages \
    -H "authorization: Bearer $ACCESS_TOKEN" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello from AWS"}]
    }' | jq -r '.content[] | select(.type == "text") | .text'
  ```

  ```python Python
  import os

  import anthropic
  import boto3
  from anthropic import WorkloadIdentityCredentials


  def get_sts_web_identity_token() -> str:
      sts = boto3.client("sts", region_name="us-east-1")
      resp = sts.get_web_identity_token(
          Audience=["https://api.anthropic.com"],
          SigningAlgorithm="RS256",
          DurationSeconds=900,
      )
      return resp["WebIdentityToken"]


  client = anthropic.Anthropic(
      credentials=WorkloadIdentityCredentials(
          identity_token_provider=get_sts_web_identity_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"],
          workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
      ),
  )

  message = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello from AWS"}],
  )
  print(next(block.text for block in message.content if block.type == "text"))
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import { oidcFederationProvider } from "@anthropic-ai/sdk/lib/credentials/oidc-federation";
  import { STSClient, GetWebIdentityTokenCommand } from "@aws-sdk/client-sts";

  const sts = new STSClient({ region: "us-east-1" });

  async function getStsWebIdentityToken(): Promise<string> {
    const out = await sts.send(
      new GetWebIdentityTokenCommand({
        Audience: ["https://api.anthropic.com"],
        SigningAlgorithm: "RS256",
        DurationSeconds: 900
      })
    );
    return out.WebIdentityToken!;
  }

  const client = new Anthropic({
    credentials: oidcFederationProvider({
      identityTokenProvider: getStsWebIdentityToken,
      federationRuleId: process.env.ANTHROPIC_FEDERATION_RULE_ID!,
      organizationId: process.env.ANTHROPIC_ORGANIZATION_ID!,
      serviceAccountId: process.env.ANTHROPIC_SERVICE_ACCOUNT_ID,
      workspaceId: process.env.ANTHROPIC_WORKSPACE_ID,
      baseURL: "https://api.anthropic.com",
      fetch
    })
  });

  const message = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello from AWS" }]
  });
  for (const block of message.content) {
    if (block.type === "text") {
      console.log(block.text);
    }
  }
  ```

  ```go Go
  ctx := context.TODO()
  cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))
  if err != nil {
  	panic(err)
  }
  stsClient := sts.NewFromConfig(cfg)

  getStsToken := option.IdentityTokenFunc(func(ctx context.Context) (string, error) {
  	out, err := stsClient.GetWebIdentityToken(ctx, &sts.GetWebIdentityTokenInput{
  		Audience:         []string{"https://api.anthropic.com"},
  		SigningAlgorithm: "RS256",
  		DurationSeconds:  aws.Int32(900),
  	})
  	if err != nil {
  		return "", err
  	}
  	return *out.WebIdentityToken, nil
  })

  client := anthropic.NewClient(
  	option.WithFederationTokenProvider(getStsToken, option.FederationOptions{
  		FederationRuleID: os.Getenv("ANTHROPIC_FEDERATION_RULE_ID"),
  		OrganizationID:   os.Getenv("ANTHROPIC_ORGANIZATION_ID"),
  		ServiceAccountID: os.Getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"),
  		WorkspaceID:      os.Getenv("ANTHROPIC_WORKSPACE_ID"),
  	}),
  )

  message, err := client.Messages.New(ctx, anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello from AWS")),
  	},
  })
  if err != nil {
  	panic(err)
  }
  for _, block := range message.Content {
  	if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  		fmt.Println(textBlock.Text)
  		break
  	}
  }
  ```

  ```java Java
  import com.anthropic.config.AuthenticationConfig;
  import com.anthropic.config.AuthenticationType;
  import com.anthropic.config.IdentityTokenConfig;
  import com.anthropic.config.InMemoryProfileConfigProvider;
  import com.anthropic.config.ProfileConfig;
  // ...
  import software.amazon.awssdk.regions.Region;
  import software.amazon.awssdk.services.sts.StsClient;
  import software.amazon.awssdk.services.sts.model.GetWebIdentityTokenRequest;

  void main() {
      StsClient sts = StsClient.builder().region(Region.US_EAST_1).build();

      // Le SDK Java accepte le jeton lui-même ou un chemin de fichier, pas une fonction ; avec
      // source("value"), path(...) contient le jeton. Le SDK envoie ce même jeton
      // à chaque actualisation du jeton d'accès Anthropic, ce qui échoue dès qu'il a
      // expiré ou, s'il possède une revendication jti, été utilisé. Pour un processus
      // de longue durée, passez source("file") et écrivez un nouveau jeton dans le fichier avant
      // chaque actualisation.
      String stsToken = sts.getWebIdentityToken(
                      GetWebIdentityTokenRequest.builder()
                              .audience("https://api.anthropic.com")
                              .signingAlgorithm("RS256")
                              .durationSeconds(900)
                              .build())
              .webIdentityToken();

      AnthropicClient client = AnthropicOkHttpClient.builder()
              .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
                      .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
                      .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
                      .authentication(AuthenticationConfig.builder()
                              .type(AuthenticationType.OIDC_FEDERATION)
                              .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
                              .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
                              .identityToken(IdentityTokenConfig.builder()
                                      .source("value")
                                      .path(stsToken)
                                      .build())
                              .build())
                      .build()))
              .build();

      var message = client.messages().create(MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024)
              .addUserMessage("Hello from AWS")
              .build());

      IO.println(message.content());
  }
  ```

  ```csharp C#
  using Amazon.SecurityToken;
  using Amazon.SecurityToken.Model;
  // ...
  using Anthropic.Credentials;
  // ...

  var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
  {
      FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
      OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
      ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
      WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
      IdentityTokenProvider = new StsTokenProvider(),
  });
  using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });

  var message = await client.Messages.Create(new()
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello from AWS" }],
  });
  foreach (var block in message.Content)
  {
      if (block.Value is TextBlock textBlock)
      {
          Console.WriteLine(textBlock.Text);
      }
  }

  class StsTokenProvider : IIdentityTokenProvider
  {
      private readonly AmazonSecurityTokenServiceClient _sts = new(Amazon.RegionEndpoint.USEast1);

      public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
      {
          var resp = await _sts.GetWebIdentityTokenAsync(new GetWebIdentityTokenRequest
          {
              Audience = ["https://api.anthropic.com"],
              SigningAlgorithm = "RS256",
              DurationSeconds = 900,
          }, ct);
          return resp.WebIdentityToken;
      }
  }
  ```

  ```bash CLI
  TOKEN_FILE=$(mktemp)
  aws sts get-web-identity-token \
    --region us-east-1 \
    --audience "https://api.anthropic.com" \
    --signing-algorithm RS256 \
    --duration-seconds 900 \
    --query WebIdentityToken --output text > "$TOKEN_FILE"

  export ANTHROPIC_IDENTITY_TOKEN_FILE="$TOKEN_FILE"
  # ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID, ainsi que
  # ANTHROPIC_SERVICE_ACCOUNT_ID et ANTHROPIC_WORKSPACE_ID sont lus depuis l'environnement.
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello from AWS"}'
  ```

  ```php PHP
  use Anthropic\Client;
  use Anthropic\Lib\Credentials\CredentialResult;
  use Anthropic\Lib\Credentials\IdentityTokenProvider;
  use Anthropic\Lib\Credentials\TokenCache;
  use Anthropic\Lib\Credentials\WorkloadIdentityCredentials;
  use Aws\Sts\StsClient;

  $sts = new StsClient(['region' => 'us-east-1', 'version' => 'latest']);

  class StsIdentityToken implements IdentityTokenProvider
  {
      public function __construct(private StsClient $sts) {}

      public function getIdentityToken(): string
      {
          return $this->sts->getWebIdentityToken([
              'Audience' => ['https://api.anthropic.com'],
              'SigningAlgorithm' => 'RS256',
              'DurationSeconds' => 900,
          ])['WebIdentityToken'];
      }
  }

  $client = new Client(credentials: new CredentialResult(
      provider: new TokenCache(new WorkloadIdentityCredentials(
          identityProvider: new StsIdentityToken($sts),
          federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
          organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
          serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
          workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
      )),
  ));

  $message = $client->messages->create(
      model: 'claude-opus-5-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello from AWS']],
  );
  $textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
  echo $textBlock->text, PHP_EOL;
  ```

  ```ruby Ruby
  require "anthropic"
  require "aws-sdk-sts"

  sts = Aws::STS::Client.new(region: "us-east-1")
  client = Anthropic::Client.new(
    credentials: Anthropic::WorkloadIdentityCredentials.new(
      identity_token_provider: -> {
        sts.get_web_identity_token(
          audience: ["https://api.anthropic.com"],
          signing_algorithm: "RS256",
          duration_seconds: 900,
        ).web_identity_token
      },
      federation_rule_id: ENV.fetch("ANTHROPIC_FEDERATION_RULE_ID"),
      organization_id: ENV.fetch("ANTHROPIC_ORGANIZATION_ID"),
      service_account_id: ENV.fetch("ANTHROPIC_SERVICE_ACCOUNT_ID"),
      workspace_id: ENV["ANTHROPIC_WORKSPACE_ID"],
    ),
  )

  message = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello from AWS"}]
  )
  puts message.content.find { it.type == :text }.text
  ```
</CodeGroup>

### Vérifier la configuration

Depuis l'intérieur de la charge de travail, échangez directement un jeton émis par STS et examinez la réponse :

```bash cURL
JWT=$(aws sts get-web-identity-token \
  --region us-east-1 \
  --audience "https://api.anthropic.com" \
  --signing-algorithm RS256 \
  --duration-seconds 900 \
  --query WebIdentityToken --output text)

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\": \"$JWT\",
    \"federation_rule_id\": \"fdrl_...\",
    \"organization_id\": \"00000000-0000-0000-0000-000000000000\",
    \"service_account_id\": \"svac_...\",
    \"workspace_id\": \"wrkspc_...\"
  }" | jq
```

Un échange réussi renvoie un `access_token` commençant par `sk-ant-oat01-` et une valeur `expires_in` en secondes. Si l'échange échoue avec la réponse opaque `401` `authentication_error` (message `Authentication failed`), consultez la [page d'historique d'authentification](https://platform.claude.com/settings/workload-identity-federation?tab=history) dans la Claude Console pour connaître le motif du refus et reportez-vous à [Dépanner un échange échoué](https://platform.claude.com/docs/fr/manage-claude/wif-reference#troubleshoot-a-failed-exchange) ; la cause la plus fréquente côté AWS est une non-correspondance de `iss` (l'URL de l'émetteur STS propre au compte doit correspondre exactement à l'`issuer_url` enregistrée).

## Utiliser les jetons de compte de service projetés EKS

Si votre charge de travail s'exécute dans un pod EKS, vous pouvez ignorer l'appel STS et lire directement depuis le disque un jeton de compte de service projeté par Kubernetes. Kubernetes projette nativement un jeton compatible OIDC dans le pod, et le SDK peut le lire à partir d'un chemin de fichier, de sorte qu'aucun fournisseur de jeton appelable n'est nécessaire. Cette méthode comporte deux étapes de configuration AWS de moins que la méthode STS mais ne fonctionne qu'à l'intérieur d'un pod ; le mécanisme sous-jacent est le même que celui de l'[intégration Kubernetes générique](https://platform.claude.com/docs/fr/manage-claude/wif-providers/kubernetes).

Cette méthode nécessite en outre un cluster EKS avec un [fournisseur OIDC IAM activé](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html) et un accès `kubectl` au cluster.

### Configurer votre cluster EKS

<Steps>
  <Step title="Trouver l'URL de l'émetteur OIDC de votre cluster">
    Chaque cluster EKS possède un émetteur OIDC unique. Récupérez-le avec la CLI AWS :

    ```bash CLI
    aws eks describe-cluster \
      --name <cluster-name> \
      --query "cluster.identity.oidc.issuer" \
      --output text
    ```

    La sortie ressemble à `https://oidc.eks.us-west-2.amazonaws.com/id/6FA42E7BFDE8549CB...`. Vous enregistrerez cette URL comme émetteur de fédération dans la section suivante.
  </Step>

  <Step title="Créer le compte de service et projeter un jeton avec l'audience Anthropic">
    Le webhook d'identité de pod EKS détecte l'annotation `eks.amazonaws.com/role-arn` et projette automatiquement un jeton avec `aud: sts.amazonaws.com`, en exposant son chemin sous `AWS_WEB_IDENTITY_TOKEN_FILE`. Ce jeton sert à l'endossement de rôle AWS. Pour l'échange Anthropic, projetez un second jeton avec `audience: https://api.anthropic.com` et montez-le à un chemin dédié.

    ```yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: inference-worker
      namespace: inference
      annotations:
        eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/inference-worker
    ```

    ```yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: inference-worker
      namespace: inference
    spec:
      serviceAccountName: inference-worker
      volumes:
        - name: anthropic-token
          projected:
            sources:
              - serviceAccountToken:
                  audience: https://api.anthropic.com
                  expirationSeconds: 3600
                  path: token
      containers:
        - name: app
          image: your-registry/inference-worker:latest
          env:
            - name: ANTHROPIC_IDENTITY_TOKEN_FILE
              value: /var/run/secrets/anthropic.com/token
            - name: ANTHROPIC_FEDERATION_RULE_ID
              value: fdrl_...
            - name: ANTHROPIC_ORGANIZATION_ID
              value: 00000000-0000-0000-0000-000000000000
            - name: ANTHROPIC_SERVICE_ACCOUNT_ID
              value: svac_...
            - name: ANTHROPIC_WORKSPACE_ID  # required when the rule covers multiple workspaces
              value: wrkspc_...
          volumeMounts:
            - name: anthropic-token
              mountPath: /var/run/secrets/anthropic.com
              readOnly: true
    ```
  </Step>

  <Step title="Noter la forme des revendications du jeton">
    Le jeton projeté est un « JSON Web Token » (jeton web JSON), ou JWT, signé par l'émetteur OIDC de votre cluster. Sa revendication `sub` suit la convention Kubernetes `system:serviceaccount:<namespace>:<service-account-name>` :

    ```json
    {
      "iss": "https://oidc.eks.us-west-2.amazonaws.com/id/6FA42E7BFDE8549CB...",
      "sub": "system:serviceaccount:inference:inference-worker",
      "aud": ["https://api.anthropic.com"],
      "kubernetes.io": {
        "namespace": "inference",
        "serviceaccount": { "name": "inference-worker", "uid": "..." }
      },
      "exp": 1775527120,
      "iat": 1775523520
    }
    ```

    La projection `serviceAccountToken` définit `aud` sur `https://api.anthropic.com`. Le jeton distinct injecté par IRSA à l'emplacement `AWS_WEB_IDENTITY_TOKEN_FILE` porte `aud: sts.amazonaws.com` et sert aux appels d'API AWS, pas à cet échange.
  </Step>
</Steps>

### Configurer Anthropic

Dans la Claude Console, accédez à la page [Workload identity](https://platform.claude.com/settings/workload-identity-federation) (`platform.claude.com/settings/workload-identity-federation`), cliquez sur **Connect workload** et sélectionnez la tuile **AWS**. L'assistant vous guide dans l'enregistrement de l'émetteur, la création d'un compte de service et la création d'une règle de fédération.

L'assistant crée ces ressources pour vous. Utilisez les valeurs suivantes, que vous les saisissiez dans l'assistant ou que vous les envoyiez à l'[Admin API](https://platform.claude.com/docs/fr/manage-claude/wif-admin-api) :

**Émetteur de fédération :** les émetteurs EKS exposent un point de terminaison JWKS public, utilisez donc le mode découverte. L'URL de l'émetteur doit correspondre exactement à la revendication `iss` du jeton. Enregistrez un émetteur par cluster.

```json
{
  "name": "prod-eks-uswest2",
  "issuer_url": "https://oidc.eks.us-west-2.amazonaws.com/id/6FA42E7BFDE8549CB...",
  "jwks": { "type": "discovery" }
}
```

**Règle de fédération :** faites correspondre la revendication `sub` Kubernetes et l'audience Anthropic `https://api.anthropic.com`. (Projetez un jeton de compte de service dédié avec cette audience ; ne réutilisez pas le jeton IRSA par défaut `sts.amazonaws.com`.)

```json
{
  "name": "prod-inference",
  "issuer_id": "fdis_...",
  "match": {
    "subject_prefix": "system:serviceaccount:inference:inference-worker",
    "audience": "https://api.anthropic.com"
  },
  "target": { "type": "service_account", "service_account_id": "svac_..." },
  "workspace_id": "wrkspc_...",
  "oauth_scope": "workspace:developer",
  "token_lifetime_seconds": 600
}
```

Soyez aussi précis que la charge de travail le permet. N'assouplissez `subject_prefix` en `system:serviceaccount:inference:*` (le `*` final en fait une correspondance par préfixe) que si chaque compte de service de l'espace de noms doit correspondre au même compte de service Anthropic.

### Obtenir et utiliser le jeton

À l'intérieur du pod, le jeton projeté se trouve à `/var/run/secrets/anthropic.com/token` (exposé sous `ANTHROPIC_IDENTITY_TOKEN_FILE` dans la spécification du Pod). Transmettez ce fichier aux identifiants de fédération du SDK et le SDK gère l'échange et l'actualisation.

<CodeGroup>
  ```bash cURL
  JWT=$(cat "$ANTHROPIC_IDENTITY_TOKEN_FILE")

  RESPONSE=$(curl -sS https://api.anthropic.com/v1/oauth/token \
    -H "content-type: application/json" \
    --data @- <<JSON
  {
    "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "assertion": "$JWT",
    "federation_rule_id": "$ANTHROPIC_FEDERATION_RULE_ID",
    "organization_id": "$ANTHROPIC_ORGANIZATION_ID",
    "service_account_id": "$ANTHROPIC_SERVICE_ACCOUNT_ID",
    "workspace_id": "$ANTHROPIC_WORKSPACE_ID"
  }
  JSON
  )

  ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r .access_token)

  curl https://api.anthropic.com/v1/messages \
    -H "authorization: Bearer $ACCESS_TOKEN" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello from EKS"}]
    }' | jq -r '.content[] | select(.type == "text") | .text'
  ```

  ```python Python
  import os

  import anthropic
  from anthropic import IdentityTokenFile, WorkloadIdentityCredentials

  client = anthropic.Anthropic(
      credentials=WorkloadIdentityCredentials(
          identity_token_provider=IdentityTokenFile(
              os.environ["ANTHROPIC_IDENTITY_TOKEN_FILE"]
          ),
          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"],
          workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
      ),
  )

  message = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello from EKS"}],
  )
  print(next(block.text for block in message.content if block.type == "text"))
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import { oidcFederationProvider } from "@anthropic-ai/sdk/lib/credentials/oidc-federation";
  import { identityTokenFromFile } from "@anthropic-ai/sdk/lib/credentials/identity-token";

  const client = new Anthropic({
    credentials: oidcFederationProvider({
      identityTokenProvider: identityTokenFromFile(process.env.ANTHROPIC_IDENTITY_TOKEN_FILE!),
      federationRuleId: process.env.ANTHROPIC_FEDERATION_RULE_ID!,
      organizationId: process.env.ANTHROPIC_ORGANIZATION_ID!,
      serviceAccountId: process.env.ANTHROPIC_SERVICE_ACCOUNT_ID,
      workspaceId: process.env.ANTHROPIC_WORKSPACE_ID,
      baseURL: "https://api.anthropic.com",
      fetch
    })
  });

  const message = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello from EKS" }]
  });
  for (const block of message.content) {
    if (block.type === "text") {
      console.log(block.text);
    }
  }
  ```

  ```go Go
  tokenPath := os.Getenv("ANTHROPIC_IDENTITY_TOKEN_FILE")

  readToken := option.IdentityTokenFunc(func(ctx context.Context) (string, error) {
  	raw, err := os.ReadFile(tokenPath)
  	if err != nil {
  		return "", fmt.Errorf("read identity token: %w", err)
  	}
  	return string(raw), nil
  })

  client := anthropic.NewClient(
  	option.WithFederationTokenProvider(readToken, option.FederationOptions{
  		FederationRuleID: os.Getenv("ANTHROPIC_FEDERATION_RULE_ID"),
  		OrganizationID:   os.Getenv("ANTHROPIC_ORGANIZATION_ID"),
  		ServiceAccountID: os.Getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"),
  		WorkspaceID:      os.Getenv("ANTHROPIC_WORKSPACE_ID"),
  	}),
  )

  message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello from EKS")),
  	},
  })
  if err != nil {
  	panic(err)
  }
  for _, block := range message.Content {
  	if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  		fmt.Println(textBlock.Text)
  		break
  	}
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  var message = client.messages().create(MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          .addUserMessage("Hello from EKS")
          .build());

  IO.println(message.content());
  ```

  ```csharp C#
  // Lit ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID et ANTHROPIC_IDENTITY_TOKEN_FILE
  // depuis l'environnement du pod.
  using var client = new AnthropicClient();

  var message = await client.Messages.Create(new()
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello from EKS" }],
  });
  foreach (var block in message.Content)
  {
      if (block.Value is TextBlock textBlock)
      {
          Console.WriteLine(textBlock.Text);
      }
  }
  ```

  ```bash CLI
  # Lit ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID et ANTHROPIC_IDENTITY_TOKEN_FILE
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello from EKS"}'
  ```

  ```php PHP
  use Anthropic\Client;

  // Lit ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID et ANTHROPIC_IDENTITY_TOKEN_FILE
  $client = new Client();

  $message = $client->messages->create(
      model: 'claude-opus-5-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello from EKS']],
  );
  $textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
  echo $textBlock->text, PHP_EOL;
  ```

  ```ruby Ruby
  require "anthropic"

  # Lit ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID et ANTHROPIC_IDENTITY_TOKEN_FILE
  client = Anthropic::Client.new

  message = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello from EKS"}]
  )
  puts message.content.find { it.type == :text }.text
  ```
</CodeGroup>

<Tip>
  La spécification du Pod définit déjà `ANTHROPIC_IDENTITY_TOKEN_FILE`, `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_SERVICE_ACCOUNT_ID` et `ANTHROPIC_WORKSPACE_ID`, vous pouvez donc construire le client sans arguments et le SDK lit automatiquement les variables d'environnement de fédération.
</Tip>

### Vérifier la configuration

Depuis l'intérieur du pod, échangez directement le jeton projeté et examinez la réponse :

```bash cURL
JWT=$(cat "$ANTHROPIC_IDENTITY_TOKEN_FILE")

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\": \"$JWT\",
    \"federation_rule_id\": \"$ANTHROPIC_FEDERATION_RULE_ID\",
    \"organization_id\": \"$ANTHROPIC_ORGANIZATION_ID\",
    \"service_account_id\": \"$ANTHROPIC_SERVICE_ACCOUNT_ID\",
    \"workspace_id\": \"$ANTHROPIC_WORKSPACE_ID\"
  }" | jq
```

Un échange réussi renvoie un `access_token` commençant par `sk-ant-oat01-` et une valeur `expires_in` en secondes. Si l'échange échoue avec la réponse opaque `401` `authentication_error` (message `Authentication failed`), consultez la [page d'historique d'authentification](https://platform.claude.com/settings/workload-identity-federation?tab=history) dans la Claude Console pour connaître le motif du refus et reportez-vous à [Dépanner un échange échoué](https://platform.claude.com/docs/fr/manage-claude/wif-reference#troubleshoot-a-failed-exchange) ; la cause la plus fréquente côté EKS est que l'`aud` du jeton projeté ne correspond pas à la règle (projetez un jeton avec `audience: https://api.anthropic.com`, et non la valeur IRSA par défaut `sts.amazonaws.com`).

## Délimiter votre règle

<Warning>
  Un `subject_prefix` de `arn:aws:iam::123456789012:role/*` correspond à tous les rôles IAM du compte. Tout principal pouvant endosser n'importe quel rôle correspondant peut obtenir un jeton Anthropic fédéré.
</Warning>

Verrouillez le bloc `match` de la règle sur la portée la plus étroite qui convient à votre cas d'utilisation :

* **Fixer l'ARN complet du rôle :** utilisez `subject_prefix: "arn:aws:iam::<account>:role/<role-name>"` sans `*` final afin que les autres rôles du compte ne correspondent pas.
* **Fixer l'ID du compte :** faites correspondre le champ `aws_account` de la revendication `https://sts.amazonaws.com/` du jeton avec la table `claims` ou une `condition` CEL comme vérification de défense en profondeur contre un préfixe mal configuré.
* **Fixer l'espace de noms et le compte de service sur EKS :** utilisez la valeur exacte `system:serviceaccount:<namespace>:<name>` sans `*` après le préfixe `system:serviceaccount:`.
* **Utiliser une règle distincte par environnement :** créez des règles distinctes pour les charges de travail de production, de préproduction et de développement plutôt que d'élargir un seul préfixe pour toutes les couvrir.

## Étapes suivantes

* Consultez la [référence WIF](https://platform.claude.com/docs/fr/manage-claude/wif-reference) pour la référence complète sur l'ordre de priorité des identifiants, la configuration des profils et la correspondance des règles.
* Pour les clusters Kubernetes autogérés qui ne sont pas sur EKS, consultez [Utiliser WIF avec Kubernetes](https://platform.claude.com/docs/fr/manage-claude/wif-providers/kubernetes).
