Skip to content

Instantly share code, notes, and snippets.

@jayg-hive
Created February 9, 2022 07:02
Show Gist options
  • Save jayg-hive/4bffead5c685ae3bc13b4b32aa7966f8 to your computer and use it in GitHub Desktop.
Save jayg-hive/4bffead5c685ae3bc13b4b32aa7966f8 to your computer and use it in GitHub Desktop.
Cognito JWT Validation via JOSE
import { createRemoteJWKSet, jwtVerify } from "jose";
import type { JWTPayload } from "jose";
import { AWS_REGION, COGNITO_USER_POOL_ID, COGNITO_WEB_CLIENT_ID } from "../utils/consts";
const cognitoIssuer = `https://cognito-idp.${AWS_REGION}.amazonaws.com/${COGNITO_USER_POOL_ID}`;
const cognitoPublicJwks = `${cognitoIssuer}/.well-known/jwks.json`;
interface AWSAuth extends JWTPayload {
"cognito:groups": string[];
client_id: string;
origin_jti: string;
event_id: string;
token_use: string;
scope: string;
username: string;
}
// Reference: https://aws.amazon.com/premiumsupport/knowledge-center/decode-verify-cognito-json-token/
export async function verifyJwtToken(
jwtAccessToken: string,
// requestUsername: string // some requests provide this (i.e., via CognitoIdentityServiceProvider cookies), validation for this can be otherwise skipped.
): Promise<boolean> {
try {
const jwks = createRemoteJWKSet(new URL(cognitoPublicJwks));
// verify if the token is valid
const { payload } = await jwtVerify(jwtAccessToken, jwks, {});
if (!payload) throw new Error("Failed verifying this token.");
const { iss, username, client_id: clientId } = payload as AWSAuth;
// verify if correct issuer is given
if (iss !== cognitoIssuer) throw new Error("Invalid issuer");
// verify if request is from the correct client
if (clientId !== COGNITO_WEB_CLIENT_ID) throw new Error("Invalid web client");
// verify if request is from the same username
// if (requestUsername !== username) throw new Error("User doesn't match");
return true;
} catch (error) {
// do error handling stuff here
return false;
}
}
@jayg-hive
Copy link
Author

Notes:

  • Can be extended to validate the token expiry using the date library of your choice; for this the exp field can be destructured from the payload.
  • On Next.js, the request has the access token as part of req.cookies. See this file for an example of how it's extracted.
  • Also for Next.js: The username can be extracted via the LastAuthUser key; it can then be passed to the above for added validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment