Cognito ID token vs access token
A Cognito user pool issues three tokens at every sign-in. The ID token says who the user is, for your app; the access token says what the caller may do, for your APIs; the refresh token gets new ones of both. The ID and access tokens are JSON Web Tokens anyone can decode - which is why this page can show their claims without your pool's keys - while the refresh token is encrypted.
| Claim | ID token | Access token |
|---|---|---|
token_use | id | access |
| App client ID | in aud | in client_id |
scope | none | the OAuth scopes granted |
| Username | cognito:username | username |
cognito:groups | yes | yes |
| Email, name, custom attributes | yes, those the app client may read | no |
sub, iss, exp, iat, auth_time | yes | yes |
exp, iat and auth_time are seconds since 1970 in UTC. auth_time is when the user signed in and stays the same across refreshes; iat is when the token itself was issued. ID and access tokens last 60 minutes by default and between 5 minutes and 1 day as set per app client; the refresh token lasts 30 days by default.
Why an API rejects a Cognito token
| Symptom | Most common cause |
|---|---|
| 401 Unauthorized after about an hour | The token expired: the app has to refresh it with the refresh token before exp |
| 401 from a REST API method with OAuth scopes, or an HTTP API route with authorization scopes | The ID token was sent: only the access token has scope |
CognitoJwtInvalidTokenUseError from aws-jwt-verify | The ID token was sent where the verifier expects the access token |
| 401 with a token that worked in another environment | A token of another user pool - each pool signs with its own keys, and iss names the pool |
| 401 from an HTTP API JWT authorizer although the pool is right | The token's app client is not in the authorizer's audience: aud for ID tokens, client_id for access tokens |
| The token has no email or custom attributes | It is an access token; the attributes are in the ID token, or come from GetUser |
A REST API's Cognito user pool authorizer treats the token as an ID token unless the method has OAuth scopes; then it needs the access token and checks its scopes. An HTTP API's JWT authorizer takes either, as long as the issuer is https://cognito-idp.REGION.amazonaws.com/POOL_ID and the app client ID is in its audience list. Tokens from the sign-in API (InitiateAuth) carry only the scope aws.cognito.signin.user.admin; custom scopes come only through managed login or the OAuth endpoints.
How to verify a Cognito JWT
Decoding a token is not verifying it - anyone can write a token with any claims. A server must check, in this order: the signature, with the public key from the pool's JSON Web Key Set (https://cognito-idp.REGION.amazonaws.com/POOL_ID/.well-known/jwks.json) whose kid matches the token header's; that iss is the pool; that token_use is the type it expects; that aud or client_id is its app client; and that exp has not passed. AWS's aws-jwt-verify library for Node.js does all of it:
import { CognitoJwtVerifier } from "aws-jwt-verify";
const verifier = CognitoJwtVerifier.create({
userPoolId: "us-east-1_EXAMPLE1",
tokenUse: "access",
clientId: "1example23456789abcdefghij",
});
// Throws if the signature, issuer, token type, app client or expiry is wrong.
const payload = await verifier.verify(token); Behind API Gateway, let the authorizer verify the token, and read the claims it passes on (event.requestContext.authorizer.claims for a REST API, event.requestContext.authorizer.jwt.claims for an HTTP API).
Frequently asked questions
Is it safe to paste a Cognito token here?
The token is decoded in your browser: nothing you paste is uploaded, processed on a server or stored. The page only counts that the inspector was used and which mistake it named, never the token or its claims. A token is still a credential until it expires, so paste only into pages you trust - and prefer an expired one.
Can I decode the Cognito refresh token?
No. It is encrypted with a key only Cognito has (a JWE, five parts instead of three), and no API accepts it. Use it only to get new ID and access tokens.
Where are the user's groups?
In cognito:groups, in both the ID and the access token, listing the groups the user belongs to. The ID token also has cognito:roles and cognito:preferred_role, the IAM roles of those groups that an identity pool can assume for the user.
How do I change how long the tokens last?
Per app client: set ID token, access token and refresh token expiration in the console, or IdTokenValidity, AccessTokenValidity, RefreshTokenValidity and TokenValidityUnits with update-user-pool-client. Tokens already issued keep their exp.
References
Understanding the identity (ID) token
Understanding the access token
Refresh tokens
Verifying JSON web tokens
Control access to REST APIs using Amazon Cognito user pools
Control access to HTTP APIs with JWT authorizers
aws-jwt-verify