šŸŽ‰ DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoysMay 23, 20265 min read
Share:Tweet

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:

  1. An IAM Identity Provider must exist in your AWS account for GitHub
  2. An IAM role must trust that identity provider
  3. The trust policy must match the GitHub Actions claims (repo, branch, etc.)
  4. 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

bash
# 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.com

If it doesn't exist, create it:

bash
aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1

Cause 2: Wrong thumbprint

The thumbprint must match GitHub's OIDC certificate. GitHub's current thumbprint (2026):

6938fd4d98bab03faadb97b34396831e3780aea1
1c58a3a8518e8759bf075b76b750d4f2df264fcd

Update the provider thumbprints:

bash
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 1c58a3a8518e8759bf075b76b750d4f2df264fcd

Cause 3: Wrong audience (client ID)

The OIDC provider must have sts.amazonaws.com as an audience.

bash
# 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:

bash
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.com

Error: 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:

bash
aws iam get-role \
  --role-name GitHubActionsRole \
  --query 'Role.AssumeRolePolicyDocument'

Correct trust policy format:

json
{
  "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

json
// 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

json
// 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:

bash
# Get your account ID
aws sts get-caller-identity --query Account

Error: Workflow Missing Permissions

Cause: Missing id-token: write permission

This is required for the workflow to request OIDC tokens from GitHub:

yaml
# 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@v4

If the permissions block is set at the workflow level without id-token: write, individual jobs inherit those restricted permissions:

yaml
# 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 it

Fix: Either add id-token: write at the workflow level, or don't set workflow-level permissions at all (defaults give id-token: write):

yaml
# 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

yaml
# 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 mode

Common 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.

bash
# Test DNS resolution on the runner
nslookup token.actions.githubusercontent.com
 
# Test connectivity
curl -I https://token.actions.githubusercontent.com

Fix: 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:

yaml
- 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:

json
{
  "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

yaml
# .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.

Newsletter

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

Comments