AWS cognito: Add safe ID token parsing guidance and per-scenario claim examples
Summary
Adds a 'How to parse this token safely' section explaining that only iss/sub/aud/exp/iat are guaranteed and that token_use must be verified, plus four decoded example ID token payloads (authorization code, groups/roles, federated sign-in, ACR/AMR) and a note that identities.primary and dateCreated are serialized as strings.
Security assessment
Documents token validation requirements (verify token_use to avoid accepting an access token as an ID token, treat non-required claims as optional, nonce for replay protection) and warns that identities.primary is a string so `=== true` checks silently fail — a real authorization-bypass footgun. It is preventive best-practice documentation rather than a fix for a named vulnerability.
Evidence
+The OpenID Connect specification requires only `iss`, `sub`, `aud`, `exp`, and `iat`, and these are present in every Amazon Cognito ID token. Amazon Cognito also always adds `token_use` with the value `id`, which you should verify to confirm that you received an ID token rather than an access token.
Diff
diff --git a/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.md b/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.md index 82c5db2..1d47c08 100644 --- a//cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.md +++ b//cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.md @@ -176,0 +177,106 @@ User pool custom attributes are always prefixed with `custom:`. +### How to parse this token safely + +The claim set in a Amazon Cognito ID token is not fixed. The claims that a token carries vary with the authentication flow, the user's group membership, and the identity provider. They also depend on the features that are enabled on your user pool. The OpenID Connect specification requires only `iss`, `sub`, `aud`, `exp`, and `iat`, and these are present in every Amazon Cognito ID token. Amazon Cognito also always adds `token_use` with the value `id`, which you should verify to confirm that you received an ID token rather than an access token. Treat every other claim as optional and conditional on how the user authenticated. + +The ID token is one of several Amazon Cognito token types, and the same parsing rules apply to all of them. For more information about parsing tokens safely, see [Parse user pool tokens safely](./user-pool-parse-tokens-safely.html). + +### Example ID tokens by scenario + +The ID token default payload combines many optional claims into a single example. In practice, the claims that a token carries depend on how the user signed in. The following examples show the claim combinations that Amazon Cognito emits for four common scenarios. Each example is a decoded payload, not a signed and serialized JWT: a real token is `header.payload.signature`, base64url-encoded and RS256-signed with your user pool's key. The `sub`, user pool ID, and timestamp values are illustrative placeholders. + +#### Baseline authorization-code ID token + +The identity token that most applications consume after they exchange an authorization code at the token endpoint. The `token_use` value of `id` distinguishes it from an access token. The `nonce` claim is present because the client sent a `nonce` parameter in the authorization request to help prevent replay attacks. + + + { + "sub": "a1b2c3d4-5678-90ab-cdef-1234567890ab", + "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ExAmPlE", + "aud": "6o7h8i9jexampleclientid", + "cognito:username": "my-test-user", + "email": "[email protected]", + "email_verified": true, + "origin_jti": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "token_use": "id", + "auth_time": 1759345200, + "iat": 1759345200, + "exp": 1759348800, + "jti": "11112222-3333-4444-5555-666677778888", + "nonce": "n-0S6_WzA2Mj" + } + +#### Group and role membership + +Amazon Cognito adds the group and role authorization claims when the user belongs to one or more user pool groups. The `cognito:groups` and `cognito:roles` claims are arrays; read them as arrays even when they contain a single element. `cognito:preferred_role` is a single string. + + + { + "sub": "a1b2c3d4-5678-90ab-cdef-1234567890ab", + "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ExAmPlE", + "aud": "6o7h8i9jexampleclientid", + "cognito:username": "my-test-user", + "cognito:groups": ["admins", "beta-testers"], + "cognito:roles": ["arn:aws:iam::111122223333:role/my-test-role"], + "cognito:preferred_role": "arn:aws:iam::111122223333:role/my-test-role", + "origin_jti": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "token_use": "id", + "auth_time": 1759345200, + "iat": 1759345200, + "exp": 1759348800, + "jti": "11112222-3333-4444-5555-666677778888" + } + +#### Federated sign-in + +A user who signed in through an external identity provider, such as a social or SAML/OIDC provider, carries the `identities` claim, which describes the external provider link. + + + { + "sub": "a1b2c3d4-5678-90ab-cdef-1234567890ab", + "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ExAmPlE", + "aud": "6o7h8i9jexampleclientid", + "cognito:username": "LoginWithAmazon_amzn1.account.EXAMPLE", + "identities": [ + { + "userId": "amzn1.account.EXAMPLE", + "providerName": "LoginWithAmazon", + "providerType": "LoginWithAmazon", + "issuer": null, + "primary": "true", + "dateCreated": "1642699117273" + } + ], + "origin_jti": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "token_use": "id", + "auth_time": 1759345200, + "iat": 1759345200, + "exp": 1759348800, + "jti": "11112222-3333-4444-5555-666677778888" + } + +###### Note + +Inside each `identities` object, `primary` and `dateCreated` are serialized as strings (`"true"` and `"1642699117273"`), not as a JSON boolean and number. A comparison such as `identity.primary === true` silently evaluates to false. Read `primary` as the string `"true"` or `"false"`, and `dateCreated` as a string that holds Unix time in milliseconds. + +#### Authentication strength (ACR and AMR) + +When your user pool reports the authentication level that the user reached, Amazon Cognito adds the `acr` and `amr` claims. The following token is from a user who authenticated with a password and a TOTP from an authenticator app, which is the highest level. Each token contains exactly one of the possible `acr` levels. For the fixed set of levels, the factor combinations that satisfy each one, and the complete list of `amr` values, see [Authentication levels with ACR and AMR claims](./cognito-user-pools-step-up-authentication.html). + + + { + "sub": "a1b2c3d4-5678-90ab-cdef-1234567890ab", + "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ExAmPlE", + "aud": "6o7h8i9jexampleclientid", + "cognito:username": "my-test-user", + "acr": "urn:cognito:loa:4", + "amr": ["pwd", "otp", "mfa"], + "origin_jti": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "token_use": "id", + "auth_time": 1759345200, + "iat": 1759345200, + "exp": 1759348800, + "jti": "11112222-3333-4444-5555-666677778888" + } + +Requesting an authentication level requires the Essentials or Plus feature plan. The `acr` and `amr` claims are computed by Amazon Cognito and can't be modified by a pre token generation Lambda trigger. Translating the Amazon Cognito levels to another standard, such as NIST or eIDAS, is the responsibility of your application. +