Opsio - Cloud and AI Solutions
DevOpsCI/CDSecurity7 min readΒ· 1,337 words

GitHub Actions OIDC Federation: Eliminate Long-Lived Cloud Credentials

Published: Β·Updated: Β·Reviewed by Opsio Engineering Team
Fredrik Karlsson

Group COO & CISO

Operational excellence, governance, and information security. Aligns technology, risk, and business outcomes in complex IT environments

GitHub Actions OIDC Federation: Eliminate Long-Lived Cloud Credentials

The fastest way to leak production AWS credentials in 2026 is still the same as it was in 2018: copy a long-lived access key into a CI secret, forget about it, and let it sit there for three years until somebody screenshots a build log. Every major cloud provider's threat-intel report names CI/CD-stored static credentials as a top-five initial-access vector. The good news: the fix has been generally available since 2021 and costs nothing to deploy. OIDC federation lets GitHub Actions assume short-lived cloud roles using a per-job identity token, with no static secret stored anywhere.

This article walks through what OIDC federation actually is, how to wire it up across AWS, Azure, and GCP, and the five misconfigurations that turn a "secure" OIDC setup into a wide-open trust relationship. It assumes you already operate workflows on github actions delivery and you're moving away from long-lived secrets.

What OIDC Federation Actually Is

OpenID Connect (OIDC) is a standard for identity assertion built on top of OAuth 2.0. In the GitHub Actions context, three actors are involved:

  • The OIDC issuer (GitHub) β€” signs and serves identity tokens that describe the running workflow
  • The relying party (your cloud provider) β€” verifies the token's signature against GitHub's published JWKS and grants access if the claims match a trust policy
  • The workflow β€” requests the token at runtime and exchanges it for cloud credentials

Each token is per-job, signed by GitHub's private key, and carries claims describing the run: repository, repository_owner, ref, sha, workflow, job_workflow_ref, environment, actor, and more. The cloud's trust policy verifies these claims before issuing credentials. There is no shared secret. The token expires in 5 minutes; the cloud credentials it exchanges for typically expire in 1 hour.

The Workflow-Side Setup

Two requirements apply to every workflow that uses OIDC. First, the workflow must request the id-token: write permission. Second, the cloud-provider Action must be the one that exchanges the token β€” do not roll your own token-exchange logic.

name: deploy
on: { push: { branches: [main] } }

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/gha-prod-deploy
          aws-region: eu-west-1
      - run: aws sts get-caller-identity

The id-token: write permission is what entitles the runner to call GitHub's OIDC token endpoint. Without it, the cloud-provider Action's request returns 403 with a clear error. Setting permissions: at the workflow level is fine for single-purpose workflows; in mixed workflows, scope the permission to the specific job that needs it.

Free Expert Consultation

Need expert help with github actions oidc federation?

Our cloud architects can help you with github actions oidc federation β€” from strategy to implementation. Book a free 30-minute advisory call with no obligation.

Solution ArchitectAI ExpertSecurity SpecialistDevOps Engineer
50+ certified engineersAWS Advanced Partner24/7 support
Completely free β€” no obligationResponse within 24h

AWS: IAM Identity Provider plus Role Trust Policy

On AWS, the relying-party setup is a one-time IAM resource pair: an OIDC identity provider for GitHub, and a role with a trust policy that conditions on the GitHub OIDC token claims.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
        "token.actions.githubusercontent.com:sub": "repo:opsio/platform:environment:production"
      }
    }
  }]
}

The sub claim is the security boundary. The format is repo:OWNER/REPO:ENVIRONMENT|ref:REF|pull_request. Pinning to environment:production means only workflows running with environment: production in the job spec can assume this role β€” and that environment carries required-reviewer protection at the GitHub side. This is the strongest default subject claim for production roles.

Azure: Workload Identity Federation on Entra ID

On Azure, the relying-party setup is a federated identity credential on an Entra ID app registration or managed identity.

az ad app federated-credential create \
  --id $APP_ID \
  --parameters '{
    "name": "github-prod",
    "issuer": "https://token.actions.githubusercontent.com",
    "subject": "repo:opsio/platform:environment:production",
    "audiences": ["api://AzureADTokenExchange"]
  }'

