Replace Long-Lived Cloud Secrets in GitHub Actions with OIDC
Swap static cloud access keys in GitHub Actions for short-lived OIDC tokens, with constraints, an AWS trust-policy example, and validation steps.
25 Jun 2026, 04:34 UTC

The decision: replace long-lived cloud credentials with OIDC
Your GitHub Actions workflow needs to deploy infrastructure or push artifacts to a cloud provider. The common approach is to store long-lived access keys—for example, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY—as encrypted repository secrets. Those credentials do not expire, are difficult to rotate on a schedule, and if leaked they give an attacker persistent access to your cloud account.
The useful takeaway is that GitHub Actions can issue short-lived OpenID Connect (OIDC) tokens for each job. Your cloud provider exchanges that token for temporary credentials, so you can remove static keys from GitHub entirely. OIDC stands for OpenID Connect, an identity layer that lets one system vouch for a user or job using a signed token instead of a password or key.
Constraints before you choose
- Confirm OIDC is available for your GitHub plan and repository visibility. Public repositories on GitHub Free generally support it; private repositories and self-hosted runners may depend on your plan.
- Verify that your target cloud service and region support OIDC federation. Not every service accepts
AssumeRoleWithWebIdentityor equivalent calls. - Forked pull requests and workflows from external contributors often cannot request OIDC tokens or may be restricted to read-only scopes.
Supported options compared
| Option | Credential type | Lifetime | Where it lives | Best for |
|---|---|---|---|---|
| Long-lived secrets | Static access key and secret key | Until manually rotated | GitHub Actions secrets, encrypted at rest | Legacy targets without OIDC support, one-off scripts |
| OIDC federated identity | Short-lived JWT exchanged for temporary role credentials | Minutes, per job | Not stored; issued at job runtime | AWS, Azure, or GCP targets with OIDC support, repeatable deployments |
Trade-offs
Security: OIDC shrinks the blast radius. A leaked GitHub repository does not expose a long-lived cloud key, and credentials expire when the job ends. The trade-off is configuration complexity: a misconfigured trust policy can either block legitimate deployments or grant a workflow more cloud access than intended.
Operational effort: Long-lived secrets are faster to set up initially but create ongoing rotation and audit work. OIDC requires a one-time cloud-side trust policy and a workflow change, after which there are no secrets to rotate.
Availability: OIDC is not universal. Some cloud services, older SDKs, or specific regions may still require static keys. Forked pull requests may also be unable to obtain tokens, which can break contribution workflows that depend on cloud deployment.
Implementation: AWS OIDC trust policy example
This example configures Amazon Web Services (AWS) to trust GitHub Actions OIDC tokens from a specific repository and branch. Run the AWS commands from a machine or administrative CI job with IAM permissions, such as iam:CreateOpenIDConnectProvider, iam:CreateRole, and iam:PutRolePolicy.
1. Create the OIDC provider in AWS IAM
Use the GitHub Actions OIDC issuer. Replace <OIDC_THUMBPRINT> with the server certificate thumbprint for token.actions.githubusercontent.com, which you can retrieve from the provider's TLS certificate before creating the resource.
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list <OIDC_THUMBPRINT>
Expected check: The command returns the provider ARN, for example arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com. Do not assume a specific ARN; verify it in the AWS IAM console.
2. Create an IAM role with a scoped trust policy
Replace ACCOUNT_ID, OWNER, and REPO with your values. The sub condition limits role assumption to jobs running on the main branch of that repository.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:OWNER/REPO:ref:refs/heads/main"
},
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}
]
}
Attach a permissions policy to the role for the specific cloud action you need, such as deploying to an S3 bucket or updating a Lambda function. Avoid wildcards like * unless the workflow genuinely requires broad access.
3. Update the GitHub Actions workflow
Add id-token: write to the job permissions so the runner can request an OIDC token. This example uses the official AWS action to exchange the token for temporary credentials.
name: Deploy via OIDC
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials from OIDC token
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::ACCOUNT_ID:role/GitHubActionsDeployRole
aws-region: us-east-1
- name: Confirm temporary identity
run: aws sts get-caller-identity
Where to run: The workflow runs in GitHub Actions. The setup commands above run locally or in a separate admin job with IAM write permissions.
Validate the exchange and audit the session
- Push the workflow to the
mainbranch and confirm the job completes. - Check that
aws sts get-caller-identityreturns an assumed-role ARN rather than a long-lived IAM user. - Review AWS CloudTrail for
AssumeRoleWithWebIdentityevents. Confirm the session issuer matches the GitHub OIDC provider ARN and the subject claim matches your repository and branch. - If validation succeeds, remove any long-lived cloud access keys previously used by this workflow from GitHub Actions secrets.
Limitations and rollback
OIDC tokens are issued per job and cannot be persisted between workflow runs. If a downstream process needs credentials outside the job, OIDC alone is not sufficient. Also, overly broad sub conditions such as repo:OWNER/REPO:* allow any branch in the repository to assume the role; narrow the claim to specific branches, environments, or tags as needed.
Because this procedure creates IAM resources and removes secrets, keep a rollback path ready:
- Re-create long-lived AWS access keys and store them as GitHub Actions secrets.
- Update the workflow to use
aws-access-key-idandaws-secret-access-keyinputs instead of OIDC. - Disable or delete the IAM role and OIDC provider only after confirming the secret-based workflow works.
Practical verification: After the OIDC workflow runs, inspect CloudTrail to confirm temporary sessions originate from your repository, then rotate or remove the static keys. If the OIDC job fails, revert the workflow file and confirm the previous secret-based deployment still succeeds.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.