Introduction

OpenID Connect (OIDC) authentication between GitHub Actions and AWS provides a secure way to access AWS resources without storing long-lived credentials in GitHub. Instead of static AWS access keys, GitHub Actions generates short-lived OIDC tokens that AWS validates through an IAM Identity Provider. This approach eliminates credential rotation requirements and reduces the blast radius of compromised secrets.

When OIDC authentication fails, the error message typically shows AccessDenied during AssumeRoleWithWebIdentity. The most common cause is that the IAM trust policy conditions don't match the claims in the OIDC token that GitHub sends. The trust policy must precisely match the repository name, branch reference, workflow environment, or other claims contained in the token. Even minor mismatches—a missing colon, wrong branch name, incorrect audience—cause authentication to fail.

Understanding the OIDC token claims and how IAM trust policies validate them is essential for configuring successful cross-provider authentication. The sub (subject) claim contains the repository and workflow context, while the aud (audience) claim identifies the intended recipient of the token.

Symptoms

When GitHub Actions OIDC AWS authentication fails, you will observe these symptoms:

  • configure-aws-credentials action fails with AccessDenied error
  • AWS rejects AssumeRoleWithWebIdentity request with "not authorized to perform sts:AssumeRoleWithWebIdentity"
  • The OIDC provider exists in AWS IAM, but workflows still cannot assume the role
  • The same workflow works from one branch or repository but fails from another
  • After repository rename or branch change, previously working workflows fail
  • CloudTrail logs show "AssumeRoleWithWebIdentity" failures with policy mismatch reasons

Common error messages from GitHub Actions:

bash
Error: User: arn:aws:sts::123456789012:assumed-role/GitHubActionsRole is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::123456789012:role/MyDeploymentRole
Error: AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity

AWS configure-credentials action output:

bash
Error: Could not assume role with OIDC: AccessDenied
Error: RequestError: send request failed caused by: Post "https://sts.amazonaws.com": AWS Error: AccessDenied

Common Causes

Several factors cause OIDC trust policy mismatches:

  1. 1.**Wrong sub condition pattern**: The trust policy's sub condition doesn't match the exact format GitHub sends. Common mistakes include missing the repo: prefix, wrong branch name format, or not accounting for workflow environment differences.
  2. 2.Incorrect audience value: The aud condition doesn't match what the GitHub Actions configure-aws-credentials action requests. The default audience is sts.amazonaws.com.
  3. 3.**Missing id-token: write permission**: The workflow doesn't grant the id-token: write permission, preventing GitHub from generating the OIDC token at all.
  4. 4.OIDC provider configuration issues: The IAM OIDC provider URL doesn't match GitHub's token endpoint, or the thumbprint configuration is outdated.
  5. 5.Trust policy too restrictive: The policy uses StringEquals instead of StringLike, making it unable to match patterns across branches or environments.
  6. 6.Wrong repository name: After renaming a repository, the trust policy still references the old name.
  7. 7.Environment-specific mismatches: The trust policy doesn't account for different environments (production vs staging) or deployment workflows.

Step-by-Step Fix

Follow these steps to diagnose and resolve OIDC trust policy issues:

Step 1: Confirm the workflow grants OIDC token permission

Check that the workflow has the required permissions:

```yaml # In the workflow file, ensure id-token: write is granted permissions: id-token: write # Required for OIDC contents: read # Required to checkout repo

# Or at the job level jobs: deploy: permissions: id-token: write contents: read steps: - uses: aws-actions/configure-aws-credentials@v4 ```

Without id-token: write, GitHub won't generate the OIDC token needed for AWS authentication.

Step 2: Decode and inspect the actual OIDC token

Before fixing the trust policy, understand what claims GitHub sends:

```yaml # Add a step to decode the token for debugging jobs: debug-oidc: permissions: id-token: write steps: - name: Get OIDC Token id: oidc uses: actions/github-script@v6 with: script: | const token = await core.getIDToken('sts.amazonaws.com'); console.log('Token received'); return token; result-encoding: string

  • name: Decode Token Claims
  • run: |
  • TOKEN="${{ steps.oidc.outputs.result }}"
  • # Split token and decode payload (middle section)
  • PAYLOAD=$(echo $TOKEN | cut -d. -f2)
  • # Add padding if needed
  • PADDING_LEN=$((4 - ${#PAYLOAD} % 4))
  • if [ $PADDING_LEN -ne 4 ]; then
  • PAYLOAD="${PAYLOAD}$(printf '=%.0s' $(seq 1 $PADDING_LEN))"
  • fi
  • echo $PAYLOAD | base64 -d 2>/dev/null | jq . || echo "Manual decode needed"
  • `

Expected token claims structure:

json
{
  "sub": "repo:myorg/myrepo:ref:refs/heads/main",
  "aud": "sts.amazonaws.com",
  "iss": "https://token.actions.githubusercontent.com",
  "repository": "myorg/myrepo",
  "repository_owner": "myorg",
  "ref": "refs/heads/main",
  "workflow": "deploy.yml",
  "job_workflow_ref": "myorg/myrepo/.github/workflows/deploy.yml@refs/heads/main",
  "event_name": "push",
  "actor": "myuser"
}

Step 3: Check the IAM trust policy conditions

Review the existing trust policy:

```bash # Get the role's trust policy aws iam get-role --role-name GitHubActionsRole --query 'Role.AssumeRolePolicyDocument' --output json

# Or via AWS Console: # IAM > Roles > GitHubActionsRole > Trust relationships ```

Verify the trust policy matches the token claims:

