I want to use an Amazon Cognito user pool as the authentication method for my application. I want a secure way to verify the ID and access tokens that clients send to my application.
Short description
When clients use a user pool to authenticate to your application, Cognito sends a JSON Web Token (JWT) for you to decode, read, and modify. To get Cognito user details from a JWT, decode the token and verify the signature.
Important: Before you grant access to your resources, you must verify the signature.
Resolution
Verify that Cognito issued JWTs
Use the following code in JavaScript:
import { CognitoJwtVerifier } from "aws-jwt-verify";
// Verifier that expects valid access tokens:
const verifier = CognitoJwtVerifier.create({
userPoolId: "user_pool_id",
tokenUse: "access",
clientId: "client_id",
});
try {
const payload = await verifier.verify(
"eyJraWQeyJhdF9oYXNoIjoidk..." // the JWT as string
);
console.log("Token is valid. Payload:", payload);
} catch {
console.log("Token not valid!");
}
Note: Replace user_pool_id with your user pool ID and client_id with your app client ID. For ID token, update the tokenUse field to "id". For a list of available parameters, see aws-jwt-verify on the GitHub website.
If you use another programming language, then see the jwt.io libraries on the JWT website or OpenID Connect libraries on the OpenID website. For a code example, see Decode and verify Amazon Cognito JWT tokens on the GitHub website.
Cognito returns up to three tokens, the ID token, the access token, and the refresh token. If you use REST APIs, AWS Amplify, or AWS SDKs to authenticate a user, then you get all three tokens.
For the Cognito hosted UI, the token that you get depends on the type of authorization grant that you use. If you use the implicit grant, then you get only the access and ID token. The authorization code grant returns access, ID, and refresh tokens. The client credentials grant returns only the access token.
Access and ID tokens include a header, a payload, and a signature. The client can't decode or verify the refresh token.
The following is a sample ID token header. The header contains the key ID ("kid"), and the algorithm ("alg") that's used to sign the token. The RS256 algorithm is an RSA signature with SHA-256:
{
"kid": "key_id_example=",
"alg": "RS256"
}
The following is an example of a payload that has information about the user and timestamps of the token's creation and expiration:
{
"sub": "aaaaaaaa-bbbb-cccc-dddd-example",
"aud": "audience_example",
"email_verified": true,
"token_use": "id",
"auth_time": 1500009400,
"iss": "https://cognito-idp.ap-southeast-2.amazonaws.com/ap-southeast-2_example",
"cognito:username": "anaya",
"exp": 1500013000,
"given_name": "Anaya",
"iat": 1500009400,
"email": "anaya@example.com"
}
The signature is a hashed and encrypted combination of the header and the payload.
Cognito generates two RSA key pairs for each user pool. The private key of each pair is used to cryptographically sign the ID token or access token. You can find the public keys at an address in the following location:
https://cognito-idp.region.amazonaws.com/userPoolId/.well-known/jwks.json
Note: Replace region with the AWS Region where your user pool is and userPoolId with your user pool ID.
The JSON file (jwks.json) is structured in the following format:
{ "keys": [{
"alg": "RS256",
"e": "AQAB",
"kid": "abcdefghijklmnopqrsexample=",
"kty": "RSA",
"n": "lsjhglskjhgslkjgh43lj5h34lkjh34lkjht3example",
"use": "sig"
}, {
"alg":
"RS256",
"e": "AQAB",
"kid": "fgjhlkhjlkhexample=",
"kty": "RSA",
"n": "sgjhlk6jp98ugp98up34hpexample",
"use": "sig"
}]
}
To verify the signature of a Cognito JWT, search for the public key with a key ID that matches the header of the token. Use different libraries to verify the signature of the token and extract values, such as expiration and username.
It's a best practice to verify that the token isn't expired. Also, make sure that the audience ("aud") in the payload matches the app client ID that you created in the Amazon Cognito user pool. The aws-jwt-verify library checks these values for you. For more information, see aws-jwt-verify on the GitHub website.
Cache public keys
Because public keys in the JWKS endpoint are rarely rotated, you don't need to download them from the endpoint each time you verify a token. Instead, download the public keys and cache them on the local machine where you use the JWT token verification logic.
The "kid" is a unique identifier for the public keys. To verify a JWT token, check your local cache to determine whether the "kid" in the token header is in the cache. If there's no cache, then download the public keys from the JWKS endpoint and update your cache.
Use different methods to check a token's expiration or revocation status
You can revoke refresh tokens and invalidate access tokens, but you can't revoke ID tokens. JWT verifier libraries verify the token's expiration, but the libraries don't check a token's revocation status. A revocation status check requires a server-side check.
Related information
Understanding user pool JSON web tokens (JWTs)
How do I revoke JWT tokens in Amazon Cognito using the AWS CLI?