How to Decode an AWS Cognito JWT: Claims, Groups, and Token Types
- Cognito issues three token types — ID, access, and refresh
- The token_use claim that tells you which type you have
- How Cognito groups appear in the decoded payload
- Reading sub vs username in Cognito tokens
Table of Contents
AWS Cognito issues ID tokens and access tokens as JWTs after authentication. Both can be decoded to inspect claims — paste either into the decoder above. The key difference is the token_use claim that tells you which one you are looking at.
Cognito ID Token vs Access Token
Cognito issues three tokens:
- ID token: Contains user attributes — sub, email, phone, custom attributes, and group memberships. Use on your frontend to display user info or on your backend to identify the user.
- Access token: Contains scopes and group memberships. Use this to authorize API calls. Shorter attribute set than the ID token.
- Refresh token: Not a JWT — an opaque token used to get new ID and access tokens. Not decodable.
Distinguish them by the token_use claim in the decoded payload: "token_use": "id" or "token_use": "access".
What a Cognito ID Token Contains
{
"sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email_verified": true,
"iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_POOLID",
"cognito:username": "janesmith",
"cognito:groups": ["Admins", "Editors"],
"aud": "YOUR-APP-CLIENT-ID",
"token_use": "id",
"auth_time": 1700000000,
"exp": 1700003600,
"iat": 1700000000,
"email": "[email protected]",
"custom:plan": "pro",
"custom:org_id": "org_123"
}
Note the Cognito-specific claim naming: cognito:username and cognito:groups use the cognito: prefix. Custom attributes use the custom: prefix. The standard sub is the user's permanent UUID — use this as your primary user identifier, not the username which can change.
Using Cognito Groups for Role-Based Access
// JavaScript: check if user is in an admin group
const payload = decodeJwt(token);
const groups = payload['cognito:groups'] || [];
const isAdmin = groups.includes('Admins');
const isEditor = groups.includes('Editors');
// In an API handler (after verifying signature)
if (!isAdmin) {
return res.status(403).json({ error: 'Admin access required' });
}
Cognito groups are baked into the token at sign-in. If you add a user to a group and they are already logged in, they will not see the new group until they sign in again or refresh their tokens.
The access token also includes cognito:groups, so you can use either token for group-based authorization.
Cognito Token Lifetimes
Default Cognito token expiry times (configurable in User Pool settings):
- ID token: 1 hour (minimum 5 minutes, maximum 1 day)
- Access token: 1 hour (same range)
- Refresh token: 30 days (minimum 1 hour, maximum 10 years)
Decode your token and check exp to confirm the actual expiry configured for your User Pool. The Amplify SDK and Cognito Identity SDK handle token refresh automatically when using Auth.currentSession().
The issuer format is always https://cognito-idp.REGION.amazonaws.com/POOL_ID — verify this matches your User Pool region and ID if you see signature verification failures.
Decode Your Cognito Token Now
Paste your Cognito ID or access token above — see groups, custom attributes, token_use, and expiry instantly.
Open Free JWT DecoderFrequently Asked Questions
How do I get my Cognito JWT in JavaScript?
With AWS Amplify: call Auth.currentSession() to get a CognitoUserSession, then session.getIdToken().getJwtToken() or session.getAccessToken().getJwtToken(). Without Amplify, use the amazon-cognito-identity-js library or standard OAuth2 flows.
Why is my Cognito custom attribute missing from the JWT?
Custom attributes must be added to the ID token via the User Pool app client settings. In the AWS console, go to User Pool > App clients > Edit > OpenID Connect scopes and make sure the attribute is mapped in the ID token.
What is the difference between sub and cognito:username?
sub is an immutable UUID assigned at account creation — it never changes. cognito:username is the user's login name which can sometimes change. Always use sub as your primary user key in your database.