json
{
  "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"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
        }
      }
    }
  ]
}

Step 4: Correct the trust policy conditions

Update the trust policy to match GitHub's token format:

bash
# Update trust policy via AWS CLI
aws iam update-assume-role-policy \
  --role-name GitHubActionsRole \
  --policy-document '{
    "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"
          },
          "StringLike": {
            "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
          }
        }
      }
    ]
  }'

Common sub condition patterns:

```json // Match any branch in the repo "sub": "repo:myorg/myrepo:*"

// Match specific branch "sub": "repo:myorg/myrepo:ref:refs/heads/main"

// Match specific environment "sub": "repo:myorg/myrepo:environment:production"

// Match multiple branches "sub": "repo:myorg/myrepo:ref:refs/heads/*"

// Match specific workflow "sub": "repo:myorg/myrepo:workflow:deploy.yml" ```

Step 5: Verify OIDC provider configuration

Check the OIDC provider exists and is correctly configured:

```bash # List OIDC providers aws iam list-open-id-connect-providers

# Get provider details aws iam get-open-id-connect-provider --open-id-connect-provider-arn arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com

# Verify the URL is correct (must be exactly) # https://token.actions.githubusercontent.com ```

Create OIDC provider if missing:

bash
# Create GitHub OIDC provider
aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list d69e56e89d0e5c5b7d2ea9cc9a8c0b02b7bf9d5f 69274b6ee75d54b834f56e5ec9a8c0b02b7bf9d5f

Step 6: Test from the exact workflow context

OIDC conditions are context-sensitive. Test from the exact conditions the trust policy allows:

```yaml # Deploy workflow to test OIDC jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - 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

  • name: Test AWS access
  • run: |
  • aws sts get-caller-identity
  • aws s3 ls
  • `

Step 7: Check CloudTrail for specific denial reason

Review CloudTrail for detailed error information:

bash
# Query CloudTrail for AssumeRole failures
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --query 'Events[?contains(EventName, `AssumeRole`)]'

Verification

After fixing the trust policy, verify OIDC authentication works:

```bash # Trigger workflow from allowed branch gh workflow run deploy.yml --ref main

# Watch workflow execution gh run watch

# Check AWS caller identity in workflow logs # Should show the assumed role ARN

# Verify in CloudTrail aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \ --query 'Events[0].CloudTrailEvent' --output text | jq '.userIdentity' ```

Expected successful output:

json
{
  "type": "AssumedRole",
  "principalId": "AROAEXAMPLE:GitHubActions",
  "arn": "arn:aws:sts::123456789012:assumed-role/GitHubActionsRole/GitHubActions",
  "accountId": "123456789012"
}

Prevention

To prevent OIDC trust policy issues:

  1. 1.Treat trust policy as workflow configuration: Document the trust policy alongside the workflow. Changes to repository name, branch structure, or environment should trigger trust policy updates.
  2. 2.**Use StringLike for flexible matching**: Unless you need strict enforcement, use StringLike with wildcards to allow multiple branches or environments.

```json // Flexible - allows any ref in repo "StringLike": { "sub": "repo:myorg/myrepo:*" }

// Strict - only main branch "StringEquals": { "sub": "repo:myorg/myrepo:ref:refs/heads/main" } ```

  1. 1.Test OIDC after repository changes: When renaming repositories, moving workflows, or changing branch names, immediately test OIDC authentication.
  2. 2.Use environment-specific roles: Create separate roles for different environments (production, staging) with specific sub conditions for each.

```json // Production role - only from main branch and production environment "StringEquals": { "sub": "repo:myorg/myrepo:environment:production" }

// Staging role - from develop branch "StringLike": { "sub": "repo:myorg/myrepo:ref:refs/heads/develop" } ```

  1. 1.Add OIDC verification to CI: Include a step that verifies OIDC authentication works before proceeding with deployment.
yaml
- name: Verify OIDC Authentication
  run: |
    CALLER=$(aws sts get-caller-identity --query Arn --output text)
    if [[ ! "$CALLER" =~ "assumed-role/GitHubActionsRole" ]]; then
      echo "OIDC authentication failed"
      exit 1
    fi
    echo "OIDC authenticated successfully as: $CALLER"
  1. 1.Document subject claim formats: Keep documentation showing the exact sub claim format for different workflow triggers (push, pull_request, workflow_dispatch, environment).
  • [WordPress troubleshooting: Fix IAM Access Denied 403 - Complete Tro](fix-iam-access-denied-403)
  • [GitHub Actions Artifact Expired or 403 Download Failed](github-actions-artifact-expired-403-download-failed)
  • [Fix Github Actions Artifact Upload Failed File Too Large 5gb Limit Issue in GitHub Actions](github-actions-artifact-upload-failed-file-too-large-5gb-limit)
  • [Fix Github Actions Aws S3 Deploy Credentials Expired Rotate Issue in GitHub Actions](github-actions-aws-s3-deploy-credentials-expired-rotate)
  • [Fix Github Actions Cache Evicted Manually Workflow Fails Download Issue in GitHub Actions](github-actions-cache-evicted-manually-workflow-fails-download)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "GitHub Actions OIDC AWS AssumeRole Failed Because the Trust Policy Did Not Match", "description": "Resolve GitHub Actions OIDC AssumeRoleWithWebIdentity failures by correcting IAM trust policy conditions, provider configuration, and workflow permissions.", "url": "https://www.fixwikihub.com/github-actions-oidc-aws-assume-role-trust-policy-failed", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-01-23T08:15:42.152Z", "dateModified": "2026-01-23T08:15:42.152Z" } </script>