---
title: Использование WIF с GitHub Actions
url: https://platform.claude.com/docs/ru/manage-claude/wif-providers/github-actions
description: Аутентифицируйте рабочие процессы GitHub Actions в Claude API с помощью краткосрочных токенов идентификации вместо долгосрочных ключей API.
---

Каждый запуск рабочего процесса GitHub Actions может запросить подписанный токен идентификации у размещённого издателя GitHub по адресу `https://token.actions.githubusercontent.com`. С помощью «Workload Identity Federation» (федерация идентификации рабочих нагрузок), или WIF, ваш рабочий процесс обменивает этот токен на краткосрочный токен доступа Anthropic, так что ваши задания CI могут вызывать Claude API без секрета `ANTHROPIC_API_KEY`, хранящегося в вашем репозитории.

Утверждение (claim) `sub` токена кодирует репозиторий и контекст триггера. Для push в ветку оно имеет форму `repo:<owner>/<repo>:ref:refs/heads/<branch>`. Запуски по pull request используют `repo:<owner>/<repo>:pull_request`, а развёртывания, ограниченные окружением, используют `repo:<owner>/<repo>:environment:<name>`. Ваше правило федерации сопоставляется с этим утверждением (и другими, такими как `repository_owner` и `ref`), чтобы решить, каким запускам рабочих процессов разрешено проходить аутентификацию.

## Предварительные требования

