---
title: Usare WIF con Google Cloud
url: https://platform.claude.com/docs/it/manage-claude/wif-providers/gcp
description: Federa i carichi di lavoro Google Cloud (Cloud Run, Cloud Functions, App Engine, GCE, GKE) alla Claude API utilizzando token di identità firmati da Google invece di chiavi API statiche.
---

Qualsiasi ambiente di calcolo Google Cloud con accesso al server dei metadati dell'istanza (Cloud Run, Cloud Functions, App Engine, Compute Engine (GCE) e GKE con Workload Identity) può richiedere un token di identità firmato da Google per il service account associato. L'emittente del token è `https://accounts.google.com` e Anthropic può convalidarlo direttamente tramite la discovery OIDC standard, senza alcuna configurazione aggiuntiva di Google Cloud.

Questa guida mostra come registrare l'emittente Google presso Anthropic, associare un service account Google a un service account Anthropic e fare in modo che il tuo carico di lavoro scambi il proprio token di identità con un token di accesso alla Claude API di breve durata.

## Prerequisiti

* Familiarità con i [concetti di WIF](https://platform.claude.com/docs/it/manage-claude/workload-identity-federation#concepts): service account, emittenti di federazione e regole di federazione.
* Un progetto Google Cloud con un carico di lavoro in esecuzione su Cloud Run, Cloud Functions, App Engine, Compute Engine o GKE.
* Un service account Google gestito dall'utente associato a quel carico di lavoro (non il service account predefinito di Compute Engine).
* Autorizzazione a creare service account, emittenti di federazione e regole di federazione nella Claude Console per la tua organizzazione Anthropic.

## Configurare Google Cloud

Google emette automaticamente token di identità per qualsiasi carico di lavoro con un service account associato. Non c'è nulla da abilitare lato Google oltre ad associare il service account corretto, ma i passaggi differiscono leggermente tra il calcolo standard e GKE.

<Tabs>
  <Tab title="Cloud Run, Cloud Functions, App Engine, GCE">
    Associa un service account dedicato al tuo servizio o alla tua istanza:

    ```bash CLI
    gcloud run deploy my-service \
      --service-account inference-worker@my-project.iam.gserviceaccount.com
    ```

    All'interno del carico di lavoro, il server dei metadati restituisce su richiesta un token di identità firmato. Richiedilo con l'`audience` che intendi registrare lato Anthropic e includi `format=full` in modo che la risposta contenga il claim `email`:

    ```text wrap
    GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full
    Metadata-Flavor: Google
    ```

    Oppure, con la CLI gcloud:

    ```bash CLI
    gcloud auth print-identity-token \
      --audiences="https://api.anthropic.com" \
      --include-email
    ```

    Gli equivalenti per gli SDK sono mostrati in [Acquisire e usare il token](https://platform.claude.com/docs/it/manage-claude/wif-providers/gcp#acquire-and-use-the-token).

    Il payload del token decodificato ha questo aspetto:

    ```json
    {
      "iss": "https://accounts.google.com",
      "aud": "https://api.anthropic.com",
      "sub": "104892...",
      "azp": "104892...",
      "email": "inference-worker@my-project.iam.gserviceaccount.com",
      "email_verified": true,
      "exp": 1775527120
    }
    ```

    Il claim `sub` è l'ID univoco numerico opaco del service account Google. Il claim `email` è l'indirizzo leggibile del service account. Esegui la corrispondenza sia su `sub` che su `email` nella tua regola di federazione.
  </Tab>

  <Tab title="GKE con Workload Identity">
    Abilita [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) sul tuo cluster e associa il tuo service account Kubernetes a un service account Google con l'annotazione `iam.gke.io/gcp-service-account`:

    ```yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: inference-worker
      namespace: prod
      annotations:
        iam.gke.io/gcp-service-account: inference-worker@my-project.iam.gserviceaccount.com
    ```

    Con questa associazione attiva, il server dei metadati GKE restituisce un token firmato da Google identico al caso di Cloud Run e GCE: stesso emittente `https://accounts.google.com`, stesso claim `email`, stesso URL di recupero. Configura Anthropic esattamente come nella sezione successiva.

    Un token `format=full` da GKE include inoltre i claim `google.compute_engine.project_id`, `google.compute_engine.zone` e `google.compute_engine.instance_name`, a cui puoi fare riferimento nel matcher `condition` di una regola di federazione (un'espressione CEL come `claims.google.compute_engine.project_id == "my-project"`) per limitare l'accesso a un cluster o a un node pool specifico.

    <Note>
      Se non vuoi associare i service account Kubernetes ai service account Google, i pod GKE possono invece usare l'emittente OIDC proprio del cluster (`https://container.googleapis.com/v1/projects/PROJECT/locations/REGION/clusters/CLUSTER`) con un volume `serviceAccountToken` proiettato. Quel percorso usa un emittente per cluster anziché `accounts.google.com`. Consulta [Usare WIF con Kubernetes](https://platform.claude.com/docs/it/manage-claude/wif-providers/kubernetes) per quel modello.
    </Note>
  </Tab>
</Tabs>

## Configurare Anthropic

Nella Claude Console, vai alla pagina [Workload identity](https://platform.claude.com/settings/workload-identity-federation) (`platform.claude.com/settings/workload-identity-federation`), fai clic su **Connect workload** e seleziona il riquadro **Google Cloud**. La procedura guidata ti accompagna nella registrazione dell'emittente, nella creazione di un service account e nella creazione di una regola di federazione.

La procedura guidata crea queste risorse per te. Usa i seguenti valori sia che tu li inserisca nella procedura guidata sia che li invii all'[Admin API](https://platform.claude.com/docs/it/manage-claude/wif-admin-api):

**Emittente di federazione:** Google pubblica il proprio documento di discovery OIDC pubblicamente, quindi usa la modalità discovery. Questo singolo emittente copre ogni superficie Google Cloud (Cloud Run, GCE, Cloud Functions, App Engine e GKE con Workload Identity). Differenzia i carichi di lavoro con le regole, non con gli emittenti.

```json
{
  "name": "gcp",
  "issuer_url": "https://accounts.google.com",
  "jwks": { "type": "discovery" }
}
```

**Regola di federazione:** Esegui la corrispondenza sia sul claim `sub` che sul claim `email`. `email` è l'indirizzo leggibile del service account; `sub` è l'ID univoco numerico del service account, che Google non riutilizza mai, quindi fissarlo protegge la regola nel caso in cui il service account venga eliminato e ne venga successivamente creato uno nuovo con la stessa email. Trova l'ID univoco con `gcloud iam service-accounts describe SA_EMAIL --format='value(uniqueId)'`.

```json
{
  "name": "gcp-inference-worker",
  "issuer_id": "fdis_...",
  "match": {
    "audience": "https://api.anthropic.com",
    "claims": {
      "sub": "104892101234567890123",
      "email": "inference-worker@my-project.iam.gserviceaccount.com"
    }
  },
  "target": {
    "type": "service_account",
    "service_account_id": "svac_..."
  },
  "workspace_id": "wrkspc_...",
  "oauth_scope": "workspace:developer",
  "token_lifetime_seconds": 600
}
```

## Acquisire e usare il token

All'interno del tuo carico di lavoro Google Cloud, recupera il token di identità dal server dei metadati, scambialo su `POST /v1/oauth/token` e usa il bearer token restituito per chiamare la Claude API. L'SDK di Claude gestisce per te il ciclo di scambio e aggiornamento quando fornisci `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`), come mostrato negli esempi seguenti. Tranne che in Java (vedi il commento in quell'esempio), tale opzione accetta un token provider che restituisce un token di identità aggiornato dal server dei metadati.

<CodeGroup>
  ```bash cURL
  # Recupera dal server dei metadati il token di identità firmato da Google
  JWT=$(curl -sS -H "Metadata-Flavor: Google" \
    "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full")

  # Scambialo con un token di accesso Anthropic
  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)

  # Chiama la Claude API
  curl -sS 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 Cloud Run"}]
    }' | jq -r '.content[] | select(.type == "text") | .text'
  ```

  ```python Python
  import os
  import anthropic
  import google.auth.transport.requests
  import google.oauth2.id_token
  from anthropic import WorkloadIdentityCredentials

  AUDIENCE = "https://api.anthropic.com"


  def fetch_google_identity_token() -> str:
      request = google.auth.transport.requests.Request()
      return google.oauth2.id_token.fetch_id_token(request, AUDIENCE)


  client = anthropic.Anthropic(
      credentials=WorkloadIdentityCredentials(
          identity_token_provider=fetch_google_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 Cloud Run"}],
  )
  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";

  const METADATA_URL =
    "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full";

  async function fetchGoogleIdentityToken(): Promise<string> {
    const response = await fetch(METADATA_URL, {
      headers: { "Metadata-Flavor": "Google" }
    });
    return response.text();
  }

  const client = new Anthropic({
    credentials: oidcFederationProvider({
      identityTokenProvider: fetchGoogleIdentityToken,
      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 Cloud Run" }]
  });
  for (const block of message.content) {
    if (block.type === "text") {
      console.log(block.text);
    }
  }
  ```

  ```go Go
  const audience = "https://api.anthropic.com"

  googleIDToken := func(ctx context.Context) (string, error) {
  	creds, err := idtoken.NewCredentials(&idtoken.Options{Audience: audience})
  	if err != nil {
  		return "", err
  	}
  	tok, err := creds.Token(ctx)
  	if err != nil {
  		return "", err
  	}
  	return tok.Value, nil
  }

  client := anthropic.NewClient(
  	option.WithFederationTokenProvider(googleIDToken, 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 Cloud Run")),
  	},
  })
  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 java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;

  void main() throws Exception {
      HttpClient http = HttpClient.newHttpClient();
      HttpRequest metadataRequest = HttpRequest.newBuilder()
              .uri(URI.create("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full"))
              .header("Metadata-Flavor", "Google")
              .build();

      // L'SDK Java accetta il token stesso o un percorso di file, non una funzione; con
      // source("value"), path(...) contiene il token. L'SDK invia lo stesso token
      // a ogni aggiornamento del token di accesso Anthropic, il che fallisce una volta
      // scaduto o, se ha un claim jti, già usato. Per un processo di lunga durata,
      // passa source("file") e scrivi un nuovo token nel file prima di
      // ogni aggiornamento.
      String googleIdentityToken = http.send(metadataRequest, HttpResponse.BodyHandlers.ofString()).body();

      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(googleIdentityToken)
                                      .build())
                              .build())
                      .build()))
              .build();

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

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

  ```csharp C#
  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 MetadataTokenProvider(),
  });
  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 Cloud Run" }],
  });
  foreach (var block in message.Content)
  {
      if (block.Value is TextBlock textBlock)
      {
          Console.WriteLine(textBlock.Text);
      }
  }

  class MetadataTokenProvider : IIdentityTokenProvider
  {
      private const string METADATA_URL =
          "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full";

      private static readonly HttpClient httpClient = new()
      {
          DefaultRequestHeaders = { { "Metadata-Flavor", "Google" } },
      };

      public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
      {
          return await httpClient.GetStringAsync(METADATA_URL, ct);
      }
  }
  ```

  ```bash CLI
  # Scrivi il token di identità firmato da Google in un file leggibile dalla CLI
  ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
  curl -sS -H "Metadata-Flavor: Google" \
    "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full" \
    > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
  export ANTHROPIC_IDENTITY_TOKEN_FILE

  # ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  # ANTHROPIC_SERVICE_ACCOUNT_ID e ANTHROPIC_WORKSPACE_ID vengono letti dall'ambiente.
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello from Cloud Run"}'
  ```

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

  const METADATA_URL = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full';

  class GoogleIdentityToken implements IdentityTokenProvider
  {
      public function getIdentityToken(): string
      {
          $context = stream_context_create([
              'http' => ['header' => "Metadata-Flavor: Google\r\n"],
          ]);

          return file_get_contents(METADATA_URL, false, $context);
      }
  }

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

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

  ```ruby Ruby
  require "anthropic"
  require "net/http"

  METADATA_URL = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full"

  credentials = Anthropic::WorkloadIdentityCredentials.new(
    identity_token_provider: -> { Net::HTTP.get(URI(METADATA_URL), {"Metadata-Flavor" => "Google"}) },
    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"]
  )
  client = Anthropic::Client.new(credentials: credentials)

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

