Tommi's Scribbles
How to Create an AWS Lambda JWT Authorizer Function
- Published on 2022-01-29
When you are creating an application that interacts with cloud services, security is one key consideration. AWS has a nice feature called an authorizer function. This means you can create an AWS Lambda function, that is called before access to the other Lambda functions in the API Gateway is allowed.
We are going to write a simple JSON Web Token based authenticator function, that can be used as is, or alternatively you can use it as a base for adding other authentication methods. ### JSON Web Token Based node.js Authorizer As AWS has good tutorials and write-ups in their documentation on how to create AWS Lambda functions and how to set up the authorizer function in the AWS API Gateway, I will not cover those here. This is even more true as my preferred method is using AWS CDK to deploy resources, including the authorizer.
Once you have your setup ready, we can start writing the Lambda. As node.js has a good JSON Web Token library available, you can just start by requiring the library.
const jwt = require("jsonwebtoken")
Next, we can define the secret key for hashing here. I prefer using a Lambda environment variable for this, but for simplicity we hardcode the key to the code. Change the value of the key to whatever you want to use and consider a good key.
const secretKey = 'aSUPERsecretKey'
I like to add a relatively large amount of logging to my lambdas, but feel free to leave them out. First I log the creation of the Lambda function.
console.log ( "Starting authorizer function" )
We then create the Lambda Handler function, get the passed token, and log the token we are authorizing. This is to debug access issues: you could have your client app print the token to the console before calling and make sure they match.
exports.handler=function( event, context, callback ) { let token = event.authorizationToken console.log("Authorizing: " + token)
When we have the token in a variable, we then check the token was actually present. If not, we can do an early out.
if(!token) { console.error("Authorization failed due to missing token.") callback("Error: Missing token") return }
If the token was present, we use the JSON Web Token library to verify the token. If there was no error or in other words if the token is valid, we log the access was allowed and generate a policy to access the method. If there is something wrong with the token, we log the denied access and generate a policy to deny access.
jwt.verify(token, secretKey, function (error, payload) { if(!error) { console.log("...allowed access for token.") // NOTE: You can replace the "user" here with whatever you // use to generate the token, e.g payload.email callback ( null, generatePolicy ( "user", "Allow", event.methodArn ) ) } else { console.error("...denied access for token.") callback(null, generatePolicy("user", "Deny", event.methodArn)) } })
That is it for the handler function. Be sure to add the final curly brace to close the handler function out!
Next, we will create a helper function that generates an access policy. The example is pretty much the same AWS gives in their documentation.
// Help function to generate an IAM policy const generatePolicy=function( principalId, effect, resource ) { let authResponse={} authResponse.principalId=principalId if ( effect && resource ) { authResponse.policyDocument={ "Statement": [ { "Action": "execute-api:Invoke", "Effect": effect, "Resource": resource } ], "Version": "2012-10-17" } } // You can also add custom properties to your response. authResponse.context={ "booleanKey": true, "numberKey": 123, "stringKey": "stringval" } return authResponse }
Conclusion
And that is it. Now, unless the client passes the correct JWT authorization token in the header of the request to your AWS APIGateway path protected by this authorizer Lambda function, they will be denied access.