* Знакомство с [концепциями WIF](https://platform.claude.com/docs/ru/manage-claude/workload-identity-federation#concepts): сервисные аккаунты, издатели федерации и правила федерации.
* Репозиторий GitHub, в котором вы можете редактировать файлы рабочих процессов и предоставлять разрешение `id-token: write`.
* Разрешение на создание сервисных аккаунтов, издателей федерации и правил федерации в Claude Console для вашей организации Anthropic.
* Идентификатор вашей организации Anthropic. Его можно найти на странице [Organization](https://platform.claude.com/settings/organization) (`platform.claude.com/settings/organization`) в Claude Console.

## Настройка рабочего процесса

GitHub выдаёт токен идентификации только тем заданиям, которые явно его запрашивают. Добавьте разрешение `id-token: write` на уровне рабочего процесса или задания:

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

Внутри задания раннер предоставляет две переменные окружения: `ACTIONS_ID_TOKEN_REQUEST_URL` и `ACTIONS_ID_TOKEN_REQUEST_TOKEN`. Вызовите URL запроса, передав токен запроса в качестве bearer-учётных данных и выбранную вами аудиторию (audience) в качестве параметра запроса, затем запишите возвращённый «JSON Web Token» (веб-токен JSON), или JWT, в файл:

```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
```

Если вы предпочитаете JavaScript, `actions/github-script` предоставляет ту же возможность через `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);
```

Декодированный токен содержит утверждения, описывающие запуск рабочего процесса. Ваше правило федерации сопоставляется с ними:

```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"
}
```

Полный список форматов `sub` см. в [справочнике GitHub по утверждению subject в OIDC](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims).

## Настройка Anthropic

В Claude Console перейдите на страницу [Workload identity](https://platform.claude.com/settings/workload-identity-federation) (`platform.claude.com/settings/workload-identity-federation`), нажмите **Connect workload** и выберите плитку **GitHub Actions**. Мастер проведёт вас через регистрацию издателя, создание сервисного аккаунта и создание правила федерации.

Мастер создаёт эти ресурсы за вас. Используйте следующие значения независимо от того, вводите ли вы их в мастере или отправляете в [Admin API](https://platform.claude.com/docs/ru/manage-claude/wif-admin-api):

**Издатель федерации:** GitHub публикует свой документ обнаружения OIDC и JWKS в открытом доступе, поэтому используйте режим обнаружения (discovery mode). Anthropic автоматически обновляет ключи, когда GitHub выполняет их ротацию.

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

**Правило федерации:** Сопоставляйте только те запуски рабочих процессов, которым вы намерены доверять. О том, как безопасно ограничить область действия этих утверждений, см. раздел [Ограничение рабочих процессов, которые могут проходить аутентификацию](https://platform.claude.com/docs/ru/manage-claude/wif-providers/github-actions#restrict-which-workflows-can-authenticate).

```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
}
```

Будьте настолько конкретны, насколько позволяет рабочая нагрузка. Ослабляйте `subject_prefix` до `repo:your-org/your-repo:*` (в паре с ограничением `claims.ref`) только в том случае, если правило должно соответствовать нескольким типам событий из одного репозитория, поскольку завершающий сегмент `sub` различается между событиями `ref:...`, `environment:...` и `pull_request`.

## Получение и использование токена

Задайте переменные окружения федерации для задания и вызывайте SDK как обычно. `Anthropic()` (typescript: `new Anthropic()`; csharp: `new AnthropicClient()`; go: `anthropic.NewClient()`; java: `AnthropicOkHttpClient.fromEnv()`; php: `new Client()`; ruby: `Anthropic::Client.new`) считывает `ANTHROPIC_IDENTITY_TOKEN_FILE`, обменивает JWT при первом запросе и автоматически обновляет токен доступа до истечения срока его действия.

<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

  # Считывает ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID и ANTHROPIC_IDENTITY_TOKEN_FILE
  # из окружения задания.
  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";

  // Считывает ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID и ANTHROPIC_IDENTITY_TOKEN_FILE
  // из окружения задания.
  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
  // Считывает ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID и ANTHROPIC_IDENTITY_TOKEN_FILE
  // из окружения задания.
  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#
  // Считывает ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID и ANTHROPIC_IDENTITY_TOKEN_FILE
  // из окружения задания.
  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
  # Считывает ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID и ANTHROPIC_IDENTITY_TOKEN_FILE
  # из окружения задания.
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello, Claude"}'
  ```

  ```php PHP
  use Anthropic\Client;

  // Считывает ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID и ANTHROPIC_IDENTITY_TOKEN_FILE
  // из окружения задания.
  $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"

  # Считывает ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID и 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, Claude"}]
  )
  puts message.content.find { it.type == :text }.text
  ```
</CodeGroup>

Срок действия каждого токена идентификации, выданного GitHub, истекает примерно через пять минут после выдачи. Конечная точка запроса токена (`ACTIONS_ID_TOKEN_REQUEST_URL`) остаётся действительной в течение всего задания, поэтому вы можете получить новый токен в любой момент. SDK обменивает токен при первом использовании и кэширует полученный токен доступа Anthropic. Для заданий, которые выполняются дольше срока жизни токена Anthropic, SDK повторно считывает `ANTHROPIC_IDENTITY_TOKEN_FILE` при каждом обновлении, поэтому периодически повторно запускайте шаг получения токена (или оберните его в фоновый цикл), чтобы файл оставался актуальным. В качестве альтернативы (кроме Java) передайте в SDK поставщика токенов, который вызывает `ACTIONS_ID_TOKEN_REQUEST_URL` напрямую вместо использования пути к файлу.

## Проверка настройки

Успешный обмен возвращает `access_token`, начинающийся с `sk-ant-oat01-`, и значение `expires_in` в секундах. Отклонённый обмен возвращает непрозрачную ошибку `401` `authentication_error` с фиксированным сообщением `Authentication failed`, независимо от того, какая проверка не прошла; в большинстве случаев причина отказа записывается в записи о попытке на [странице истории аутентификации](https://platform.claude.com/settings/workload-identity-federation?tab=history) в Claude Console, а раздел [Устранение неполадок при неудачном обмене](https://platform.claude.com/docs/ru/manage-claude/wif-reference#troubleshoot-a-failed-exchange) описывает проверки по порядку. Наиболее распространённая причина на стороне GitHub Actions — несоответствие формата утверждения `sub` (его завершающий сегмент различается для событий `ref:...`, `environment:...` и `pull_request`); в записи истории указывается причина `match_subject_prefix`.

## Ограничение рабочих процессов, которые могут проходить аутентификацию

<Warning>
  Один лишь `subject_prefix` со значением `repo:your-org/*` соответствует каждому репозиторию в вашей организации, а без ограничения `ref` он также соответствует запускам `pull_request`, инициированным из форков. Любой, кто может открыть pull request в соответствующий репозиторий, мог бы получить федеративный токен Anthropic.
</Warning>

Ограничьте блок `match` правила самой узкой областью, подходящей для вашего сценария использования:

* **Привязка к одному репозиторию:** Используйте `subject_prefix: "repo:your-org/your-repo:*"`, чтобы другие репозитории в организации не соответствовали правилу.
* **Привязка к защищённой ветке:** Добавьте `"ref": "refs/heads/main"` (или вашу релизную ветку) в `claims`, чтобы запуски по pull request и функциональные ветки не соответствовали правилу.
* **Явная привязка владельца:** Добавьте `"repository_owner": "your-org"` в `claims` в качестве дополнительной защитной проверки на случай пограничных ситуаций при разборе `sub`.
* **Привязка к окружению развёртывания:** Для заданий развёртывания сопоставляйте `subject_prefix: "repo:your-org/your-repo:environment:production"` и защитите это окружение обязательными рецензентами в GitHub.

## Следующие шаги

* [Workload Identity Federation](https://platform.claude.com/docs/ru/manage-claude/workload-identity-federation): полное пошаговое руководство по настройке, переменные окружения и приоритет учётных данных.
* [Аутентификация](https://platform.claude.com/docs/ru/manage-claude/authentication): сравнение федерации с ключами API.
