OIDC: Deploy to the Cloud Without Long-Lived Secrets
Swap static cloud keys for short-lived OIDC tokens that GitHub mints per run and the cloud verifies.
Storing an AWS access key or a GCP service-account JSON as a repository secret works, but it is a standing liability: the credential is long-lived, powerful, and one leak away from disaster. OpenID Connect (OIDC) removes it entirely.
How it works
- GitHub mints a short-lived, signed OIDC token for the workflow run, describing the repo, branch, and environment.
- Your cloud provider is configured to trust GitHub's OIDC issuer and to accept tokens matching specific claims (e.g.
repo:acme/app:ref:refs/heads/main). - The cloud exchanges that token for temporary credentials scoped to a role β valid for minutes, not forever.
What you need
permissions: id-token: writeon the job so it may request the token.- A trust policy / workload-identity binding on the cloud side that pins your repo and branch.
- The provider's login action (e.g.
aws-actions/configure-aws-credentials) with a role ARN instead of keys.
Example
name: Deploy to AWS
on:
push:
branches: [main]
permissions:
id-token: write # required to request the OIDC token
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# No AWS keys anywhere β assume a role via OIDC
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/gh-deploy
aws-region: us-east-1
- run: aws s3 sync ./dist s3://my-bucket --deleteWhen to use it
- A team configures AWS OIDC federation so GitHub Actions can deploy to ECS using short-lived tokens instead of storing long-lived AWS access keys as secrets.
- A developer connects GitHub Actions to Google Cloud using Workload Identity Federation so no service-account JSON key is ever stored in GitHub.
- A security engineer removes all long-lived cloud credentials from GitHub secrets after enabling OIDC, reducing the attack surface for secret leaks.
More examples
Assume AWS IAM role with OIDC
The id-token: write permission allows the runner to request a JWT; configure-aws-credentials exchanges it for temporary AWS credentials.
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
aws-region: us-east-1
- run: aws s3 sync dist/ s3://my-bucket/Authenticate to GCP with Workload Identity
The auth action exchanges the GitHub OIDC token for a GCP access token bound to the specified service account, with no JSON key needed.
permissions:
id-token: write
contents: read
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123/locations/global/workloadIdentityPools/github/providers/github
service_account: [email protected]
- uses: google-github-actions/deploy-cloudrun@v2
with:
service: my-api
image: gcr.io/my-project/my-api:latestConfigure AWS trust policy for OIDC
This IAM trust policy allows only workflows running on the main branch of the specific repository to assume the role, scoping access precisely.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789: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:org/repo:ref:refs/heads/main"
}
}
}]
}
Discussion