I token di identità Google scadono dopo circa un'ora. Tranne che in Java (vedi il commento in quell'esempio), l'SDK richiama nuovamente il token provider ed esegue di nuovo lo scambio automaticamente prima della scadenza. Per gli script shell che vengono eseguiti più a lungo del valore `expires_in` del token di accesso, esegui l'aggiornamento con un timer e ripeti lo scambio.

## Verificare la configurazione

Dall'interno del tuo carico di lavoro, decodifica il token di identità e conferma che i claim corrispondano alla tua regola:

```bash cURL
curl -sS -H "Metadata-Flavor: Google" \
  "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full" \
  | jq -rR 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson'
```

Verifica che `iss` sia `https://accounts.google.com`, che `aud` sia `https://api.anthropic.com` e che `email` corrisponda al valore nella tua regola di federazione. Quindi esegui lo scambio della sezione precedente. Uno scambio riuscito restituisce un `access_token` che inizia con `sk-ant-oat01-` e un valore `expires_in` in secondi. Se lo scambio fallisce con la risposta opaca `401` `authentication_error` (messaggio `Authentication failed`), controlla la [pagina della cronologia di autenticazione](https://platform.claude.com/settings/workload-identity-federation?tab=history) nella Claude Console per il motivo del rifiuto e consulta [Risolvere i problemi di uno scambio fallito](https://platform.claude.com/docs/it/manage-claude/wif-reference#troubleshoot-a-failed-exchange); la causa più comune sul lato Google Cloud è l'assenza del claim `email` (richiedi il token con `format=full` in modo che venga incluso).

## Limitare l'ambito della regola

<Warning>
  Il claim `sub` di Google è l'ID univoco numerico opaco del service account e non ha un prefisso stabile. Un `subject_prefix` con un `*` finale corrisponde a service account arbitrari in ogni progetto Google Cloud, e ognuno di essi potrebbe ottenere un token Anthropic federato.
</Warning>

Vincola il blocco `match` della regola all'ambito più ristretto adatto al tuo caso d'uso:

* **Fai corrispondere `sub` esattamente:** Imposta l'ID univoco numerico completo in `claims.sub` e non usare mai `subject_prefix` per i token Google.
* **Fissa il claim `email`:** Aggiungi `claims.email` accanto a `sub` in modo che debbano corrispondere sia l'ID stabile sia l'indirizzo leggibile.
* **Fissa l'audience:** Imposta `audience` sul valore esatto che richiedi al server dei metadati, in modo che i token emessi per altri consumatori vengano rifiutati.
* **Fissa il progetto su GKE:** Per i token `format=full`, aggiungi una `condition` come `claims.google.compute_engine.project_id == "my-project"` per limitare la regola ai nodi di un solo progetto.

## Passaggi successivi

* Leggi la pagina [Workload Identity Federation](https://platform.claude.com/docs/it/manage-claude/workload-identity-federation) per il modello completo delle risorse e la precedenza delle credenziali negli SDK.
* Aggiungi una regola di federazione separata per ogni ambiente (produzione, staging) in modo da poterne revocare una senza influire sulle altre.
