Summary
When AssumeRoleWithWebIdentity on AWS STS (Security Token Service), STS can NOT validate the aud claim of access token issued by Auth0.
The solutions are, using ID token or Lambda Authorizer.
How to Reproduce
This post shows a sample API that responds to the result using the AWS resources with Auth0 authentication.
Building a frontend
You need to build a frontend app and configure Auth0 by reading the Auth0 Quickstarts.
Please refer to Auth0 React SDK Quickstarts: Call an API to call an API from the frontend app.
Configure Auth0 API
To open the API for an authenticated user of Auth0, create the API on the Auth0 dashboard.
You can use Identifier as you want, the Auth0 recommends API endpoint is preferred to use it. This article uses https://example.com/ as Identifier.
Configure the AWS
Setting Identity providers
After this step, this app will pass the access token issued by Auth0 to the AWS STS. Therefore, you need to add an Auth0 as an identity provider on AWS refer to Creating IAM identity providers.
Go to the “Identity providers” on IAM console, add provider as below.
- Provider type: Open ID Connect
- Provider URL: Copy the domain from Auth0 dashboard.
- Audience: Copy the API Audience from Auth0 dashboard.
Creating a role to delegate permissions to an AWS service
On the identity providers page, push “Assign role” to create a new role. You need to assign the policies for the AWS resources you want to use. See Creating a role for web identity or OIDC.
Sample trust relationships is as below.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/YOUR_AUTH0_DOMAIN.auth0.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"YOUR_AUTH0_DOMAIN.auth0.com:aud": "https://example.com/"
}
}
}
]
}
Creating API
When creating API on lambda, you can access the AWS resources by attaching the policy.
But on your server or GCP cloud functions, you can’t do it.
In this case, there is a way to access the AWS resources via the access key issued by STS.
This is an example of accessing AWS resources. When requested, this API receives access token, API requests to exchange the access token for AWS temporary credentials.
const jose = require("jose");
const {
STSClient,
AssumeRoleWithWebIdentityCommand,
} = require("@aws-sdk/client-sts");
exports.handler = async (event, context) => {
const accessToken = event.headers.authorization.split(" ")[1];
console.log("[Access Token] ", accessToken);
const JWKS = jose.createRemoteJWKSet(
new URL("https://YOUR_AUTH0_DOMAIN.auth0.com/.well-known/jwks.json")
);
// Verifying access token
const { payload, protectedHeader } = await jose.jwtVerify(accessToken, JWKS, {
issuer: "https://YOUR_AUTH0_DOMAIN.auth0.com/",
audience: "https://example.com/",
});
console.log("[Protected Header] ", protectedHeader);
console.log("[Payload] ", payload);
// Requesting temporary security credentials from STS
const client = new STSClient({ region: "us-east-1" });
const command = new AssumeRoleWithWebIdentityCommand({
RoleArn: "arn:aws:iam::XXXXXXXXXXXX:role/Auth0SampleRole",
RoleSessionName: "Auth0AssumeRoleSession",
WebIdentityToken: accessToken,
});
try {
const awsCredentials = await client.send(command);
console.log("[STS Credentials] ", awsCredentials);
// DO Something with AWS Resource
} catch (error) {
// error handling.
console.log("[STS Error] ", error);
}
return {};
};
Error will occur
When this API is executed, the error like below will occur.
InvalidIdentityTokenException: Incorrect token audience
{
'$fault': 'client',
'$metadata': {
httpStatusCode: 400,
requestId: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
extendedRequestId: undefined,
cfId: undefined,
attempts: 1,
totalRetryDelay: 0
},
Error: {
Type: 'Sender',
Code: 'InvalidIdentityToken',
Message: 'Incorrect token audience',
message: 'Incorrect token audience'
},
RequestId: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
xmlns: 'https://sts.amazonaws.com/doc/2011-06-15/'
}
“Incorrect token audience” means aud claim of access token and audience of identity provider does not match.
Why does this error occur?
You can see the data what the access token contains in jwt.io. The access token issued by Auth0 has aud claim like that.
"aud": [
"https://example.com/",
"https://YOUR_AUTH0_DOMAIN.auth0.com/userinfo"
],
The aud claim identifies the recipients that the JWT is intended for. When AssumeRoleWithWebIdentity, STS verifies that matching between “aud claim of access token” and “audience of identity provider”.
In general case, when the API responses “Incorrect token audience” message, you need to check the 2 parameters are same. But this sample’s audience parameter is contained in the aud claims correctly.
In fact, STS will always fail validation if the aud token is specified as an array.
The aud claim can contain an array of strings or a single string value defined in RFC 7519. However, STS accepts single string only. When using ID token instead of access token, you can see that STS accepts its.
I contacted AWS support and was told that STS cannot accept an array of aud claims. This behavior is also same for Cognito ID pools.
Alternative Solutions
However, you may not face this situation normally, I show you alternative solutions.
Using ID Token
If your API is public within same domain of frontend app, using ID token is suitable to use. Because the ID token issued by Auth0 contains single string aud claim, STS can accept the token.
Using Lambda Authorizer
In other ways, you can use lambda authorizer to verify the access token yourself.