NodeJS Lambda Authorizer for JWT Access Tokens

The Golden Gate Bridge at sunset — photo taken by me while on my bike commute home the other night

Not paying for Medium? The gist of this post can also be found here: https://blog.concannon.tech/tech-talk/nodejs-jwt-lambda-authorizer/.

I recently created a Lambda Authorizer to secure AWS API Gateway endpoints with JSON Web Tokens (JWTs). I found that many tutorials exist for Lambda Authorizer creation, but I found a lack of examples for such a script in NodeJS. Here’s a nice diagram I created that depicts the Authentication and Authorization process using a web or mobile app, AWS API Gateway, a Lambda Authorizer, and OAuth2-issued JWT access tokens.

I wrote the authorizer function with two npm dependencies, both maintained by Auth0:

The jsonwebtoken package handles the logic behind token decoding, verification of the signature, checking for expiration, and checking for other options which you specify. Customize the verificationOptions variable to your liking (and make sure that you check the “audience” property to be sure that this token was issued for use at your resource server).

The jwks-rsa package handles the retrieval of your ID Provider’s signing keys. The code assumes that your ID Provider uses solely the RSA256 signing algorithm, but this can be changed in the verificationOptions algorithms parameter if it is not the case.

The code can be found below, and also at https://github.com/cconcannon/lambda-authorizer-jwt

const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const keyClient = jwksClient({
cache: true,
cacheMaxAge: 86400000, //value in ms
rateLimit: true,
jwksRequestsPerMinute: 10,
strictSsl: true,
jwksUri: process.env.JWKS_URI
})
const verificationOptions = {
// verify claims, e.g.
// "audience": "urn:audience"
"algorithms": "RS256"
}
const allow = {
"principalId": "user",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": process.env.RESOURCE
}
]
}
}
function getSigningKey (header = decoded.header, callback) {
keyClient.getSigningKey(header.kid, function(err, key) {
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
})
}
function extractTokenFromHeader(e) {
if (e.authorizationToken && e.authorizationToken.split(' ')[0] === 'Bearer') {
return e.authorizationToken.split(' ')[1];
} else {
return e.authorizationToken;
}
}
function validateToken(token, callback) {
jwt.verify(token, getSigningKey, verificationOptions, function (error) {
if (error) {
callback("Unauthorized")
} else {
callback(null, allow)
}
})
}
exports.handler = (event, context, callback) => {
let token = extractTokenFromHeader(event) || '';
validateToken(token, callback);
}

If you make changes to the code, you will need to run npm i in your root folder, then zip the index.js file and the node_modules/ folder to upload to Lambda.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store