[Update] AWS STS now supports provider-specific claim validation for GitHub, Google, CircleCI, and OCI in OIDC federation
This page has been translated by machine translation. View original
Introduction
Hello everyone, this is Akaike.
Many of you probably use OIDC federation when accessing AWS resources from GitHub Actions.
However, when relying on the sub claim in your trust policy, complex conditions like "allow only this Environment of this branch of this repository" can be quite difficult to implement.
In this context, AWS STS has been updated to support IdP-specific claim validation in OIDC federation.
With this update,
provider-specific claims from GitHub, Google, CircleCI, and Oracle Cloud Infrastructure (OCI) can now be used as condition keys in IAM role trust policies and resource control policies.
This is a particularly welcome update for those who access AWS resources through CI/CD pipelines using GitHub Actions.
In this article, I'll compare the traditional configuration methods with what's now possible after this update, with specific policy examples using GitHub as an example.
Recap of OIDC Federation
First, let's briefly review how OIDC federation works as background knowledge.
General Mechanism of OIDC Federation
OIDC federation is a mechanism that uses tokens (JWTs) issued by external Identity Providers (IdPs) to prove "who you are" to another service.
JWTs contain attribute information called claims, and the receiving party validates these claims to determine whether to allow access.
In the AWS Case
When using OIDC federation with AWS, you use the AWS STS AssumeRoleWithWebIdentity API.
When you pass a JWT issued by an IdP to this API, AWS STS validates the token, checks it against the conditions in the IAM role's trust policy, and issues temporary AWS credentials.
For example, with GitHub Actions, the flow is:
-
- A GitHub Actions workflow executes
-
- GitHub issues an OIDC token (JWT)
-
- The workflow calls
AssumeRoleWithWebIdentitywith that token
- The workflow calls
-
- AWS STS validates the token and checks it against the IAM role's trust policy conditions
-
- If the conditions are met, temporary credentials are issued
In step 4 above, JWT claims can be used as condition keys, and with this update, the number of available condition keys has increased significantly.
Traditional Condition Keys
Previously, there were no GitHub-specific condition keys, and
the condition keys available for OIDC federation trust policies were limited to these standard claims:
| AWS STS Condition Key | IdP JWT Claim | Description |
|---|---|---|
aud |
azp (or aud if not set) |
Audience of the token |
sub |
sub |
Subject (principal identifier) |
amr |
amr |
Authentication Method Reference |
oaud |
aud |
Original audience |
email |
email |
Email address |
Traditional GitHub Actions Trust Policy Example
To control access from GitHub Actions, we mainly used the sub claim.
{
"Version":"2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:org-name/repo-name:ref:refs/heads/demo"
}
}
}
]
}
While this method allows for control like "only from the main branch of a specific repository," the sub claim is a string with information packed in this format:
repo:{owner}/{repo}:ref:refs/heads/{branch}
repo:{owner}/{repo}:environment:{environment_name}
repo:{owner}/{repo}:pull_request
As you can see, the value of sub changes format depending on the context.
Additionally, you cannot specify the same condition key multiple times within an IAM policy's Condition block.
Therefore, it was not possible to express compound conditions like "specific branch AND specific Environment" using only sub.
As a result, we often had to rely on wildcard matching with StringLike, like this:
{
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
}
}
}
This made fine-grained control by branch or Environment difficult, risking unintended access from workflows.
New Condition Keys
With this update, provider-specific claims can now be used as individual condition keys.
Here's a list of the added condition keys:
Condition Keys Added for GitHub Actions
| Condition Key | Description |
|---|---|
actor |
Individual account that triggered the workflow |
actor_id |
Immutable ID of the individual account |
job_workflow_ref |
Reference path of reusable workflow |
repository |
Repository where the workflow is running |
repository_id |
Immutable ID of the repository |
workflow |
Workflow name |
ref |
Git reference (branch/tag) |
environment |
Environment name for the job |
enterprise_id |
Enterprise ID containing the repository |
Condition Keys Added for Google
| Condition Key | Description |
|---|---|
organization_number |
Google Cloud/Workspace organization number |
Condition Keys Added for CircleCI
| Condition Key | Description |
|---|---|
project_id |
UUID of the CircleCI project |
Condition Keys Added for OCI
| Condition Key | Description |
|---|---|
rpst_id |
Resource Principal Session Token ID |
Sub is Still Required
For trust policies specifying an OIDC provider as Principal, it appears that evaluation of the sub condition key is still mandatory.
When I tried to create an IAM role without specifying token.actions.githubusercontent.com:sub in the trust policy, I got the following error that prevented creation:
So it seems necessary to evaluate sub using either StringEquals, StringLike, or StringEqualsIgnoreCase.
Trust policy with trusted principal arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com must evaluate, using StringEquals, StringLike or StringEqualsIgnoreCase, token.actions.githubusercontent.com:sub which is not scoped to all.
Therefore, the new condition keys don't replace sub, but rather complement it to enable more fine-grained control.
Note that when a Condition block contains multiple condition operators (like StringEquals and StringLike), they are all evaluated as AND conditions.
This means that in addition to the wildcard match on sub, all other condition keys like repository and environment must also be satisfied, resulting in more fine-grained access control than was previously possible.
New GitHub Actions Trust Policy Example
Let's look at a trust policy using the new condition keys:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:repository": "my-org/my-repo",
"token.actions.githubusercontent.com:ref": "refs/heads/main",
"token.actions.githubusercontent.com:environment": "production"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
}
}
}
]
}
Information that was previously packed into the sub claim can now be specified as individual condition keys.
This allows for expressing compound conditions like "specific repository" AND "main branch" AND "production Environment".
Example Using Immutable IDs
Using immutable IDs allows for more robust policies that aren't affected by repository or account name changes.
Using repository_id (numeric ID) instead of repository (string) ensures the condition doesn't break if the repository is renamed.
Similarly, actor_id handles changes to GitHub usernames.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:repository_id": "123456789",
"token.actions.githubusercontent.com:actor_id": "123456789"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
}
}
}
]
}
Example Specifying a Particular Workflow
Using job_workflow_ref allows restricting access to only workflows that come through a specific reusable workflow.
This enables setups where AWS access is allowed only through common deployment workflows managed by your organization.
For example, you can prevent deployments that don't go through a workflow managed by the security team, which helps strengthen governance.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:job_workflow_ref": "my-org/shared-workflows/.github/workflows/deploy.yml@refs/heads/main"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/*"
}
}
}
]
}
Enterprise-Wide Control Example
For GitHub Enterprise Cloud users, enterprise_id can allow access from all repositories within an Enterprise while blocking external access.
This eliminates the need to list individual repositories and update the trust policy whenever a new repository is added.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:enterprise_id": "345"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/*"
}
}
}
]
}
Terraform Configuration Examples
Now let's look at practical examples of configuring OIDC providers and IAM roles with Terraform.
Traditional Configuration
# OIDC Provider
resource "aws_iam_openid_connect_provider" "github_actions" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
}
# IAM Role
resource "aws_iam_role" "github_actions_deploy" {
name = "github-actions-deploy"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github_actions.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
"token.actions.githubusercontent.com:sub" = "repo:my-org/my-repo:ref:refs/heads/main"
}
}
}
]
})
}
Updated Configuration
As mentioned earlier, sub is still required, so we add new condition keys alongside sub.
It's now much clearer at a glance what each condition is restricting.
# OIDC Provider
resource "aws_iam_openid_connect_provider" "github_actions" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
}
# IAM Role
resource "aws_iam_role" "github_actions_deploy" {
name = "github-actions-deploy"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github_actions.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
"token.actions.githubusercontent.com:repository" = "my-org/my-repo"
"token.actions.githubusercontent.com:ref" = "refs/heads/main"
"token.actions.githubusercontent.com:environment" = "production"
}
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:my-org/my-repo:*"
}
}
}
]
})
}
Conclusion
I've summarized AWS STS's support for IdP-specific claim validation in OIDC federation.
The ability to use individual condition keys for information previously packed into the sub claim has improved policy readability and maintainability.
This is a welcome update for organizations using GitHub Actions CI/CD pipelines, in terms of both security and operations.
It might be a good opportunity to review your existing OIDC federation settings and transition to more fine-grained conditions.
I hope this helps improve the security of your CI/CD pipelines.