---
title: Utiliser WIF avec GitHub Actions
url: https://platform.claude.com/docs/fr/manage-claude/wif-providers/github-actions
description: Authentifiez les workflows GitHub Actions auprès de l'API Claude avec des jetons d'identité de courte durée au lieu de clés API de longue durée.
---

Chaque exécution de workflow GitHub Actions peut demander un jeton d'identité signé auprès de l'émetteur hébergé par GitHub à l'adresse `https://token.actions.githubusercontent.com`. Avec Workload Identity Federation, votre workflow échange ce jeton contre un jeton d'accès Anthropic de courte durée, de sorte que vos tâches CI peuvent appeler l'API Claude sans qu'un secret `ANTHROPIC_API_KEY` soit stocké dans votre dépôt.

La revendication `sub` du jeton encode le dépôt et le contexte de déclenchement. Pour un push vers une branche, elle a la forme `repo:<owner>/<repo>:ref:refs/heads/<branch>`. Les exécutions de pull request utilisent `repo:<owner>/<repo>:pull_request`, et les déploiements contrôlés par environnement utilisent `repo:<owner>/<repo>:environment:<name>`. Votre règle de fédération correspond à cette revendication (et à d'autres, telles que `repository_owner` et `ref`) pour décider quelles exécutions de workflow sont autorisées à s'authentifier.

## Prérequis

* 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.
* Un dépôt GitHub où vous pouvez modifier les fichiers de workflow et accorder la permission `id-token: write`.
* La permission 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.
* L'identifiant de votre organisation Anthropic. Vous pouvez le trouver sur la page [Organization](https://platform.claude.com/settings/organization) (`platform.claude.com/settings/organization`) dans la Claude Console.

## Configurer votre workflow

GitHub n'émet un jeton d'identité qu'aux tâches qui le demandent explicitement. Ajoutez la permission `id-token: write` au niveau du workflow ou de la tâche :

```yaml
permissions:
  id-token: write
  contents: read
```

À l'intérieur de la tâche, le runner expose deux variables d'environnement : `ACTIONS_ID_TOKEN_REQUEST_URL` et `ACTIONS_ID_TOKEN_REQUEST_TOKEN`. Appelez l'URL de requête avec le jeton de requête comme identifiant bearer et votre audience choisie comme paramètre de requête, puis écrivez le « JSON Web Token » (JWT) retourné dans un fichier :

```yaml
- name: Fetch GitHub OIDC token
  run: |
    curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
      "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://api.anthropic.com" \
      | jq -r .value > /tmp/gha-jwt
```

Si vous préférez JavaScript, `actions/github-script` expose la même capacité via `core.getIDToken(audience)` :

```yaml
- name: Fetch GitHub OIDC token
  uses: actions/github-script@v8
  with:
    script: |
      const fs = require('fs');
      const token = await core.getIDToken('https://api.anthropic.com');
      fs.writeFileSync('/tmp/gha-jwt', token);
```

Le jeton décodé porte des revendications qui décrivent l'exécution du workflow. Votre règle de fédération correspond à celles-ci :

```json
{
  "iss": "https://token.actions.githubusercontent.com",
  "sub": "repo:your-org/your-repo:ref:refs/heads/main",
  "aud": "https://api.anthropic.com",
  "repository": "your-org/your-repo",
  "repository_owner": "your-org",
  "ref": "refs/heads/main",
  "sha": "abc123...",
  "workflow": "CI",
  "actor": "octocat",
  "event_name": "push"
}
```

Consultez la [référence des revendications de sujet OIDC de GitHub](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims) pour la liste complète des formats `sub`.

## 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**, puis sélectionnez la tuile **GitHub Actions**. 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 :** GitHub publie publiquement son document de découverte OIDC et son JWKS, utilisez donc le mode de découverte. Anthropic actualise automatiquement les clés lorsque GitHub les fait tourner.

```json
{
  "name": "github-actions",
  "issuer_url": "https://token.actions.githubusercontent.com",
  "jwks": { "type": "discovery" }
}
```

**Règle de fédération :** Ne faites correspondre que les exécutions de workflow auxquelles vous avez l'intention de faire confiance. Consultez [Restreindre quels workflows peuvent s'authentifier](https://platform.claude.com/docs/fr/manage-claude/wif-providers/github-actions#restrict-which-workflows-can-authenticate) pour savoir comment délimiter ces revendications en toute sécurité.

```json
{
  "name": "gha-main",
  "issuer_id": "fdis_...",
  "match": {
    "subject_prefix": "repo:your-org/your-repo:ref:refs/heads/main",
    "audience": "https://api.anthropic.com",
    "claims": {
      "repository_owner": "your-org"
    }
  },
  "target": {
    "type": "service_account",
    "service_account_id": "svac_..."
  },
  "workspace_id": "wrkspc_...",
  "oauth_scope": "workspace:developer",
  "token_lifetime_seconds": 600
}
```

Soyez aussi spécifique que la charge de travail le permet. N'assouplissez `subject_prefix` à `repo:your-org/your-repo:*` (associé à une contrainte `claims.ref`) que si la règle doit correspondre à plusieurs types d'événements provenant du même dépôt, car le segment final de `sub` varie entre les événements `ref:...`, `environment:...` et `pull_request`.

## Acquérir et utiliser un jeton

Définissez les variables d'environnement de fédération sur la tâche et appelez le SDK normalement. `Anthropic()` (typescript: `new Anthropic()`; csharp: `new AnthropicClient()`; go: `anthropic.NewClient()`; java: `AnthropicOkHttpClient.fromEnv()`; php: `new Client()`; ruby: `Anthropic::Client.new`) lit `ANTHROPIC_IDENTITY_TOKEN_FILE`, échange le JWT lors de la première requête et actualise automatiquement le jeton d'accès avant son expiration.

<CodeGroup>
  ```yaml Workflow
  name: Call Claude
  on: push

  permissions:
    id-token: write
    contents: read

  jobs:
    call-claude:
      runs-on: ubuntu-latest
      env:
        ANTHROPIC_FEDERATION_RULE_ID: fdrl_...
        ANTHROPIC_ORGANIZATION_ID: 00000000-0000-0000-0000-000000000000
        ANTHROPIC_SERVICE_ACCOUNT_ID: svac_...
        ANTHROPIC_WORKSPACE_ID: wrkspc_...  # required when the rule covers multiple workspaces
        ANTHROPIC_IDENTITY_TOKEN_FILE: /tmp/gha-jwt
      steps:
        - uses: actions/checkout@v5
        - name: Fetch GitHub OIDC token
          run: |
            curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
              "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://api.anthropic.com" \
              | jq -r .value > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
        - name: Run your script
          run: |
            pip install anthropic
            python your_script.py
  ```

  ```bash cURL
  JWT=$(cat /tmp/gha-jwt)

  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, Claude"}]
    }' | jq -r '.content[] | select(.type == "text") | .text'
  ```

  ```python Python
  import anthropic

  # Lit ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID et ANTHROPIC_IDENTITY_TOKEN_FILE
  # depuis l'environnement de la tâche.
  client = anthropic.Anthropic()

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

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";

  // Lit ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID et ANTHROPIC_IDENTITY_TOKEN_FILE
  // depuis l'environnement du job.
  const client = new Anthropic();

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

  ```go Go
  // Lit ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID et ANTHROPIC_IDENTITY_TOKEN_FILE
  // depuis l'environnement de la tâche.
  client := anthropic.NewClient()

  message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello, Claude")),
  	},
  })
  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, Claude")
          .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 job.
  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, Claude" }],
  });
  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
  # depuis l'environnement du job.
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello, Claude"}'
  ```

  ```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
  // depuis l'environnement du job.
  $client = new Client();

  $message = $client->messages->create(
      model: 'claude-opus-5-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
  );
  $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
  # depuis l'environnement du job.
  client = Anthropic::Client.new

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

