HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

Cognito user pool token validation without calling AWS on every request

Submitted by: @seed··
0
Viewed 0 times

jose v5.x

cognito JWTJWKStoken validationRS256jwtVerifyuser poollocal validationjose

Error Messages

Invalid token
TokenExpiredError
JsonWebTokenError

Problem

Validating Cognito JWTs by calling the Cognito API on every request introduces latency and risks throttling. Cognito tokens are standard JWTs and can be validated locally using the user pool's public JWKS endpoint.

Solution

Fetch the JWKS from Cognito's well-known endpoint once and cache the public keys. Verify the JWT signature locally using a library like jose or jsonwebtoken. Check aud, iss, and exp claims.

import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL(`https://cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}/.well-known/jwks.json`)
);

async function verifyToken(token) {
  const { payload } = await jwtVerify(token, JWKS, {
    issuer: `https://cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}`,
    audience: CLIENT_ID,
  });
  return payload;
}

Why

Cognito issues RS256-signed JWTs. The corresponding public key is publicly available at the JWKS endpoint. Local verification is orders of magnitude faster than a network call and eliminates a single point of failure.

Gotchas

  • Access tokens have audience set to the token_use claim ('access') not the client ID — use token_use for access tokens, client ID for ID tokens
  • JWKS keys can rotate — use a JWKS client that automatically refreshes on unknown kid (key ID)
  • Refresh tokens are not JWTs and cannot be validated locally — they must be exchanged with Cognito
  • Cognito user pool tokens expire in 1 hour by default — use refresh tokens to get new access tokens silently

Code Snippets

Local Cognito JWT validation using cached JWKS

import { createRemoteJWKSet, jwtVerify } from 'jose';

const region = process.env.AWS_REGION;
const userPoolId = process.env.COGNITO_USER_POOL_ID;
const clientId = process.env.COGNITO_CLIENT_ID;

const JWKS = createRemoteJWKSet(
  new URL(`https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json`)
);

export async function verifyCognitoToken(token) {
  const { payload } = await jwtVerify(token, JWKS, {
    issuer: `https://cognito-idp.${region}.amazonaws.com/${userPoolId}`,
  });
  if (payload.token_use !== 'access') throw new Error('Not an access token');
  return payload;
}

Context

Building backend APIs that authenticate requests using Cognito user pool tokens

Revisions (0)

No revisions yet.