Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Verifying AWS Cognito OIDC ID Token

The ID token proved to your application upon a user logging into the Cognito SSO, is actually a JSON Web Token (JWT) This is a requirement from the OIDC specification. You can convert it to the underlying JSON by using a JWT debugger such as

Retrieving The JWK For Verification

In order to verify the JWT, in order to be sure that someone did legitimately log in through the third-party service, and not just hit your endpoint with a made-up ID token, you need to have your JSON Web Key (JWK). We use this key to reproduce the signature, and if it matches the one provided, we know that the JWT is legitimate. If using Amazon Cognito, you can retrieve the JWK by going to:

$jsonWebKeyUrl = "https://cognito-idp.{$region}{$userPoolId}/.well-known/jwks.json";

$region would be something like eu-west-2 for London. You will need to retrieve the user pool ID from the web console, or Terraform (however you set up the user pool).

This URL will actually give you a pair of keys JSON format like so:

    "keys": [
            "alg": "RS256",
            "e": "XXXX",
            "kty": "RSA",
            "n": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "use": "sig"
            "alg": "RS256",
            "e": "XXXX",
            "kty": "RSA",
            "n": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "use": "sig"

AWS documentation info about the structure of these keys.

You will know which key you need by the kid matching up with the one provided in the kid attribute of the JWT header.

Using PHP Package For JWT Verification

Since we do not wish to have to manually perform the many complicated operations for re-generating and verifying the signature, we can use the firebase JWT package to do this for us like so:

use Firebase\JWT\JWK;
use Firebase\JWT\JWT;

// Build up the cognito URL to get the JWK from
$region = AWS_REGION;
$userPoolId = USER_POOL_ID
$jsonWebKeyUrl = "https://cognito-idp.{$region}{$userPoolId}/.well-known/jwks.json";

// Laravel HTTP GET request to array
// You probably want to cache this result array
$jsonWebKeys = Http::get($jsonWebKeyUrl)->json(); 

// Finally, use the key to verify and decode the ID token JWT
$firebaseKeys = JWK::parseKeySet($jsonWebKeys);
$decodedPayload = JWT::decode($idToken, $firebaseKeys); // throws SignatureInvalidException if invalid signature given

Decoded And Verified JWT

That's it! You have now verified the JWT (will have thrown exception if invalid), and have the decoded payload contents in the $decodedPayload variable. Below is some sample output of that variable if you were to JSON pretty-print it.

    "at_hash": "xxxxxxxxxxxxxxxxxxxxxx",
    "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "cognito:groups": [
    "email_verified": true,
    "iss": "https:\/\/\/eu-west-2_xxxxxxxxx",
    "cognito:username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "origin_jti": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "aud": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
    "token_use": "id",
    "auth_time": 1650901990,
    "name": "Programster Page",
    "exp": 1650905590,
    "iat": 1650901990,
    "jti": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "email": ""

In here, you can see some really useful information, such as the user's email address, ID (referred to as sub for subject), and the groups the user is part of (cognito:groups). Those groups can be particularly useful for determining what the user should or should not be able to do within your application.


Last updated: 25th April 2022
First published: 25th April 2022