Skip to content

Instantly share code, notes, and snippets.

@afraz-khan
Last active April 27, 2023 14:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afraz-khan/65ebd02326480ac96e4ef9c066e0dbea to your computer and use it in GitHub Desktop.
Save afraz-khan/65ebd02326480ac96e4ef9c066e0dbea to your computer and use it in GitHub Desktop.
AWS Cognito JWT Verification Algorithm | TypeScript
import jwkToPem from 'jwk-to-pem';
import jwt, {JwtPayload, VerifyCallback, VerifyOptions} from 'jsonwebtoken';
import {AppConfig} from '../AppConfig';
import JsonWebTokenException from '../../exception/JsonWebTokenException';
export class CognitoJWTVerifier {
/*
This is application config object having all cognito params.
*/
protected config: AppConfig;
public getTokenOrThrowError(token: string): JwtPayload {
let decoded: any;
try {
this.verifyToken(token);
decoded = jwt.decode(token);
if (!decoded) {
throw new JsonWebTokenException('Unable to decode the token.');
}
// this check only applicable for AccessTokens
if (decoded.client_id !== this.config.cognito.clientId) {
throw new JsonWebTokenException('App clientId is invalid.');
}
} catch (error) {
if (error instanceof TokenExpiredError) {
throw new AuthenticationExpiryException();
}
throw new JsonWebTokenException('Token is invalid.');
}
return decoded;
}
private verifyToken(token: string): void {
const claims: VerifyOptions = {
issuer: `${this.config.parameters.aws.cognito.endpoint}/${this.config.parameters.aws.cognito.userpoolId}`,
algorithms: ['RS256'],
/*
audience claim is only applicable in case of IDToken verification but if you
are using AccessToken for verification then, you have to berify 'client_id'
claim of the token separately.
*/
audience: this.config.parameters.aws.cognito.clientId,
};
const pem = this.getPublicKey(token);
/*
STEP 3: Verify the JWT using known JWK and additional claims based on type of
token(ID or Access) you want to verify.
*/
jwt.verify(token, pem, claims, (err, decodedToken) => {
if (err) {
throw err;
}
if (!decodedToken) {
throw new JsonWebTokenException('JWTPayload not found.');
}
if (!['id', 'access'].includes((decodedToken as IAccessTokenPayload).token_use)) {
throw new JsonWebTokenException('token_use claim is invalid.');
}
});
}
private getPublicKey(token: string): string {
try {
/*
STEP 1: Verify structure of the JWT and return the header part of the JWT.
*/
const [headerEncoded] = token.split('.');
const buff = Buffer.from(headerEncoded, 'base64');
const text = buff.toString('ascii');
const header = JSON.parse(text);
/*
STEP 2: Find out JWK for the public-id that will be used to verify the JWT.
Since AWS Cognito Userpool has 2 RSA key pairs and one of them is used to
signt th JWT.
*/
for (const jwk of this.config.parameters.aws.cognito.jwKeys) {
if (jwk.kid === header.kid) {
return jwkToPem(jwk as any);
}
}
} catch (error) {
throw new JsonWebTokenException('Fails reading key.');
}
throw new JsonWebTokenException('Fails reading key.');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment