/** * The MIT License (MIT) * Copyright (c) 2022 PaperNick * * Resouces used: * https://github.com/auth0/node-jwks-rsa * https://github.com/auth0/node-jsonwebtoken * https://auth0.com/blog/navigating-rs256-and-jwks/ * https://gist.github.com/westmark/faee223e05bcbab433bfd4ed8e36fb5f * https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ */ const AUTH0_AUTHORITY = 'https://auth0.tenant.com'; const AUTH0_AUDIENCE = 'https://api.yourapp.com/'; const jwksRsa = require('jwks-rsa'); const jwt = require('jsonwebtoken'); const jwksClient = jwksRsa({ jwksUri: `${AUTH0_AUTHORITY.replace(/\/+$/, '')}/.well-known/jwks.json`, }); /** * Attempt to parse the given JWT token. Throws an Error if token is invalid. * * @param {string} token JWT token without "Bearer " in the beginning * @throws {Error} * @returns {JwtPayload} */ async function parseJwtToken(token) { if (!token) { throw new Error('Missing authorization token'); } // Decode without verifying if the signature is valid. // Warning: do not access decoded token payload before verifying with jwt.verify() const decodedToken = jwt.decode(token, { complete: true }); if (!decodedToken || decodedToken.header.alg !== 'RS256') { // Only RS256 tokens are supported at the moment throw new Error('Invalid token or algorithm'); } let signingKey; try { signingKey = await jwksClient.getSigningKey(decodedToken.header.kid); } catch (error) { throw new Error('Could not retrieve key to verify token'); } try { const jwtPayload = jwt.verify(token, signingKey.getPublicKey(), { audience: AUTH0_AUDIENCE }); return jwtPayload; } catch (error) { throw new Error('The token is invalid or has expired'); } } /** * Attempt to parse the given JWT token. Returns undefined on error. * * @param {string} token * @returns {JwtPayload|undefined} */ async function parseJwtTokenQuiet(token) { try { return await parseJwtToken(token); } catch (error) { return; } } module.exports = { parseJwtToken, parseJwtTokenQuiet };