# google-github-actions/auth: Workload Identity Federation in two legs

> Set up google-github-actions/auth v3: direct or service-account Workload Identity Federation, the attribute condition, token_format and each error.

Source: https://latchkey.dev/learn/actions/google-github-actions-auth  
Updated: 2026-11-12

google-github-actions/auth exchanges the job's GitHub OIDC token at Google's Security Token Service, through a Workload Identity Pool provider you configure, and writes a credentials file that gcloud and the Google client libraries pick up in later steps. Name a `service_account` and that federated identity impersonates it; leave it out and you grant IAM roles to the pool's `principalSet` directly, which the README calls the preferred setup.

The usual story is a green auth step and a red step three lines later, or a red auth step whose message lists an endpoint and a status but no IAM role. Both make sense once you know there are two legs, that the action always runs the first one, and that it runs the second only when you ask for a token.

Every GitHub organization's tokens come from one issuer, which is why Google says a GitHub provider must carry an attribute condition that restricts it to your organization. Everything here is from v3.0.0 of the action, its source and troubleshooting doc, and Google's federation guide, read 2026-11-12.

## How the exchange works

The action requests a GitHub OIDC token whose audience defaults to `https://iam.googleapis.com/` followed by your provider's full name, and posts it to `https://sts.googleapis.com/v1/token`. STS checks the issuer, applies the provider's attribute mapping, evaluates its attribute condition and returns a federated token, exposed as the `auth_token` output.

Then it writes `gha-creds-*.json` into `$GITHUB_WORKSPACE` and exports `GOOGLE_APPLICATION_CREDENTIALS` and `CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE` pointing at it. The file holds the URL and bearer token for requesting a GitHub OIDC token, not a token itself, so a library can fetch a fresh GitHub token and repeat the exchange. With a `service_account`, the file also carries an impersonation URL, and the library calls IAM Credentials `generateAccessToken` when it first needs a token. That is why an impersonation failure usually surfaces in a later step as [Unable to acquire impersonated credentials](/learn/github-actions/oidc-gcp-unable-to-acquire-impersonated-credentials-in-ci), not in the auth step.

Two ordering rules follow from the file. `actions/checkout` must run first, because it would delete the file. And `gsutil` does not read it; use `gcloud storage`.

```.github/workflows/deploy.yml
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
        with:
          project_id: my-project
          workload_identity_provider: projects/123456789/locations/global/workloadIdentityPools/github/providers/my-repo
      - run: gcloud storage ls gs://my-bucket
```

## The pool, the provider and the attribute condition

Create a pool, then an OIDC provider in it with issuer `https://token.actions.githubusercontent.com`. Every claim you want to test must be mapped to an attribute first, and Google says that for GitHub you must add an attribute condition restricting tokens to your organization, because GitHub uses one issuer for every organization.

Google also recommends the numeric `repository_owner_id` and `repository_id` over the names, since a deleted organization or repository name can be claimed by someone else. Mapped attributes are limited to 127 bytes, and pool, provider and IAM changes can take up to 5 minutes to propagate.

```Terminal
gcloud iam workload-identity-pools create github \
  --project=my-project --location=global

gcloud iam workload-identity-pools providers create-oidc my-repo \
  --project=my-project --location=global \
  --workload-identity-pool=github \
  --issuer-uri=https://token.actions.githubusercontent.com \
  --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.repository_owner_id=assertion.repository_owner_id" \
  --attribute-condition="assertion.repository_owner_id == '1342004'"
```

> The required condition, the numeric-ID advice and the example owner ID `1342004` are from Google Cloud's Workload Identity Federation with deployment pipelines guide; the 127-byte limit and 5-minute propagation from the action's README and TROUBLESHOOTING.md. All read 2026-11-12.

## Direct federation or through a service account

Since v2 `service_account` is optional, and leaving it out selects Direct Workload Identity Federation. The trade-offs, from the README:

|  | Direct | Through a service account | credentials_json |
| --- | --- | --- | --- |
| Who gets IAM roles | `principalSet://iam.googleapis.com/<pool>/attribute.repository/<org/repo>` | The service account; the principalSet gets `roles/iam.workloadIdentityUser` on it | The service account |
| `access_token` or `id_token` outputs | No | Yes | Yes, with Token Creator on itself |
| Service support | Only services that accept `principalSet` identities | Services that accept a service account | Services that accept a service account |
| Long-lived secret | None | None | A key that does not expire by default |

## token_format and the outputs

