OIDC authentication for GitHub Actions to AWS is a game-changer because it eliminates the need for long-lived AWS access keys, which are a significant security risk.

Let’s see it in action. Imagine a GitHub Actions workflow that needs to deploy a web application to an S3 bucket.

name: Deploy to S3 with OIDC

on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
      packages: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::111122223333:role/GitHubOidcRole
          aws-region: us-east-1

      - name: Deploy to S3
        run: aws s3 sync ./dist s3://my-unique-app-bucket --delete

Here’s what’s happening under the hood. When the configure-aws-credentials action runs, it initiates a handshake with AWS. GitHub, acting as an OpenID Connect (OIDC) provider, issues a short-lived JWT (JSON Web Token) to the workflow. This token contains claims about the workflow’s identity, such as the repository, branch, and environment. The configure-aws-credentials action then exchanges this JWT with AWS Security Token Service (STS) AssumeRoleWithWebIdentity API. AWS STS validates the JWT against a trust policy configured on an IAM role. If valid, STS issues temporary AWS security credentials (access key ID, secret access key, and session token) to the GitHub Actions runner. These temporary credentials are then used by subsequent AWS CLI commands in the workflow.

The core problem this solves is the insecure practice of storing long-lived AWS access keys directly in GitHub Secrets. Those keys, if compromised, grant broad access to your AWS account for their entire lifetime. OIDC replaces this with short-lived, automatically rotated credentials, drastically reducing the attack surface. The IAM role (arn:aws:iam::111122223333:role/GitHubOidcRole in the example) is the linchpin. Its trust policy explicitly permits the GitHub OIDC provider to assume it, but only for specific repositories and branches. This fine-grained control ensures that only legitimate GitHub Actions runs can obtain AWS credentials.

The permissions block in the GitHub Actions workflow is crucial. id-token: write signals to GitHub that this workflow needs to request an OIDC token. contents: read is often needed to check out the repository code. packages: write might be used if you’re also pushing container images. The role-to-assume parameter in the configure-aws-credentials action is where you specify the ARN of the IAM role in your AWS account that has been configured with the OIDC trust relationship. The aws-region is straightforward – it’s the AWS region where your S3 bucket resides.

The actual AWS CLI command, aws s3 sync ./dist s3://my-unique-app-bucket --delete, then uses the temporary credentials provided by the action to perform the deployment. The --delete flag ensures that any files in the S3 bucket that are no longer in the ./dist directory are removed, keeping the bucket synchronized.

A common pitfall is misconfiguring the IAM role’s trust policy. It needs to be precise. For example, a policy like this is essential:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::111122223333: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-github-username/your-repo-name:ref:refs/heads/main"
        }
      }
    }
  ]
}

Notice the StringLike condition. This is where you restrict which GitHub repositories and branches can assume this role. If this condition is too broad or too narrow, your actions will fail. For instance, if you forget to specify the ref:refs/heads/main part, any branch in that repository could assume the role, which is usually not desired. Conversely, if you hardcode a specific commit SHA, it will only work for that single commit.

The next step in securing your CI/CD pipeline often involves managing secrets within AWS itself, such as using AWS Secrets Manager for database credentials, which can then be accessed by your deployed applications.

Want structured learning?

Take the full Github-actions course →