The Azure subject claim format is identical to AWS. The audience is fixed at api://AzureADTokenExchange. The Action handles the rest:

- uses: azure/login@v2
  with:
    client-id: ${{ vars.AZURE_CLIENT_ID }}
    tenant-id: ${{ vars.AZURE_TENANT_ID }}
    subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

GCP: Workload Identity Pools and Providers

On GCP, the relying-party setup is a Workload Identity Pool containing a Workload Identity Provider (the GitHub OIDC issuer) plus a service-account binding that allows the federated identity to impersonate a GCP service account.

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

gcloud iam service-accounts add-iam-policy-binding gha-deploy@my-proj.iam.gserviceaccount.com \
  --role=roles/iam.workloadIdentityUser \
  --member="principalSet://iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/gh-pool/attribute.repository/opsio/platform"

The attribute-condition is the GCP equivalent of AWS's StringEquals on sub. Conditioning on assertion.repository alone is the loosest production policy you should accept; tighter policies condition on assertion.environment or assertion.ref as well.

The Five Misconfigurations That Defeat OIDC

OIDC federation is only as strong as its trust policy. The five mistakes that turn a "secure" setup into a wide-open trust relationship:

  1. Wildcard subject claims β€” repo:opsio/* or repo:opsio/platform:* grants any workflow in any repo (or in the platform repo from any branch) full access. Always pin to a specific ref, environment, or job_workflow_ref
  2. Missing audience check β€” the audience claim defaults to a generic value if not configured. AWS, Azure, and GCP each use a specific audience; configure it explicitly and verify it on the relying-party side
  3. Trusting any branch β€” StringLike on sub with :ref:refs/heads/* gives every feature-branch workflow production access. Production roles should pin to environment:production only
  4. Pull-request subject reuse β€” pull_request workflows from forks have sub values like repo:owner/repo:pull_request. Trusting that pattern lets any external contributor's PR assume your role on any pull-request workflow run
  5. One role for everything β€” using a single high-privilege role for staging and production deploys, dependency updates, and ad-hoc workflows defeats the per-environment isolation OIDC enables. Use one role per logical scope

Migration: How to Move Off Static Secrets

For an existing estate of workflows using long-lived AWS_ACCESS_KEY_ID/AZURE_CREDENTIALS/GCP_SA_KEY secrets, the migration follows a predictable pattern:

  1. Inventory every workflow secret with cloud-provider credentials. The GitHub REST API at /repos/:owner/:repo/actions/secrets lists them; you cannot read values, but the names and last-modified dates are enough
  2. Categorise by environment and minimum scope. Most static-secret estates use one or two over-privileged roles; the OIDC migration is a chance to split them
  3. Provision the OIDC identity provider and per-environment roles in each cloud account
  4. Migrate workflows one job at a time, replacing the static-secret env: block with the cloud-provider Action and id-token: write
  5. Rotate (then delete) the original IAM access keys, service principal secrets, or service-account JSON keys

For a typical mid-market estate (50-200 workflows), this migration is 2-4 weeks of focused work, much of which is testing rather than configuration. The upside is that after migration, the cloud-side audit trail (CloudTrail, Azure Activity Log, Cloud Audit Logs) carries the GitHub workflow context per call β€” every API action is traceable to the exact workflow run that made it.

How Opsio Helps

Opsio's security practice runs OIDC migrations as part of broader CI/CD security engagements. We typically deliver the federation setup, trust-policy hardening, and secrets sweep in 4-8 weeks alongside our wider Opsio's cloud security work. For organisations operating Opsio's managed detection, the migrated estate's audit-trail richness becomes a meaningful uplift in detection quality. Customers who want the workflow design and federation work bundled into a single managed offering use our end-to-end github actions, which includes OIDC federation by default in every engagement.

About the Author

Fredrik Karlsson
Fredrik Karlsson

Group COO & CISO at Opsio

Operational excellence, governance, and information security. Aligns technology, risk, and business outcomes in complex IT environments

Editorial standards: This article was written by a certified practitioner and peer-reviewed by our engineering team. We update content quarterly to ensure technical accuracy. Opsio maintains editorial independence β€” we recommend solutions based on technical merit, not commercial relationships.