GitHub Actions OIDC Authentication

Deploying to a cloud provider requires credentials. Storing long-lived cloud keys as GitHub secrets creates risk: keys leak, expire, and need manual rotation. OpenID Connect, called OIDC, replaces stored keys with short-lived tokens that exist only for the length of a job.

OIDC in Simple Words

Imagine a visitor badge at an office. A permanent master key can open every door forever, and losing it is a disaster. A visitor badge opens specific doors, expires at the end of the day, and the front desk checks identity before issuing it. OIDC works like the visitor badge. GitHub proves who the workflow is, and the cloud issues a temporary pass.

Old Method Versus OIDC

FeatureStored Cloud KeysOIDC
LifetimeMonths or yearsMinutes
RotationManual workNot needed
Leak impactHighLow
ScopeOften broadLimited by repository and branch

The Handshake

+-------------+  1. request token  +------------------+
| Workflow    | -----------------> | GitHub OIDC      |
| job         | <----------------- | provider         |
+-------------+  2. signed token   +------------------+
      |
      | 3. present token
      v
+-------------+  4. check rules    +------------------+
| Cloud       | -----------------> | Trust policy     |
| provider    | <----------------- | (repo, branch)   |
+-------------+  5. approve        +------------------+
      |
      | 6. temporary credentials
      v
+-------------+
| Job deploys |
+-------------+

What Is Inside the Token

The signed token carries claims, which are facts about the job. The cloud provider reads them to decide whether to trust the request.

ClaimExample Value
sub (subject)repo:my-org/my-app:ref:refs/heads/main
repositorymy-org/my-app
refrefs/heads/main
environmentproduction
aud (audience)sts.amazonaws.com

Workflow Setup

The job needs the id-token: write permission. Without it, GitHub refuses to issue a token.

name: Deploy With OIDC

on:
  push:
    branches: [ main ]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Get temporary AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-deploy-role
          aws-region: us-east-1

      - name: Check identity
        run: aws sts get-caller-identity

The role address contains no secret. Anyone can read it, because the trust policy decides who may use the role.

Cloud Side Setup on AWS

Create the Identity Provider

In IAM, add an OpenID Connect provider with the address https://token.actions.githubusercontent.com and the audience sts.amazonaws.com.

Create a Role With a Trust Policy

The policy limits access to one repository and branch.

{
  "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:my-org/my-app:ref:refs/heads/main"
      }
    }
  }]
}

Attach a permissions policy to the role that grants only the deployment actions the job needs.

Other Cloud Providers

ProviderMechanismCommon Action
Microsoft AzureFederated credential on an app registrationazure/login
Google CloudWorkload identity federationgoogle-github-actions/auth
HashiCorp VaultJWT authentication methodhashicorp/vault-action

Tighten the Trust Policy

A loose policy defeats the purpose. A subject like repo:my-org/* lets every repository in the organization assume the role. Pin the exact repository. Pin a branch or an environment for production roles. Use separate roles for staging and production.

Troubleshooting

  • The error "Credentials could not be loaded" often means the id-token: write permission is missing.
  • An access denied message points to a subject mismatch. Compare the policy text with the actual claim.
  • Pull request workflows carry a different subject, such as repo:my-org/my-app:pull_request. Add that value to the policy only when needed.

Benefits in Practice

Teams delete cloud secrets from repository settings, remove rotation reminders, and shrink the damage from any leak. Audit logs in the cloud show exactly which repository and branch assumed each role.