Last active
April 27, 2023 14:29
-
-
Save afraz-khan/65ebd02326480ac96e4ef9c066e0dbea to your computer and use it in GitHub Desktop.
AWS Cognito JWT Verification Algorithm | TypeScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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