GCP Workload Identity Federation for GitHub Actions: Keyless Authentication, OIDC Setup, and Service Account Impersonation

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Rotated a GCP service account JSON key that had been stored as a GitHub Actions secret for 18 months after a security review identified it as an unrotated credential with broad GCP permissions. The rotation required coordinating updates across 14 different GitHub repositories that referenced the same secret, updating the secret value in each, and testing that each deployment pipeline still worked after the rotation. The entire rotation took 4 hours and required temporary outages of several deployment workflows while the new key was being propagated.
Migrating all 14 repositories to Workload Identity Federation took 3 hours total — one hour to set up the Workload Identity Pool and provider, two hours to update each repository's workflow file to use the new auth action. After the migration, there are no credentials to rotate, no secrets to manage across repositories, and no risk of a leaked JSON key providing persistent access. The GitHub OIDC token that each workflow job uses to authenticate expires in 5 minutes and is job-specific. It is not reusable even if logged.
Setup: creating the trust relationship between GitHub and GCP
Workload Identity Federation setup creates a trust relationship through a chain of three resources: the Workload Identity Pool (the GCP container that holds external identity providers), the OIDC Identity Provider (the configuration that trusts GitHub's token issuer and maps OIDC claims to GCP attributes), and the service account IAM binding (the permission that allows the Workload Identity federated principal to impersonate a specific service account). All three must be correctly configured for the authentication chain to complete.
Create one shared Workload Identity Pool per GCP project that all repositories authenticate against rather than per-repository pools
Create a single Workload Identity Pool per GCP project rather than a separate pool per repository or team, and use attribute conditions on the pool's OIDC provider and service account IAM bindings to restrict which repositories can access which service accounts. A single pool simplifies the audit trail (all GitHub Actions authentication events are in one pool's logs), reduces configuration overhead (one pool setup rather than N per team), and makes attribute condition policy consistent. Scope access at the IAM binding level rather than the pool level: the pool is shared infrastructure, but each service account's principalSet:// IAM binding specifies the exact repository or repository owner that can impersonate it. This architecture scales to 50 repositories using the same pool with different service account access permissions without requiring additional pool configuration.
Test attribute condition rejections before enabling production deployments to verify branch restrictions work correctly
Verify that attribute conditions correctly reject unauthorized authentication attempts by deliberately testing rejection scenarios before removing the old JSON key authentication. Create a test branch in the repository and manually trigger the workflow from that branch — if the attribute condition restricts to main only, the workflow should fail at the auth step with an attribute condition message. Create a fork of the repository and attempt to run the workflow from the fork — the repository attribute condition should reject authentication from the fork's repository name. Verify that pull request runs are rejected if the condition restricts to merged branches: GitHub Actions OIDC tokens for pull request runs set assertion.ref to the PR merge ref, not the base branch, which means a condition checking assertion.ref=='refs/heads/main' correctly rejects pull request runs. Document each rejection test with the expected error message and the actual result before deploying to production, creating a record that the access controls function as designed.
Service account design: least privilege impersonation architecture
The service account that GitHub Actions impersonates through Workload Identity Federation should follow the same least privilege principles as any other service account — the convenience of keyless authentication does not change the requirement to limit what the authenticated identity can do. A Workload Identity-authenticated GitHub Actions job that impersonates a service account with editor or owner project-level roles gains broad GCP access with a legitimate authentication method, creating a supply chain attack risk if the GitHub Actions workflow is compromised.
Create separate service accounts for different deployment types: build, deploy, and infrastructure change operations each use a different service account
Create separate service accounts for distinct GitHub Actions workflow functions: a build service account with permissions to push container images to Artifact Registry and read from GCS build caches, a deploy service account with permissions to update Cloud Run services or GKE deployments in specific namespaces, and an infrastructure service account with permissions for Terraform-managed resource creation and modification. Each workflow job's impersonation target is the minimum-permission service account for that job's function. A workflow that builds and pushes a Docker image uses the build service account with roles/artifactregistry.writer on the specific repository, not a project-level role. A workflow that deploys a Cloud Run service uses the deploy service account with roles/run.developer on the specific service, not project-wide developer access. Separate service accounts also enable separate audit trails: build activity and deploy activity are distinguishable by the service account identity in Cloud Logging, making it straightforward to correlate which workflow run triggered which GCP resource change.
Configure workflow-level service account selection based on job purpose rather than repository-level defaults
Configure service account selection at the individual workflow job level rather than at the repository level, allowing different jobs within the same workflow to use different service accounts appropriate to each job's function. In the GitHub Actions workflow, the google-github-actions/auth action's service_account parameter can be a job-level input or environment variable set per job. A workflow with separate build and deploy jobs uses the build service account in the build job and the deploy service account in the deploy job, scoped by IAM conditions. The IAM binding for the deploy service account can include an attribute condition that restricts impersonation to workflows where the GitHub ref is refs/heads/main, preventing the deploy service account from being impersonated by a feature branch build job even if the same Workload Identity Pool is used. This fine-grained control is not achievable with JSON key authentication, where the key grants access regardless of which branch or job is using it.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
The bottom line
GCP Workload Identity Federation for GitHub Actions eliminates long-lived service account JSON keys from GitHub secrets by replacing static credentials with short-lived OIDC token exchange. Create a Workload Identity Pool and OIDC provider referencing GitHub's issuer URL with attribute mappings for repository, repository_owner, actor, and ref claims. Configure service account IAM bindings using principalSet:// member format with attribute.repository conditions to restrict impersonation to specific repositories. Add attribute conditions on the provider to further restrict authentication to specific branches or environments. Set id-token: write permission in the GitHub Actions workflow permissions block and configure the google-github-actions/auth action with the workload_identity_provider and service_account parameters. Test rejection scenarios before removing old JSON key authentication. Create separate service accounts per workflow function to maintain least privilege. Monitor Workload Identity authentication events in Cloud Logging and alert on impersonation from unexpected repository principals.
Frequently asked questions
How do I create a GCP Workload Identity Pool and OIDC provider for GitHub Actions?
Create the Workload Identity Pool and OIDC provider using gcloud commands that establish the trust relationship between GCP and GitHub's OIDC token issuer. First, create the pool: gcloud iam workload-identity-pools create github-pool --location=global --display-name='GitHub Actions Pool' --description='Workload Identity Pool for GitHub Actions authentication'. Second, create the OIDC provider within the pool: gcloud iam workload-identity-pools providers create-oidc github-provider --location=global --workload-identity-pool=github-pool --display-name='GitHub Actions Provider' --attribute-mapping='google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner,attribute.actor=assertion.actor,attribute.ref=assertion.ref' --issuer-uri='https://token.actions.githubusercontent.com'. The attribute mapping translates GitHub OIDC token claims to GCP attributes: google.subject becomes the unique principal identifier, attribute.repository stores the repository name for condition filtering, and attribute.ref stores the Git ref (branch/tag) for restricting deployments to specific branches. After creating the provider, retrieve the full Workload Identity Provider resource name for use in the GitHub Actions workflow: gcloud iam workload-identity-pools providers describe github-provider --location=global --workload-identity-pool=github-pool --format='value(name)'.
How do I configure the service account IAM binding to allow Workload Identity impersonation?
Configure the service account IAM binding by granting the roles/iam.workloadIdentityUser role to the Workload Identity Pool principal that represents the GitHub repository or set of repositories authorized to impersonate the service account. First, get the full Workload Identity Pool name: POOL_NAME=$(gcloud iam workload-identity-pools describe github-pool --location=global --format='value(name)'). Then create the IAM binding on the service account: gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT_EMAIL --role=roles/iam.workloadIdentityUser --member="principalSet://iam.googleapis.com/${POOL_NAME}/attribute.repository/YOUR_GITHUB_ORG/YOUR_REPO". The principalSet:// member format with the attribute.repository specifier restricts impersonation to only tokens from the specified GitHub repository — a token from any other repository cannot impersonate this service account even if it can authenticate to the Workload Identity Pool. For organization-wide access where multiple repositories need to impersonate the same service account, use the attribute.repository_owner specifier: principalSet://iam.googleapis.com/${POOL_NAME}/attribute.repository_owner/YOUR_GITHUB_ORG which allows any repository in the organization to impersonate the service account (broader and should only be used for shared infrastructure service accounts).
How do I configure the GitHub Actions workflow to authenticate with GCP Workload Identity?
Configure the GitHub Actions workflow by adding the id-token: write permission to the workflow or job, adding the google-github-actions/auth step with the Workload Identity provider and service account parameters, and ensuring subsequent steps use the authenticated GCP credentials. Add permissions to the workflow or job level: permissions: { id-token: write, contents: read }. Add the authentication step using the google-github-actions/auth action: - name: Authenticate to Google Cloud, uses: google-github-actions/auth@v2, with: { workload_identity_provider: projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/providers/github-provider, service_account: deploy-service-account@PROJECT_ID.iam.gserviceaccount.com }. After the auth step, subsequent steps that use gcloud CLI, gsutil, or GCP SDKs automatically use the authenticated credentials without any additional configuration — the auth action sets the GOOGLE_APPLICATION_CREDENTIALS environment variable pointing to a temporary credential file that all GCP tools recognize. Retrieve the PROJECT_NUMBER using gcloud projects describe PROJECT_ID --format='value(projectNumber)' as the Workload Identity provider URI requires the project number, not the project ID.
How do I restrict Workload Identity authentication to specific branches or environments?
Restrict Workload Identity authentication to specific branches or deployment environments using Workload Identity Provider attribute conditions that evaluate the GitHub OIDC token claims against allowed values before issuing GCP credentials. Add an attribute condition to the OIDC provider that restricts authentication to the main branch and repository-dispatched events from approved workflows: gcloud iam workload-identity-pools providers update-oidc github-provider --location=global --workload-identity-pool=github-pool --attribute-condition="attribute.repository=='YOUR_GITHUB_ORG/YOUR_REPO' && (assertion.ref=='refs/heads/main' || assertion.ref=='refs/heads/production')". This condition ensures that only GitHub Actions jobs running on the main or production branch can obtain GCP credentials — pull request runs, feature branch deployments, and draft workflow runs from other branches cannot authenticate. For environment-based restriction using GitHub Environments (which have required reviewer approval): set the GitHub Actions job's environment to production and add the assertion.environment claim to the attribute condition: assertion.environment=='production' to restrict GCP access to jobs explicitly running in the production environment. Test the attribute condition by deliberately running the workflow from a non-main branch and verifying that the auth step fails with an attribute condition error.
How do I migrate from service account JSON key authentication to Workload Identity Federation?
Migrate from service account JSON key authentication to Workload Identity Federation by setting up the Workload Identity Pool and provider alongside the existing key-based authentication, testing the new authentication in a parallel workflow, then switching the primary workflow and revoking the old key. Start by following the pool, provider, and IAM binding setup steps while the existing GOOGLE_APPLICATION_CREDENTIALS-based authentication remains in the workflow. Create a test workflow that uses the new google-github-actions/auth action with the Workload Identity parameters but only runs on a specific test branch or on manual trigger. Run the test workflow and verify that GCP API calls succeed with the Workload Identity credentials by checking that the subsequent gcloud commands return expected results. After confirming the Workload Identity authentication works, update the production workflow to replace the - uses: google-github-actions/auth step that uses a credentials_json parameter (the JSON key) with the new workload_identity_provider and service_account parameters. Test the updated production workflow with a non-critical deployment. After successful production deployment with Workload Identity authentication, delete the GitHub secret containing the service account JSON key and delete or disable the service account key in GCP IAM to prevent future use of the now-removed credential.
How do I debug Workload Identity Federation authentication failures in GitHub Actions?
Debug Workload Identity Federation authentication failures by examining the error message from the google-github-actions/auth step and checking each component of the authentication chain: OIDC token issuance, pool/provider configuration, and service account IAM binding. The most common errors and their causes: Error: google-github-actions/auth failed with: googleapi: Error 403: Unable to generate access token means the service account IAM binding is missing or incorrectly configured — verify that the principalSet:// member format exactly matches the Workload Identity Pool name and repository attribute. Error: google-github-actions/auth failed with: Error fetching OIDC token means the id-token: write permission is not set in the workflow permissions block. Error: Attribute condition is not met means the attribute condition on the provider rejects this token — check that the branch, repository, and environment in the condition match the workflow's actual context. Enable debug logging in the auth action by setting ACTIONS_STEP_DEBUG: true in the repository secrets, which causes the action to print the decoded OIDC token claims — compare the claim values against the attribute mapping and condition to identify the mismatch. Use gcloud iam workload-identity-pools operations list --location=global --workload-identity-pool=github-pool to review recent authentication attempts and their failure reasons in the GCP IAM logs.
How do I audit and monitor Workload Identity Federation usage in GCP?
Audit Workload Identity Federation usage by enabling GCP audit logging for IAM activities and querying Cloud Logging for service account impersonation events originating from the Workload Identity Pool. In Cloud Logging, filter for IAM service account impersonation events from the Workload Identity Pool: resource.type=service_account and protoPayload.authenticationInfo.principalEmail matching the Workload Identity Pool subject pattern. Each GitHub Actions job authentication appears as a Cloud Logging event with the Workload Identity Pool principal as the authentication identity and the impersonated service account as the principal email. The log entry includes the workflowRef, repository, and actor claims from the GitHub OIDC token, providing a complete audit trail linking each GCP API call to the specific GitHub Actions workflow run, repository, and actor that made it. Create a Cloud Logging alert that fires when a Workload Identity Pool principal impersonates a service account from an unexpected repository (attribute.repository not in the approved list), which indicates either a misconfigured attribute condition or an unauthorized repository attempting to use the Workload Identity Pool. Export Workload Identity authentication events to BigQuery using a Log Sink for long-term retention and compliance reporting.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
