Image for post
Image for post

JWT Authentication in Express.js

A week ago I’ve spoken about basics of JWT; what are they, why they’re great alternative for cookies, and also, I briefly described different ways of authentication with their usage, as well as ways of matching identity, based on provided token.

Today I’d like to show you, one method on real life example. As Node developer, I’ll naturally demonstrate that on the Express project, although (I believe) similar pattern might be applied on majority of modern backend environments.

Introduction

As this post is about JWT implementation, I’ll assume you already have have at least basic project. Therefore, I should mention, that the file structure will probably differ. Here’s the structure for my project:

+-- package.json
+-- app.js
+-- middlewares.js
+-- libs
| +-- auth.js
+-- models
| +-- User.js
| +-- index.js (exports all the files)
+-- routes
| +-- auth.js
| +-- dashboard.js
| +-- index.js (exports all the files)

So as you see, that’s probably just typical express project. Nothing fancy.

  • libs directory stands for libraries. All the logic goes there.
  • models is where we store all the DB models, mongoose in my case. Not much related JWT happens there.
  • routes is self explanatory — all the routes are stored there. Auth in this case.

Baby Steps

I’ll skip the npm init step, as I expect you to know at least the basics of Node and it’s package manager environment. I’ll also skip express setup, as that’s not topic of this thread. So first step after that, would obviously be… installing JWT!

Here’s the JWT package: NPM

So go ahead and type:

npm install jsonwebtoken --save

Once we got that, we will have to import JWT into our project. So, where do we put it in? In my case, as I follow modular pattern and I split the logic into smaller pieces — all JWT gonna be available in /libs/auth.js, which routes and middlewares will be requiring.

libs/auth.js

Let’s start with bit of controversy. As you might or might not know — JWT’s verify method is completely synchronous, although it’s available in two forms — sync and async. The only difference between two of those is their format, but they both do just the same work underneath. If you chose to go with synchronous option, that’s fine, just remember you can not simply set it to the value — you’ll have to wrap it up with try{} catch{}, as the function throws the error!

I like to go with async way, I believe it’s more classy. Also, here’s another controversy — I’m wrapping few lines of code into a Promise, “what’s the point to split it like that” you may ask? First of all, I wouldn’t like to share any kind of logic, on my routes, and also, by splitting it like that — I can always reuse the whole auth system.

Here’s bit more, to speak about, especially more to explain, but of course — more controversy as well!

So, what’s happening here? We start with passing a details object, as you can see on line 8–11 — i I’m making sure it actually is an object. Why an object? Well, object because it’s easier to manipulate with data that way, and is easier to pass single object, formatted up to you, instead of having to remember the whole param list. Right?

So, then we make sure the we got set token’s maxAge, and so it’s in valid format. If not — we set it to 3600 secs.

Now. Here we got the controversy I mentioned. I used lodash, in order to filter out functions and any values, representing the password — because we don’t need functions to be passed with our payload, and two — because we neither want to publicly share the password. That’s just in case, if we were not careful enough, not to get it out earlier. This definitely isn’t a musthave, and even more — usage lodash is not essential. But is easier. Much easier.

So we got to the point, where we finally sign the token. JWT aren’t following my philosophy of using objects where you can, so we got to remember the order of those arguments. First one is a payload. Then, we pass the secret — remember, this can not be a random string, as you gotta validate your token with it afterwards — and the options object.

Then we of course return the token — as this is synchronous process, we could easily return it straight from the function, that’s just my personal preference, to assign it to the variable.

I’ve set only two params by myself, although there’s more of those, some might be really helpful. Here’s the cheatsheet:

  • algorithm (default: HS256)
  • expiresIn: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, "2 days", "10h", "7d"
  • notBefore: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, "2 days", "10h", "7d"
  • audience
  • issuer
  • jwtid
  • subject
  • noTimestamp
  • header

Don’t forget to export both functions. If you’re developing in ES5 — that doesn’t count you, but for ES6 guys — remember to do so.

Implementation!

Time to merge what we made so far into an actual, working system. Let’s start with middlewares.

So what, what do we need? Of course, we got to import our shiny verifyJWToke, promise, we created. If you’re not into ES6 — that’s what we import exact function like. With require, you’ll require the library first, then assign the func to a separate variable (or not).

But hey! Another controversy; where we were suppose to look for the token? In the query? That’d be good idea. But why not to make use of the header, and why not to pass it along with other data? That’s completely up to you, I chose to pass the token inside the body.

But what’s below that. That is really important. As we verify the token, we receive it’s payload. So, referring to my recent post, about techniques of JWT implementation — if you decided to go with white/blacklist solution, that’s where you’ll query your Redis server. Although I did not, I chose to store email and user_id details in public key, so I can find user’s identity if I need to. But, that’s what login route is for, we will come to that soon. So I save the payload to req.user and I go foward.

Yeah, but what if token was invalid, and we got an error? No problem, we simply respond with status 400, or status 403. Both codes are valid, as provided token had to be either fake or expired.

So. Tonight’s the night. We finally gonna make our token based login route. Again, as I said above multiple times — I assume you already know the basics of Node and Express, and I won’t show you all the mongoose models, or additional middlewares for validating body params user passed.

So, except importing express, defining new router, our user model and param checking middleware, we are also importing the lib we made. Or just it’s function. First few steps are just like any other authorisation system:

  1. we query database for the user
  2. we compare provided password, with the one in the returned document’s
  3. we convert our Mongoose model into JSON format and strip it off, of any confidential data.

Easy, right? So I definitely won’t cover this here. What we’re interested in, is what happens after, which is the response. We send the token back, in the response, together with status 200. Response format is really up to you, I always try to follow the same pattern — I respond with JSON, and if request was successful — I add “success” key.

What’s more exciting, is what’s below. The token key/value. Because we’ve put pressure on modularity — we don’t have to do any overthinking, we simply assign to the key, the result from the function, we earlier created.

I won’t be showing you the signup route, as JWT implementation is pretty much the same — you create a user, and create a token, based on it’s parsed result.

No, I did not forget about the middleware we created. Let’s make some use of it! So, just to clarify:

  1. user tries to access the dashboard
  2. user is verified for his token
    a) token is provided and is valid — user get’s passed in,
    b) token is either not provided or is invalid — middleware cuts in, and responds with status 400.

Summary

Wow, that’s quite a long post, but mostly because I was trying to explain you all the aspects, that you might question, especially if you’re a newbie. As you can see, token authorisation is quite easy to implement, although it requires a bit more work, but in my personal opinion — not enough, to stop me from using it, for my REST implementations. The whole process can be shrinked even more, but that’s where you got decide whether you want your code to be modular or not.

Written by

JavaScript Developer. Email me at me@polethedev.com

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