Verifying a JSON Web Token
JSON web tokens (JWTs) can be decoded, read, and modified easily. A modified access token creates a risk of privilege escalation. A modified ID token creates a risk of impersonation. Your application trusts your user pool as a token issuer, but what if a user intercepts the token in transit? You must ensure that your application is receiving the same token that Amazon Cognito issued.
Amazon Cognito issues tokens that use some of the integrity and confidentiality features of the
OpenID Connect (OIDC) specification. User pool tokens indicate validity with objects like the
expiration time, issuer, and digital signature. The signature, the third and final segment of
the .
-delimited JWT, is the key component of token validation. A malicious user
can modify a token, but if your application retrieves the public key and compares the
signature, it won't match. Any application that processes JWTs from OIDC authentication must
perform this verification operation with each sign-in.
On this page, we make some general and specific recommendations for verification of JWTs. Application development spans a variety of programming languages and platforms. Because Amazon Cognito implements OIDC sufficiently close to the public specification, any reputable JWT library in your developer environment of choice can handle your verification requirements.
These steps describe verifying a user pool JSON Web Token (JWT).
Prerequisites
Your library, SDK, or software framework might already handle the tasks in this section. AWS SDKs provide tools for Amazon Cognito user pool token handling and management in your app. AWS Amplify includes functions to retrieve and refresh Amazon Cognito tokens.
For more information, see the following pages.
Many libraries are available for decoding and verifying a JSON Web Token (JWT). If you
want to manually process tokens for server-side API processing, or if you are using other
programming languages, these libraries can help. See the OpenID foundation list of libraries for working
with JWT tokens
Validate tokens with aws-jwt-verify
In a Node.js app, AWS recommends the aws-jwt-verify
libraryaws-jwt-verify
, you can populate a CognitoJwtVerifier
with
the claim values that you want to verify for one or more user pools. Some of the values that
it can check include the following.
-
That access or ID tokens aren't malformed or expired, and have a valid signature.
-
That access tokens came from the correct user pools and app clients
. -
That access token claims contain the correct OAuth 2.0 scopes
. -
That the keys that signed your access and ID tokens match a signing key
kid
from the JWKS URI of your user pools. The JWKS URI contains public information about the private key that signed your user's token. You can find the JWKS URI for your user pool at
https://cognito-idp.
.<Region>
.amazonaws.com/<userPoolId>
/.well-known/jwks.json
For more information and example code that you can use in a Node.js app or a AWS Lambda
authorizer, see aws-jwt-verify
Understanding and inspecting tokens
Before you integrate token inspection with your app, consider how Amazon Cognito assembles JWTs. Retrieve example tokens from your user pool. Decode and examine them in detail to understand their characteristics, and determine what you want to verify and when. For example, you might want to examine group membership in one scenario, and scopes in another.
The following sections describe a process to manually inspect Amazon Cognito JWTs as you prepare your app.
Confirm the structure of the JWT
A JSON Web Token (JWT) includes three sections with a .
(dot) delimiter
between them.
- Header
-
The key ID,
kid
, and the RSA algorithm,alg
, that Amazon Cognito used to sign the token. Amazon Cognito signs tokens with analg
ofRS256
. Thekid
is a truncated reference to a 2048-bit RSA private signing key held by your user pool. - Payload
-
Token claims. In an ID token, the claims include user attributes and information about the user pool,
iss
, and app client,aud
. In an access token, the payload includes scopes, group membership, your user pool asiss
, and your app client asclient_id
. - Signature
-
The signature isn't decodable base64url like the header and payload. It's an RSA256 identifier derived from a signing key and parameters that you can observe at your JWKS URI.
The header and payload are base64url-encoded JSON. You can identify them by the
opening characters eyJ
that decode to the starting character {
.
If your user presents a base64url-encoded JWT to your app and it's not in the format
[JSON Header].[JSON Payload].[Signature]
, it's not a valid Amazon Cognito token and
you can discard it.
Validate the JWT
The JWT signature is a hashed combination of the header and the payload. Amazon Cognito generates two pairs of RSA cryptographic keys for each user pool. One private key signs access tokens, and the other signs ID tokens.
To verify the signature of a JWT token
-
Decode the ID token.
The OpenID Foundation also maintains a list of libraries for working with JWT tokens
. You can also use AWS Lambda to decode user pool JWTs. For more information, see Decode and verify Amazon Cognito JWT tokens using AWS Lambda
. -
Compare the local key ID (
kid
) to the publickid
.-
Download and store the corresponding public JSON Web Key (JWK) for your user pool. It is available as part of a JSON Web Key Set (JWKS). You can locate it by constructing the following
jwks_uri
URI for your environment:https://cognito-idp.
<Region>
.amazonaws.com/<userPoolId>
/.well-known/jwks.jsonFor more information on JWK and JWK sets, see JSON Web Key (JWK)
. Note
Amazon Cognito might rotate signing keys in your user pool. As a best practice, cache public keys in your app, using the
kid
as a cache key, and refresh the cache periodically. Compare thekid
in the tokens that your app receives to your cache.If you receive a token with the correct issuer but a different
kid
, Amazon Cognito might have rotated the signing key. Refresh the cache from your user pooljwks_uri
endpoint.This is a sample
jwks.json
file:{ "keys": [{ "kid": "1234example=", "alg": "RS256", "kty": "RSA", "e": "AQAB", "n": "1234567890", "use": "sig" }, { "kid": "5678example=", "alg": "RS256", "kty": "RSA", "e": "AQAB", "n": "987654321", "use": "sig" }] }
- Key ID (
kid
) -
The
kid
is a hint that indicates which key was used to secure the JSON Web Signature (JWS) of the token. - Algorithm (
alg
) -
The
alg
header parameter represents the cryptographic algorithm that is used to secure the ID token. User pools use an RS256 cryptographic algorithm, which is an RSA signature with SHA-256. For more information on RSA, see RSA cryptography. - Key type (
kty
) -
The
kty
parameter identifies the cryptographic algorithm family that is used with the key, such as "RSA" in this example. - RSA exponent (
e
) -
The
e
parameter contains the exponent value for the RSA public key. It is represented as a Base64urlUInt-encoded value. - RSA modulus (
n
) -
The
n
parameter contains the modulus value for the RSA public key. It is represented as a Base64urlUInt-encoded value. - Use (
use
) -
The
use
parameter describes the intended use of the public key. For this example, theuse
valuesig
represents signature.
- Key ID (
-
Search the public JSON Web Key for a
kid
that matches thekid
of your JWT.
-
-
Use a JWT library to compare the signature of the issuer to the signature in the token. The issuer signature is derived from the public key (the RSA modulus
"n"
) of thekid
in jwks.json that matches the tokenkid
. You might need to convert the JWK to PEM format first. The following example takes the JWT and JWK and uses the Node.js library, jsonwebtoken, to verify the JWT signature:
Verify the claims
To verify JWT claims
-
By one of the following methods, verify that the token hasn't expired.
-
Decode the token and compare the
exp
claim to the current time. -
If your access token includes an
aws.cognito.signin.user.admin
claim, send a request to an API like GetUser. API requests that you authorize with an access token return an error if your token has expired. -
Present your access token in a request to the userInfo endpoint. Your request returns an error if your token has expired.
-
-
The
aud
claim in an ID token and theclient_id
claim in an access token should match the app client ID that was created in the Amazon Cognito user pool. -
The issuer (
iss
) claim should match your user pool. For example, a user pool created in theus-east-1
Region will have the followingiss
value:https://cognito-idp.us-east-1.amazonaws.com/
.<userpoolID>
-
Check the
token_use
claim.-
If you are only accepting the access token in your web API operations, its value must be
access
. -
If you are only using the ID token, its value must be
id
. -
If you are using both ID and access tokens, the
token_use
claim must be eitherid
oraccess
.
-
You can now trust the claims inside the token.