Workload Identity Federation: OIDC-based Access for External Workloads
A production-focused guide to Workload Identity Federation: OIDC-based Access for External Workloads on Google Cloud Platform..
20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.
- ✓Google Cloud project with billing enabled, gcloud CLI (version 400+), jq, curl, GitHub account with admin access to a repository, basic understanding of OIDC and IAM.
Workload Identity Federation: OIDC-based Access for External Workloads is like having a specialized tool that handles workload identity federation so you don't have to build and manage it yourself — it just works out of the box with Google Cloud's infrastructure.
You just rotated a service account key for the third time this month, and a developer accidentally committed the new one to a public repo. Sound familiar? Static keys are a ticking time bomb—they leak, expire, and require manual rotation. Workload Identity Federation (WIF) with OIDC is the kill switch. It lets external workloads (GitHub Actions, GitLab CI, AWS Lambda) impersonate a Google Cloud service account using a short-lived token from an external identity provider. No keys, no secrets, no rotation. In this article, we'll build a production-grade OIDC federation from scratch, covering token exchange, attribute mapping, and the gotchas that will bite you in production.
Why Static Keys Are a Security Anti-Pattern
Service account keys are long-lived secrets that grant powerful access to Google Cloud resources. They're often stored in CI/CD secrets, config files, or worse—committed to source control. A single leaked key can lead to data exfiltration, resource hijacking, or a massive cloud bill. Even with rotation policies, keys remain valid for hours or days, giving attackers a wide window. Workload Identity Federation replaces these static keys with ephemeral tokens that expire in minutes. The external workload presents an OIDC token from its identity provider (e.g., GitHub's OIDC issuer), and Google Cloud exchanges it for a short-lived access token. No secrets to manage, no rotation to schedule. This is the gold standard for cross-cloud and CI/CD access.
OIDC Token Exchange: The Core Mechanism
Workload Identity Federation relies on the OIDC token exchange flow defined in RFC 8693. The external workload obtains an OIDC token from its identity provider (e.g., GitHub, GitLab, AWS STS). This token contains claims like sub, aud, and issuer. The workload then calls the Google Cloud Security Token Service (STS) endpoint https://sts.googleapis.com/v1/token to exchange the OIDC token for a Google Cloud access token. The STS validates the token against a workload identity pool and provider, applies attribute mappings, and returns a short-lived access token (typically 1 hour). The workload can then use this token to call Google Cloud APIs. The entire exchange happens without any static secrets—just the OIDC token itself.
aud claim matches your expected audience. In GitHub Actions, the aud defaults to https://github.com/<org>; set it explicitly in your workflow.Setting Up a Workload Identity Pool and Provider
A workload identity pool is a logical grouping of external identities. Within each pool, you define providers that map to specific OIDC issuers (e.g., https://token.actions.githubusercontent.com for GitHub Actions). The provider configuration includes attribute mappings that extract claims from the OIDC token and map them to Google Cloud attributes like google.subject and attribute.repository. These attributes are used in IAM policies to grant fine-grained access. For example, you can allow only workflows from a specific repository to impersonate a service account. The setup involves creating the pool, adding the provider, and configuring attribute conditions.
google.subject to a unique claim like assertion.sub (which includes the repo and run ID). This ensures each workflow run gets a unique principal for auditing.ref == 'refs/heads/main' for production access.Granting IAM Permissions to External Identities
Once the pool and provider are set up, you grant IAM roles to the external identities using the principal identifier principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/attribute.repository/REPO. This binds the external identity to a service account via the roles/iam.workloadIdentityUser role. The service account then has its own IAM roles (e.g., roles/storage.objectAdmin). The external workload impersonates the service account, inheriting its permissions. This two-step delegation ensures that the external identity never directly holds GCP roles—only the service account does.
principalSet://.../attribute.repository/* grants access to all repositories in your org. Always scope to specific repos or branches.Configuring GitHub Actions for OIDC Federation
To use WIF from GitHub Actions, you need to configure the workflow to request an OIDC token from GitHub and exchange it for GCP credentials. The google-github-actions/auth action handles the token exchange automatically. You must set permissions: id-token: write in the workflow to allow the job to request the OIDC token. The action uses the workload identity provider and service account you configured. No secrets are stored—the OIDC token is generated by GitHub and validated by GCP.
@v2) to avoid breaking changes. Use Dependabot to keep them updated.id-token: write only on jobs that need GCP access. Overly permissive permissions can lead to token leakage in logs.google-github-actions/auth action for seamless integration.Attribute Mapping and Conditions: Fine-Grained Access Control
Attribute mappings translate OIDC claims into Google Cloud attributes. For example, attribute.repository=assertion.repository maps the GitHub repository to a custom attribute. You can then use attribute conditions to restrict access based on these attributes. Conditions are CEL (Common Expression Language) expressions evaluated against the OIDC token claims. For instance, assertion.ref == 'refs/heads/main' ensures only pushes to main branch can impersonate the service account. This enables environment-specific access without managing multiple pools.
gcloud iam workload-identity-pools providers test-iam-conditions before deploying to production.Auditing and Monitoring OIDC Token Exchanges
Every OIDC token exchange is logged in Cloud Audit Logs under the sts.googleapis.com service. You can view who exchanged a token, which pool and provider were used, and the resulting access token's scope. This is critical for security incident response. Set up log-based metrics and alerts for failed exchanges (e.g., invalid token, expired token, or attribute condition mismatch). Additionally, monitor the iam.googleapis.com/impersonation logs to see which service account was impersonated and by which external identity.
protoPayload.status.code != 0 and set up an alert to notify the security team within minutes of a failed exchange.Handling Token Expiry and Refresh
The access token obtained from STS is valid for 1 hour by default. For long-running jobs, you need to refresh the token before it expires. The google-github-actions/auth action handles this automatically by caching the token and refreshing it when needed. For custom scripts, you can implement a refresh loop that calls the STS endpoint again with a new OIDC token. Note that the OIDC token itself is short-lived (typically 5-10 minutes for GitHub Actions), so you must request a new one if the job runs longer. In GitHub Actions, the OIDC token is available only during the job; for longer runs, consider splitting into multiple jobs.
Troubleshooting Common OIDC Federation Issues
Common issues include: (1) OIDC token not having the correct aud claim—ensure your workflow sets audience in the auth action. (2) Attribute condition mismatch—test conditions with gcloud iam workload-identity-pools providers test-iam-conditions. (3) Service account not having roles/iam.workloadIdentityUser—verify the IAM binding. (4) Token expired—check the OIDC token expiry (GitHub tokens last 5 minutes). (5) Wrong pool provider audience—ensure the audience matches exactly. Use the gcloud auth print-access-token command to test the exchange manually.
https://github.com/<org>; set it explicitly in the auth action with audience: //iam.googleapis.com/....test-iam-conditions command in CI/CD to validate attribute conditions before deploying changes to production.Migrating from Service Account Keys to OIDC Federation
Migrating existing workflows from static keys to OIDC federation involves: (1) Creating the workload identity pool and provider. (2) Granting the roles/iam.workloadIdentityUser role to the external identity. (3) Updating CI/CD configuration to use the auth action instead of gcloud auth activate-service-account --key-file. (4) Removing the key file from secrets. (5) Rotating and deleting the old service account key. Test the migration with a non-production workflow first. Monitor audit logs to ensure no workflows are still using the old key.
Advanced: Custom OIDC Providers (e.g., AWS, GitLab, Custom IdP)
Workload Identity Federation supports any OIDC-compliant identity provider. For AWS, you can use the AWS STS GetCallerIdentity endpoint to obtain an OIDC token. For GitLab, the issuer is https://gitlab.com. For custom IdPs, you need to provide the issuer URI and configure attribute mappings accordingly. The setup is similar to GitHub: create a pool, add a provider with the issuer URI, and map claims. The key difference is the token format and claims available. Always validate the iss and aud claims in the provider configuration.
aws sts get-caller-identity with a web identity token from an OIDC provider, or use the AWS Security Token Service to generate a token for federation.sub claim that can be mapped to google.subject for auditability.Production Best Practices and Gotchas
- Least privilege: Grant only the roles needed. Use separate service accounts for different workloads. 2. Attribute conditions: Always restrict by repository and branch. 3. Audit: Enable audit logs and set up alerts for failed exchanges. 4. Token lifetime: Set the STS token lifetime to the minimum required (e.g., 30 minutes). 5. Provider hardening: Use
attribute-conditionto validateissandaudclaims. 6. Key deletion: After migration, delete all service account keys. 7. Testing: Usegcloud iam workload-identity-pools providers test-iam-conditionsto validate conditions. 8. Monitoring: Monitor foriam.googleapis.com/impersonationlogs to detect unusual impersonation patterns.
google.iam.admin.v1.CreateServiceAccountKey events.iam.disableServiceAccountKeyCreation). This enforces WIF adoption across your organization.GKE Workload Identity vs External WIF: Don't Confuse Them
There are two distinct Workload Identity Federation features in GCP—and confusing them causes real problems. External WIF (what this article covers) is for workloads outside GCP: GitHub Actions, GitLab CI, AWS Lambda, Azure VMs. It uses google_iam_workload_identity_pool and google_iam_workload_identity_pool_provider Terraform resources. GKE Workload Identity is a separate feature that lets Kubernetes service accounts (KSAs) in GKE clusters impersonate Google service accounts. It uses an automatically managed pool at PROJECT_ID.svc.id.goog. The mechanisms differ: GKE WIF uses the cluster's metadata server, external WIF uses the STS API. You can use both in the same project for different use cases. Production insight: we once had an engineer spend two days debugging an external WIF setup that was actually a GKE cluster—they were using the wrong pool type. The pool in GKE is automatic; external pools are manually created. Know which one you need.
Cost: Workload Identity Federation Is Free
Workload Identity Federation itself incurs no additional cost. There is no charge for creating workload identity pools, providers, or performing token exchanges via the STS API. You only pay for the Google Cloud APIs and services that the federated identity calls. This makes WIF a strictly better choice than service account keys from a cost perspective—keys have no direct cost either, but the operational overhead of rotation, auditing, and breach remediation is significant. Production insight: the only cost consideration is the additional Cloud Audit Log volume from STS token exchanges, which is negligible for most workloads. At scale (millions of exchanges per day), you might see a small increase in logging costs. But this is far outweighed by the security benefits.
Cross-Project Access with WIF
A workload identity pool in project A can grant access to resources in project B. The IAM binding is on the target resource (or project), not on the pool. For example, a GitHub Actions workflow authenticates to project A's pool, gets a token, and can access Cloud Storage in project B if the service account has the necessary IAM roles in project B. This enables centralized identity management across multiple projects. The pool is tied to the project where it's created, but the federated identity can be granted access anywhere in the organization. Production insight: use a dedicated 'identity project' for your workload identity pools. Grant the federated identities access to resource projects via cross-project IAM bindings. This keeps identity management centralized and resource access decentralized.
| File | Command / Code | Purpose |
|---|---|---|
| check-key-usage.sh | for sa in $(gcloud iam service-accounts list --format='value(email)'); do | Why Static Keys Are a Security Anti-Pattern |
| token-exchange.sh | OIDC_TOKEN=${GITHUB_TOKEN} | OIDC Token Exchange |
| setup-pool.sh | PROJECT_ID="my-project" | Setting Up a Workload Identity Pool and Provider |
| grant-iam.sh | PROJECT_NUMBER="123456789" | Granting IAM Permissions to External Identities |
| .github | name: Deploy to GCP | Configuring GitHub Actions for OIDC Federation |
| update-attribute-condition.sh | gcloud iam workload-identity-pools providers update-oidc "github-provider" \ | Attribute Mapping and Conditions |
| query-audit-logs.sh | gcloud logging read 'resource.type=sts_service AND protoPayload.methodName="goog... | Auditing and Monitoring OIDC Token Exchanges |
| refresh_token.py | def get_access_token(oidc_token, pool_provider): | Handling Token Expiry and Refresh |
| test-exchange.sh | POOL_PROVIDER="projects/123456789/locations/global/workloadIdentityPools/github-... | Troubleshooting Common OIDC Federation Issues |
| .github | name: Migrate to WIF | Migrating from Service Account Keys to OIDC Federation |
| setup-aws-provider.sh | POOL_ID="aws-pool" | Advanced |
| delete-keys.sh | SA_EMAIL="ci-deploy@my-project.iam.gserviceaccount.com" | Production Best Practices and Gotchas |
| wif-comparison.txt | External WIF (this guide): | GKE Workload Identity vs External WIF |
| cost.txt | Workload Identity Federation: | Cost |
| cross-project-wif.sh | gcloud iam service-accounts add-iam-policy-binding \ | Cross-Project Access with WIF |
Key takeaways
Common mistakes to avoid
3 patternsIgnoring gcp workload identity federation best practices
Over-provisioning without rightsizing analysis
Skipping IAM least-privilege principles
Interview Questions on This Topic
What is Workload Identity Federation: OIDC-based Access for External Workloads and when would you use it in production?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.
That's Google Cloud. Mark it forged?
6 min read · try the examples if you haven't