Generating and verifying JWT Tokens

Now that we have all of that out of the way, lets get into the ‘meat & potatoes’ of JWT and how we use it. There are two JWT functions that will handle everything in this example:

jwt.sign()

jwt.sign(payload, secretkey, [options, callback])

The first function jwt.sign() will generate a JWT token, assign it to a user object, and then return that JWT token so we can pass it where ever we may need. It can be either asynchronous or synchronous depending if a callback is supplied. The payload parameter will be the user object in our case, the secretkey is made up by you, and it can be anything. The callback parameter is where we handle sending our token, and the options parameter will be where can set an expiration time among other things.

Note: It is important that in production you NEVER HAVE YOUR SECRET KEY VISIBLE like in this example. This is not production code, it is merely an example of how JWT works. Your secret key should be stored in an environment variable, like all sensitive information.

jwt.verify()

jwt.verify(token, secretkey, [options, callback])

The second asynchronous function jwt.verify() will verify the users token when a protected route is accessed. It takes in the token as one parameter, the secret key that you defined in the jwt.sign() function, and then you have the options and callback parameters. The callback will be where we can access and send protected data.

Putting it All Together

Let’s take a quick moment to look at an overview of routes/api/userRoutes.js :

Starting from the top we are just importing JWT and our mock user model. Pretty self explanatory if you’re familiar with node, so let’s move onto discussing the routes and accessing them with POSTman.

Now the real fun. On line 6 we have a POST route found at /user/login that handles our mock login system. We check to make sure the posted username and password match our mock user, and if so we generate a JWT token for the user starting on line 14 by:

Passing in our user object, that in this case comes from the mock user model in models/dummyUser.js

A secret key that in this case is privatekey

An options parameter { expiresIn: '1h' }

Finally a callback that contains the parameters (err, token)

If an err is returned in the callback, we are sending a Forbidden (403) code to signify that access is.. well forbidden.

If there is no err returned in the callback, we allow access to the token that JWT has generated. Now when we passed in the user object {user} , this is how we ‘attached’ a token to the user data. This lets us identify a specific JWT token with a user’s data.

Logging In

Here’s what it looks like when we access /user/login via POSTman:

Making a POST request to login

So in POSTman I am making a POST request to the /user/login route with form data. I am passing in a username and password key/value pair to simulate the mock user logging in.

Notice that the password and username match that of our sole mock user. Since everything matched and the user was ‘logged in’, the jwt.sign() function found in the login route returned a unique JWT token. Perfect, exactly what we want. This token is what will be used to access our protected routes. In a production application, this would be sent to a frontend client like React to be used when the client makes requests to protected backend routes.

Just to lightly touch on the expiration date, your application would need to have some sort of logic that checks for an expired token so that it can handle sending the user back to a log in page to be given a new fresh token. For example, think if you were logged into your bank account. After a few minutes of inactivity, you would usually be logged out and required to log back in. This works two-fold because A) it logs you out of your session in case you forget to yourself and B) it gives the app a chance to refresh whatever authorization it’s using.

Requesting Protected Routes

Alas, the final step to this whole JWT authorization flow. Let’s start with another POSTman gif to show what we will be accessing, then I will explain what’s going on.

First this is what happens if we try to access a protected route without a JWT token:

Accessing a protected route WITHOUT a JWT token returns 403 Forbidden

The 403 is also thrown when the token is invalid. So just as the code dictated in the /user/login GET route starting on line 24 , when we fail to access a protected route with a JWT token, the callback in jwt.verify() returns err . This error lets us send out the 403 Forbidden bat signal to whatever failed to request the route.

On the flip-side, this is what it looks like when we get a 200 OK bat signal:

Granted access to a protected route with the JWT token passed in the Authorization header

Wait, what? You probably noticed I passed the JWT token in a header named Authorization with the GET request. You also probably noticed the added Bearer before the JWT token. Let me explain.

Authorization: <type> <credentials> is a pattern introduced by the W3C in HTTP 1.0. Sites that use this pattern are more than likely implementing OAuth 2.0 bearer tokens. The OAuth 2.0 Authorization framework sets another number of requirements to authorization secure. For example, requiring the use of HTTPS. Remember, HTTPS makes sending the token from the server to client more secure. Here is more info on the OAuth 2.0 Auth Framework.

So with that in mind, our Authorization header requires Bearer as the type, with the JWT token being the credentials. Knowing this, it makes the explanation for the checkToken() function found on line 45 make a little bit more sense. This function is passed into our protected route like so:

app.get('/user/login', checkToken, (req, res) => { //Callback });

In the checkToken() function, we check to make sure the token is not undefined, and then we split req.header into an array. This is because the Authorization header comes back as a string. For example: Bearer jh3uj3jedjd3 .

function, we check to make sure the token is not undefined, and then we split into an array. This is because the Authorization header comes back as a string. For example: . We know that the split() method turns a string into an array of sub-strings. So we can safely assume that our now split header looks like ['Bearer', 'jh3uj3jedjd3'] . That is why on line 50 we set const token = bearer[1] with an index of 1. It’s clear from the example that the token is at index 1 in the array.

method turns a string into an array of sub-strings. So we can safely assume that our now split header looks like . That is why on we set with an index of 1. It’s clear from the example that the token is at index 1 in the array. After this, on line 52 set set req.token equal to the token we get from the Authorization header. Then we use next() to invoke the next route handler.

set set equal to the token we get from the Authorization header. Then we use to invoke the next route handler. Finally, we handle an undefined header by sending a good ole’ fashion Forbidden 403 .

Accessing the Protected Route

So, we’ve passed an Authorization header with the token to the protected route. We’ve verified the token to not be undefined, and we have a stripped away the Bearer string from the header in the checkToken() function, leaving us with just the token. Now, we need to use the last piece of the puzzle: jwt.verify() to gain access to the authorized data. Here’s how this works.

jwt.verify(token, 'privatekey', [options, callback]) will use req.token as the token parameter, in this case 'privatekey' as the secret key, and then our call back will look like: (err, authorizedData) => { //callback } . If the err parameter is returned, just like the others will signify to then return a Forbidden 403 response to let whoever know the token verification failed. If we pass an incorrect secret key here, we will always get back a 403 response code.

The authorizedData parameter is the bread and butter. This contains all of the protected data that we requested. Here is what the user data looks like when accessed successfully from POSTman: