ExpressJS and MongoDB App Authorisation with JSON Web Token

Panupong Viriyasuebpong
5 min readJul 21, 2021

Hi guys, I have been trying to learn ExpressJS REST API by building a just-for-fun project “Lift-Log”. At first, I was going to skip the authentication and authorisation processes altogether because the only client using the API would be myself. But then I thought I could also pretend that I am going expose the API publicly, which implies it will need some sort of security features, plus to gain as much experience as possible during this project is only beneficial, so here I sharing what I have learned about basic JWT authorisation working on my MongoDB/ExpressJS app, hope someone will find this useful!

First, let’s take a look at the schema, we have a simple MongoDB model with username and password for authentication, and data is well.. the actual data belong to the user. I am using a single list value here as an example for simplicity.

Now, if I have an endpoint that accesses or modifies the data under a specific user such as

GET '/api/:username'

I would like the user to be able to use this endpoint only if they have provided the correct username and password. One of the most simple ways to verify that is to include the username and password into the request headers whenever you are making a request.

But, of course, attaching your username and password to your request and sending them over every time may not be the neatest solution, this is when JSON Web Token (JWT) comes into play.

The idea is to have the users authenticate themselves once with the username and password, if successful, they will receive a signed JWT. The JWT contains a JSON object payload, which can be any information. In this case, we will simply use{ username: user.username } as the payload to specify the username the user logged in with. This token will only allow access to the data record with a matching username. And also note that the token, when generated, is signed with the server’s secret key, so we can verify and check the payload every time it is sent back with the requests.

Before logging in this is an example of a signup request.

'/signup' request body example:{
"username": "user123",
"password": "12345678"
}

This piece of code looks for username and password in the request body, checks if the provided username already exists, if it doesn’t, a new user record with the username and a hashed version of the password is created. I used the bcrypt library to avoid storing the passwords as plain texts on the database. The parameter 10 is the salt rounds, the higher the rounds, the more expensive it is to crack with brute-force. Next we will see the login request, how to compare the password to its hashed version, and how a JWT is generated.

In the /login endpoint above, we look for an existing record with a matching username. In line 11 we use bcrypt again to check if the password provided in the request can produce the hash that matches what is stored in the record when signing up. On successful authentication, we proceed to line 18, where we create a JSON web token with the payload being the username used when logging in, you can attach any information that suits your authorisation solution to the payload as long as it is in a JSON format. The token is then signed with a secret key only known to the server and sent back to the client.
To make further requests that involve the user’s data, the client will have to attach this token to the request header to prove that the authenticated has already been done and they are permitted to access the data. Let’s look at an example below.

Here have an endpoint trying to access data under a specific username, before allowing such a request, we would like to verify the token if it is authentic and was generated by the same user. The authorise middleware checks if the request header contains a token, if so, it tries to decode the token using the same secret key used in signing when the token was generated. In case the token is valid, the decoding should result in a JSON object containing the username. Note that before decoding the token, the first 7 characters were removed, that is because the token string starts with the prefix “Bearer ” followed by the actual token value. The username the request is access is them compared against the one from the token, if they match, the next() method is called to proceed from the middleware to the main request, otherwise, we cut the request short and send back an error message.

Now, let’s have a look at the code in action, I am using Postman in this example, it allows you to fire requests to your API endpoints without implementing any user interface or web browser, I really recommend it for anyone doing REST API development.

To start off, I signed up myself up with a username and a password.

Then I logged in with the same username and password to receive a token.

I tried to access the data of user123 I have just create without providing the token, the request got rejected.

Now with a valid token, I am able to access my own data without issues.

If I change the endpoint path to user567, the request got rejected, not because the token is invalid but because it only grants access to data under user123.

There it is, a basic authorisation solution I have implemented on my practice project, hope it was useful and you guys learned something from it. If anyone is interested in what I am doing please go check out the Lift-Log project repository. I acquire more techniques while working on this project, I’ll be making more articles about them, so stay tuned. For now, happy learning guys.

--

--