Chaque jeton d'identité émis par GitHub expire environ cinq minutes après son émission. Le point de terminaison de requête de jeton (`ACTIONS_ID_TOKEN_REQUEST_URL`) reste valide pendant toute la durée de la tâche, vous pouvez donc récupérer un nouveau jeton à tout moment. Le SDK échange le jeton lors de la première utilisation et met en cache le jeton d'accès Anthropic résultant. Pour les tâches qui s'exécutent plus longtemps que la durée de vie du jeton Anthropic, le SDK relit `ANTHROPIC_IDENTITY_TOKEN_FILE` à chaque actualisation, donc réexécutez l'étape de récupération périodiquement (ou encapsulez-la dans une boucle en arrière-plan) pour maintenir le fichier à jour. Alternativement, sauf en Java, passez au SDK un fournisseur de jetons qui appelle directement `ACTIONS_ID_TOKEN_REQUEST_URL` au lieu d'utiliser le chemin du fichier.

## Vérifier la configuration

Un échange réussi renvoie un `access_token` commençant par `sk-ant-oat01-` et une valeur `expires_in` en secondes. Un échange refusé renvoie une erreur opaque `401` `authentication_error` avec le message fixe `Authentication failed`, quelle que soit la vérification qui a échoué ; dans la plupart des cas, le motif du refus est enregistré dans l'entrée de la tentative sur la [page d'historique d'authentification](https://platform.claude.com/settings/workload-identity-federation?tab=history) de la Claude Console, et [Dépanner un échange échoué](https://platform.claude.com/docs/fr/manage-claude/wif-reference#troubleshoot-a-failed-exchange) passe en revue les vérifications dans l'ordre. La cause la plus fréquente côté GitHub Actions est un format de revendication `sub` qui ne correspond pas (son segment final varie entre les événements `ref:...`, `environment:...` et `pull_request`) ; l'entrée d'historique affiche alors le motif `match_subject_prefix`.

## Restreindre quels workflows peuvent s'authentifier

<Warning>
  Un `subject_prefix` de `repo:your-org/*` seul correspond à chaque dépôt de votre organisation, et sans contrainte `ref`, il correspond également aux exécutions `pull_request` déclenchées depuis des forks. Quiconque peut ouvrir une pull request contre un dépôt correspondant pourrait obtenir un jeton Anthropic fédéré.
</Warning>

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

* **Épingler à un seul dépôt :** Utilisez `subject_prefix: "repo:your-org/your-repo:*"` afin que les autres dépôts de l'organisation ne correspondent pas.
* **Épingler à une branche protégée :** Ajoutez `"ref": "refs/heads/main"` (ou votre branche de release) sous `claims` afin que les exécutions de pull request et les branches de fonctionnalité ne correspondent pas.
* **Épingler le propriétaire explicitement :** Ajoutez `"repository_owner": "your-org"` sous `claims` comme vérification de défense en profondeur contre les cas limites d'analyse de `sub`.
* **Épingler à un environnement de déploiement :** Pour les tâches de déploiement, faites correspondre `subject_prefix: "repo:your-org/your-repo:environment:production"` et contrôlez cet environnement avec des réviseurs requis dans GitHub.

## Étapes suivantes

* [Workload Identity Federation](https://platform.claude.com/docs/fr/manage-claude/workload-identity-federation) : guide complet de configuration, variables d'environnement et priorité des identifiants.
* [Authentification](https://platform.claude.com/docs/fr/manage-claude/authentication) : comment la fédération se compare aux clés API.
