The token exchange replaces a long-lived key with a short-lived one, and the value is entirely in how narrowly the trust is scoped.
{
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub":
"repo:org/shop:ref:refs/heads/main"
}
}
}
// the misconfiguration that trusts the whole internet:
// "sub": "repo:*" or omitting the sub condition
Omitting the subject condition means any GitHub Actions workflow in any repository anywhere can assume the role, and the configuration looks complete because the audience check passes. Scoping to a branch is the useful granularity for a deploy role; scoping to an environment — repo:org/shop:environment:production — is better still, because it composes with the approval gate. Every one of these is a string match, so a typo fails closed, which is the one merciful property.