By default the action generates no token beyond the federated one. Set `token_format: access_token` and it impersonates the service account inside the step and outputs an OAuth 2.0 token, which is what `docker login` to Artifact Registry needs with the username `oauth2accesstoken`. `access_token_lifetime` defaults to `3600s` and cannot exceed one hour unless the `constraints/iam.allowServiceAccountCredentialLifetimeExtension` policy allows up to 12. `token_format: id_token` needs `id_token_audience`, and ID tokens last 10 minutes.

Outputs: `project_id`, `credentials_file_path`, `auth_token` (always), `access_token` and `id_token` (only for their format). Asking for a token also moves the impersonation error into the auth step, which makes it easier to read.

```.github/workflows/deploy.yml
- id: auth
  uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
  with:
    token_format: access_token
    workload_identity_provider: projects/123456789/locations/global/workloadIdentityPools/github/providers/my-repo
    service_account: deployer@my-project.iam.gserviceaccount.com
- uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
  with:
    registry: us-docker.pkg.dev
    username: oauth2accesstoken
    password: ${{ steps.auth.outputs.access_token }}
```

## The errors, and which leg raised them

Everything the action raises is prefixed `google-github-actions/auth failed with: `. Read the next clause: it names the leg. `workload_identity_provider` and `credentials_json` are mutually exclusive, and the action fails if you pass both or neither. With `credentials_json`, minify the key to one line before storing it, because GitHub masks a secret line by line. The audience leg has its own page: [workload identity audience errors](/learn/github-actions/google-auth-workload-identity-audience).

| Message | Leg | Check |
| --- | --- | --- |
| `GitHub Actions did not inject $ACTIONS_ID_TOKEN_REQUEST_TOKEN or $ACTIONS_ID_TOKEN_REQUEST_URL into this job.` | Token request | `id-token: write`, or a fork pull request |
| `Failed to generate Google Cloud federated token for //iam.googleapis.com/projects/...` | STS | Provider name with the project number, attribute condition, mapping, casing |
| `Failed to generate Google Cloud OAuth 2.0 Access Token for <service account>` | Impersonation, in the step | `roles/iam.workloadIdentityUser` for the right principalSet |
| `Unable to acquire impersonated credentials` in a later step | Impersonation, deferred | The same binding; this is the library reporting it |
| `The size of mapped attribute exceeds the 127 bytes limit.` | STS | A long repository or branch name in a mapped claim |
| `The access token lifetime cannot exceed 3600 seconds.` | Impersonation | Clock skew; try `access_token_lifetime: 3300s` |

> Messages from src/main.ts, src/client/workload_identity_federation.ts and src/client/iamcredentials.ts at v3.0.0 and from docs/TROUBLESHOOTING.md, read 2026-11-12.

## FAQ

### Should I use direct Workload Identity Federation or a service account?

Direct if every service you call accepts `principalSet` identities and you do not need an access token output; the README prefers it. Use a service account when you need `token_format` (for a registry login, say) or a service that does not support principal sets.

### Why does gcloud fail with "Unable to acquire impersonated credentials" after the auth step passed?

Without `token_format`, the auth step only runs the STS leg and writes a file; gcloud performs the impersonation later and gets the 403. Grant the provider's principalSet `roles/iam.workloadIdentityUser` on the service account, then add `token_format: access_token` if you want the failure reported in the auth step instead.

### Why is gsutil not authenticated after google-github-actions/auth?

The README says `gsutil` does not use the credentials the action exports. Switch the commands to `gcloud storage`, which reads `CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE`.

### What is the audience for google-github-actions/auth?

By default the action requests a GitHub token with audience `https://iam.googleapis.com/` plus the full provider name, which is what a provider with default allowed audiences expects. Set `audience` only if you configured custom allowed audiences on the provider.

## References

- [google-github-actions/auth README and action.yml at v3.0.0 (read 2026-11-12)](https://github.com/google-github-actions/auth/blob/v3.0.0/README.md)
- [google-github-actions/auth docs/TROUBLESHOOTING.md](https://github.com/google-github-actions/auth/blob/v3.0.0/docs/TROUBLESHOOTING.md)
- [google-github-actions/auth src/client/workload_identity_federation.ts at v3.0.0](https://github.com/google-github-actions/auth/blob/v3.0.0/src/client/workload_identity_federation.ts)
- [Google Cloud: Workload Identity Federation with deployment pipelines](https://cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines)
- [GitHub docs: OpenID Connect](https://docs.github.com/en/actions/concepts/security/openid-connect)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
