Cognito Identity in GitHub Actions
Table of Contents
We’ll look at how most authentication with AWS from GitHub Actions is accomplished today and how it compares to using Cognito Identity.
GitHub Actions OIDC Access Token
When we authenticate with AWS STS
using the GitHub Actions OIDC Access Token
as illustrated here, we do it with an access token having claims similar to below. (Full access token can be viewed here.)
{
"sub": "repo:catnekaise/example-repo:environment:dev",
"aud": "sts.amazonaws.com",
"environment": "dev",
"ref": "refs/heads/main",
"sha": "example-sha",
"repository": "catnekaise/example-repo",
"repository_owner": "catnekaise",
"repository_visibility": "private",
"repository_id": "2",
"run_id": "example-run-id",
"run_number": "4",
"runner_environment": "github-hosted",
"actor": "djonser",
"event_name": "workflow_dispatch",
"ref_type": "branch",
"job_workflow_ref": "catnekaise/example-repo/.github/workflows/test.yml@refs/heads/main",
"iss": "https://token.actions.githubusercontent.com"
}
Trust Policy - GitHub Actions Access Token
When we create trust policies for our AWS IAM Roles we can only match against the aud
and sub
claims of the GitHub Actions OIDC access token. It’s possible to customize the sub
claim by concatenate multiple claims into the sub
claim. Doing so can create a complex string that we now have match in our trust policies.
A trust policy on an AWS IAM Role could look similar to below after we have customized the sub
claim. The example below attempts to match for repo
(repository), context
(environment), job_workflow_ref
and runner_environment
.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::111111111111:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:catnekaise/example-repo:environment:dev:*job_workflow_ref:catnekaise/shared-workflows/.github/workflows/deploy.yml@refs/heads/main:runner_environment:self_hosted*"
},
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}
]
}
The more we customize the sub
claim the more complex our trust policy condition for token.actions.githubusercontent.com:sub
becomes.
Cognito Identity Access Token
Using Cognito Identity we will exchange our GitHub Actions OIDC Access Token
for a Cognito Identity OIDC Access Token
. When we do this we also select what claims in the GitHub Actions OIDC Access Token we want mapped to principal tags in the Cognito Identity OIDC Access Token.
Doing this token exchange and selecting to map the claims actor
, job_workflow_ref
, repository
, environment
and runner_environment
will give us an access token looking like below:
{
"kid": "eu-west-13",
"typ": "JWS",
"alg": "RS512"
}
{
"sub": "eu-west-1:22222222-example",
"aud": "eu-west-1:11111111-example",
"amr": [
"authenticated",
"token.actions.githubusercontent.com",
"arn:aws:iam::111111111111:oidc-provider/token.actions.githubusercontent.com:OIDC:repo:catnekaise/example-repo:environment:dev"
],
"https://aws.amazon.com/tags": {
"principal_tags": {
"actor": [
"djonser"
],
"job_workflow_ref": [
"catnekaise/example-repo.github/workflows/oidc.yaml@refs/heads/main"
],
"repository": [
"catnekaise/example-repo"
],
"environment": [
"dev"
],
"runner_environment": [
"self-hosted"
]
}
},
"iss": "https://cognito-identity.amazonaws.com",
"https://cognito-identity.amazonaws.com/identity-pool-arn": "arn:aws:cognito-identity:eu-west-1:111111111111:identitypool/eu-west-1:11111111-example",
"exp": 1234567890,
"iat": 1234567890
}
The aud
claim in the Cognito Identity access token is the Cognito Identity Pool ID and the sub
claim is an ID generated by Cognito Identity based on the sub
claim that was present in the GitHub Actions OIDC access token.
Trust Policy - Cognito Identity Access Token
Using this new access token we’ll create a trust policy validating the same claims as before, but instead of having to match everything inside the token.actions.githubusercontent.com:sub
condition we can now do it in individual condition statements.
Such a trust policy would look like below.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": [
"sts:AssumeRoleWithWebIdentity",
"sts:TagSession"
],
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "eu-west-1:11111111-example",
"aws:requestTag/repository": ["catnekaise/example-repo"],
"aws:requestTag/environment": ["dev"],
"aws:requestTag/runner_environment": ["self-hosted"],
"aws:requestTag/job_workflow_ref": ["catnekaise/shared-workflows/.github/workflows/deploy.yml@refs/heads/main"]
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "token.actions.githubusercontent.com"
}
}
}
]
}
Using Pictures
In all scenarios we execute a workflow with the permission id-token
set to write
and begin by creating a GitHub Actions OIDC Access token as explained here. (These details may have been hidden from you if you have used aws-actions/configure-aws-credentials.)
GitHub Actions and AWS STS
This is what most of are using today as detailed here.
GitHub Actions and Cognito Identity Basic (Classic) AuthFlow
Using the Basic AuthFlow, we make the token exchange with Cognito Identity and can now use this new token to authenticate with any role in any AWS Account that has a trust policy trusting this token.
GitHub Actions and Cognito Identity Enhanced (Simplified) AuthFlow
Using the Enhanced AuthFlow, there’s still a token exchange with Cognito Identity, but the operations GetOpenIdToken
and AssumeRoleWithWebIdentity
are bundled into a single operation called GetCredentialsForIdentity
. The AWS IAM roles must also be in the same AWS Account as the Cognito Identity Pool.
What is the catch?
In order to do this, Cognito Identity has to be introduced as a component in your CI/CD environment. Cognito Identity is primarily intended (marketed) for use in mobile apps, so it may feel strange to do this. Also, introducing Cognito Identity can increase complexity and administration of a CI/CD environment, but at the same time it can also reduce the complexity and time required for administration. Ultimately you’ll have to evaluate the use case for yourself and your organization to determine if it adds value or not.
Additional Context
The reason why we would use Cognito Identity for this is that we need a third party that we can trust to convert our initial credentials (GitHub token) to another credential without allowing the GitHub Actions workflows any option to modify the claims. Using Cognito Identity we use a native AWS service that is free of charge to accomplish this.
Attribute Based Access Control
Because the GitHub Actions OIDC Access Token claims are now principal/session tags on the AWS IAM Role we assumed, they can be used for ABAC. Read more about ABAC for AWS and have look at this tutorial to learn more if you are unfamiliar with ABAC.
Next
Have a look here for additional resources on the topic of GitHub Actions ABAC in AWS.