GitHub Actions OIDC to AWS Not Working ā Every Fix (2026)
Your GitHub Actions workflow can't authenticate to AWS using OIDC. You're getting 'Not authorized to perform sts:AssumeRoleWithWebIdentity' or token errors. Here's every cause and the exact fix for each one.
GitHub Actions OIDC authentication to AWS is the right way to deploy ā no long-lived AWS credentials in secrets. But when it breaks, the error messages are cryptic and the debugging path isn't obvious.
Here are every cause and the exact fix.
How GitHub Actions OIDC Works (Quick Recap)
GitHub Actions Job
ā (requests JWT token from GitHub's OIDC provider)
GitHub OIDC Provider (token.actions.githubusercontent.com)
ā (presents JWT to AWS)
AWS STS AssumeRoleWithWebIdentity
ā (validates against IAM Identity Provider)
Temporary AWS credentials
For this to work:
- An IAM Identity Provider must exist in your AWS account for GitHub
- An IAM role must trust that identity provider
- The trust policy must match the GitHub Actions claims (repo, branch, etc.)
- The workflow must request the correct permissions
Error: "Not authorized to perform sts:AssumeRoleWithWebIdentity"
This is the most common error. It means AWS rejected the assume-role request.
Cause 1: IAM Identity Provider doesn't exist or is misconfigured
# Check if the provider exists
aws iam list-open-id-connect-providers
# Should return something like:
# arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.comIf it doesn't exist, create it:
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1Cause 2: Wrong thumbprint
The thumbprint must match GitHub's OIDC certificate. GitHub's current thumbprint (2026):
6938fd4d98bab03faadb97b34396831e3780aea1
1c58a3a8518e8759bf075b76b750d4f2df264fcd
Update the provider thumbprints:
aws iam update-open-id-connect-provider-thumbprint \
--open-id-connect-provider-arn arn:aws:iam::ACCOUNT:oidc-provider/token.actions.githubusercontent.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1 1c58a3a8518e8759bf075b76b750d4f2df264fcdCause 3: Wrong audience (client ID)
The OIDC provider must have sts.amazonaws.com as an audience.
# Check audiences on your OIDC provider
aws iam get-open-id-connect-provider \
--open-id-connect-provider-arn arn:aws:iam::ACCOUNT:oidc-provider/token.actions.githubusercontent.com \
--query 'ClientIDList'Should return ["sts.amazonaws.com"]. If it shows something else:
aws iam add-client-id-to-open-id-connect-provider \
--open-id-connect-provider-arn arn:aws:iam::ACCOUNT:oidc-provider/token.actions.githubusercontent.com \
--client-id sts.amazonaws.comError: Trust Policy Not Matching
Even with the OIDC provider correct, the role's trust policy must match the exact claims in the JWT.
Check your role's trust policy:
aws iam get-role \
--role-name GitHubActionsRole \
--query 'Role.AssumeRolePolicyDocument'Correct trust policy format:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_ORG/YOUR_REPO:*"
}
}
}
]
}Common trust policy mistakes:
Mistake 1: Wrong subject format
// WRONG - this will never match
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo"
// CORRECT - includes the ref
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
// OR be specific about branch
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main"Mistake 2: Using StringEquals for sub when you need wildcard
// WRONG - StringEquals requires exact match
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
}
// CORRECT - use StringLike for wildcards
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
}Mistake 3: Wrong account ID in Federated ARN
The OIDC provider ARN must match your actual account:
# Get your account ID
aws sts get-caller-identity --query AccountError: Workflow Missing Permissions
Cause: Missing id-token: write permission
This is required for the workflow to request OIDC tokens from GitHub:
# WRONG - missing permissions
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
# CORRECT
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
contents: read # Required to checkout
steps:
- uses: aws-actions/configure-aws-credentials@v4If the permissions block is set at the workflow level without id-token: write, individual jobs inherit those restricted permissions:
# WRONG - workflow-level permissions override job-level
permissions:
contents: read # This removes id-token by default
jobs:
deploy:
# id-token: write is NOT available here even if you add itFix: Either add id-token: write at the workflow level, or don't set workflow-level permissions at all (defaults give id-token: write):
# If you set workflow-level permissions, include id-token
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
...Error: "Error: Credentials could not be loaded"
Cause: Wrong action version or config
# WRONG - old version without OIDC support
- uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::ACCOUNT:role/GitHubActionsRole
aws-region: us-east-1
# CORRECT - v4 with OIDC
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::ACCOUNT:role/GitHubActionsRole
aws-region: us-east-1
# Do NOT include aws-access-key-id or aws-secret-access-key
# Those switch it to static credentials modeCommon mistake: Having both role-to-assume AND aws-access-key-id set. Remove the static credentials entirely when using OIDC.
Error: "Unable to resolve host token.actions.githubusercontent.com"
This happens in self-hosted runners on private networks.
# Test DNS resolution on the runner
nslookup token.actions.githubusercontent.com
# Test connectivity
curl -I https://token.actions.githubusercontent.comFix: Ensure outbound HTTPS (port 443) access from the runner to token.actions.githubusercontent.com and sts.amazonaws.com.
Debugging OIDC Claims
Print the JWT claims in your workflow to see exactly what GitHub is sending:
- name: Debug OIDC token
run: |
# Get the OIDC token
TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq -r '.value')
# Decode the payload (middle section)
echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool
env:
ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${{ env.ACTIONS_ID_TOKEN_REQUEST_TOKEN }}
ACTIONS_ID_TOKEN_REQUEST_URL: ${{ env.ACTIONS_ID_TOKEN_REQUEST_URL }}The claims you'll see:
{
"sub": "repo:myorg/myrepo:ref:refs/heads/main",
"aud": "sts.amazonaws.com",
"iss": "https://token.actions.githubusercontent.com",
"repository": "myorg/myrepo",
"ref": "refs/heads/main",
"job_workflow_ref": "myorg/myrepo/.github/workflows/deploy.yml@refs/heads/main"
}The sub field is what you're matching in the trust policy. Match it exactly.
Working End-to-End Example
# .github/workflows/deploy.yml
name: Deploy to AWS
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
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
role-session-name: GitHubActions-${{ github.run_id }}
- name: Verify credentials
run: aws sts get-caller-identity
- name: Deploy
run: |
aws s3 sync ./dist s3://my-bucket/Quick Checklist
ā IAM OIDC provider exists for token.actions.githubusercontent.com
ā OIDC provider audience is "sts.amazonaws.com"
ā OIDC provider thumbprint is current
ā Role trust policy uses correct account ID
ā Trust policy sub condition uses StringLike (not StringEquals) for wildcards
ā Sub pattern includes the :* or specific :ref:refs/heads/branch suffix
ā Workflow has "id-token: write" permission
ā Using aws-actions/configure-aws-credentials@v4 (not v1/v2)
ā No static credentials mixed with OIDC config
Related: GitHub Actions CI/CD Pipeline Tutorial | How to Build a DevSecOps Pipeline
Affiliate note: AWS IAM OIDC federation is free. For advanced GitHub Actions workflows, GitHub Actions offers 2,000 free minutes/month for private repos.
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam ā just practical engineering content.
Related Articles
AWS CodeBuild Build Failing or Timing Out ā Fix Guide
CodeBuild exits with status 1, times out mid-build, or fails with cryptic phase errors. Here's how to diagnose DOWNLOAD_SOURCE, BUILD, and POST_BUILD failures with specific fixes.
AWS CodePipeline vs GitHub Actions ā Which CI/CD Tool to Use? (2026)
AWS CodePipeline and GitHub Actions both automate deployments. But they have very different strengths. Here's an honest comparison with real examples.
AWS IAM Permission Denied Errors ā How to Fix Every Variant (2026)
Getting 'Access Denied' or 'is not authorized to perform' errors in AWS? Here's how to diagnose and fix every IAM permission issue ā EC2, EKS, Lambda, S3, and CLI.