Serverless JWT authentication with Netlify and Zeit

When using AWS Lambda you can use the API Gateway and it works great.
However for personal projects I prefer going with Netlify or Zeit, much simpler and faster to setup but without the API Gateway.

In this article we will see together how I came to use function wrappers to help me handle authenticated routes on lambdas.

Note: We all know server side authentication topic is super wide, this article will only focus on JWT authentication.

Authentication with lambdas

My issue was simple. How do i make lambdas that will run only for authenticated users or admin specifically.
Let’s see together the options I discovered and their pros and cons i had.

Using Express

Since we are looking at nodeJS lambdas, we are able to reuse production ready user authentication frameworks. A well known one would be Passport with Express. If you are used to Passport in the first place or if you are looking to handle by yourself multiple login providers then it can be a good choice.
Good resources have already been created regarding this topic, here are some:

https://www.netlify.com/blog/2018/09/13/how-to-run-express.js-apps-with-netlify-functions/

https://markus.oberlehner.net/blog/implementing-an-authentication-flow-with-passport-and-netlify-functions/

However, I felt a bit off to setup an express framework when using lambdas, as if it was defeating the purpose of small bits of codes with only the minimum required. Additionally since i only want to verify a JWT token i didn’t feel the need for Passport.

Using inline checks

Here a simple example of how to perform inline checks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
exports.handler = function(event, context, callback) {
if(!await isRequestAuthenticated(event)){
return callback(null, {
statusCode: 401,
body: "Not Authenticated"
});
}
const body = performSomeComplexComputations()

return callback(null, {
statusCode: 200,
body,
});
};

This method is simple and easy to understand. However what bothered me was:
Mixing authentication code and operational code.
You do not completely abstract away the authentication code making annoying to reuse. If i had to protect another route i would have to reimplement the same unauthenticated response logic.

I quickly dismissed it and searched for another method.

Using Wrappers

Working with React at Sqreen for few years now made me think of one way to abstract logic in React, function wrappers. I tried to implement a simple function wrapper to help me handle authentication and see where it can lead me.
Lambda code

1
2
3
4
5
6
7
8
9
10
11
const editAnArticleLamda = function(event, context, callback) {
const editedArticleList = editArticles();
return callback(null, {
statusCode: 200,
body: editedArticleList,
});
};

// onlyAuthenticatedUsersWrapper is a wrapper that guarantee you that editAnArticleLamda
// will only be executed if the request is authenticated
exports.handler = onlyAuthenticatedUsersWrapper(editAnArticleLamda);

Wrapper code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const jwt = require('jsonwebtoken');

module.exports = fn => (...args) => {
const [context] = args;
// using context.queryStringParameters.token as a token holder is for the sake of the demo.
// use cookies or an authorization header if you want to use it in prod
if(!context.queryStringParameters.token) {
return unauthResp(...args);
}
jwt.verify(context.queryStringParameters.token, 'mySecret', function(err, decoded) {
if(err) {
return unauthResp(...args);
}
return fn(...args)
});
};


const unauthResp = (event, context, callback) => {
callback(null, {
statusCode: 400,
body: 'Not authenticated',
});
};

Using a wrapper is as explicit as the inline one and it’s easier to split concerns now.
You do not mix authentication code with operational code and you can focus on what your function should do.

The best part is that it’s easily reusable, can be extended to handle more additional login types and even combined with other wrappers.
I let you checkout this code repo if you want to run some code.
https://github.com/sqreen/article-serverless-auth-example

Conclusion

I hope that this article gave you some ideas on your next project!
If it piques your curiosity you can also have a look at pipe() and compose() that would allow you to easily chain wrappers. Check out this article for more info:
https://medium.com/free-code-camp/pipe-and-compose-in-javascript-5b04004ac937

Note: When working on this article I stumbled upon https://github.com/middyjs/middy, a pretty cool middleware engine that also help you do the job.