Login App with CSRF protection – Implement authentication in Node.js using JWT access token and refresh token – Part 2

In this article, we will show you how to implement authentication in Node.js using JWT access token and refresh token. As we have already discussed about the implementation flow of the authentication a.k.a secure login app with CSRF protection in the previous article. So we will cover only Node.js implementation in this second part of the series.

Node.js API Authentication With JWT, Add Login Using the Authorization Code Flow, Refresh Tokens With JWT Authentication, JWT Refresh Token for Multiple Devices, token based authentication in node js example, bearer token build a node.js api authentication with jwt tutorial, step by step explanation of the authentication in Node js using JWT access token, refresh token and CSRF token. JWT authentication in backend technologies like laravel, php, etc, Requesting access tokens and authorization codes. Secure authentication in node js using JWT access token, refresh token, CSRF protection and XSS protection.

We had already discussed the authentication with Node.js in the previous articles where we explained the basic way to manage the authentication using JWT token and moreover here we will learn about the security.

We planned to divide this article into three parts.

Way to create secure REST API in Node.js using JWT token with CSRF protection

  1. Create simple REST API in Node.js
  2. Install npm dependencies
  3. Define the environment variables
  4. Manage general utility
  5. Create API for user sign in
  6. Create API for user sign out
  7. Create API to verify token
  8. Create API to get user list
  9. Implement middleware to validate the token
  10. Output

1. Create simple REST API in Node.js

As we are planning to create a REST API in Node.js so we should have an initial setup of the Node.js application. Checkout this link for your reference.

Below is the file structure of the application that you should prefer.

File Structure - Clue Mediator
File Structure – Clue Mediator

2. Install npm dependencies

We should have the following dependencies in the Node application.

  • dotenv – It helps us to load the .env variables into process.env. With the help of it we can access those variables throughout the application.
  • cors– To access resources from the different server, we have to enable the Cross-Origin Resource Sharing (CORS) request.
  • jsonwebtoken– We’ll use this package to create access token and refresh token by passing the primary JSON data and secret key.
  • body-parser– It used to parse the incoming request and make it available under req.body property.
  • cookie-parser – This package is used to parse the cookies and also used to enable the signed cookie support by passing a secret key.
  • rand-token – Additionally we will use this package to create a random token for CSRF protection.
  • moment – Here we’ll use the moment npm package to set the expiry date of the token.
  • ms – Use this package to convert the time format to milliseconds.

Run the following command to install the required dependencies.

After successful installation, We will use these packages in several files for different functionalities. For now your server.js file should look like this and later on we’ll see the utils.js file.

server.js

In above code, we have added only those packages that are used in this file only and rest of the packages we will add it in utils.js file.

If you noticed that we have set http://localhost:3000 to origin in configuration of the CORS package that means we will allow only this url to access our API. Also we will use secure HttpOnly cookies for authentication so we need to set credentials: true in the same configuration.

For secure HttpOnly cookies, we have to use a secret key for cookie parsing. Here we are assigning the cookie secret from the environment file that we covered in the next topic.

3. Define the environment variables

Let’s try to create a .env file to define the environment variables and access it through process.env.

.env

Following keys to use in the project.

  • JWT_SECRET – Use it to create JWT access token and refresh token.
  • ACCESS_TOKEN_LIFE – Define the life of the access token so we can use it to set the expiry time of the access token. We have considered the 15 mins (15m).
  • REFRESH_TOKEN_LIFE – Same as the access token we will define the life of the refresh token. It should be longer than the access token so we have considered the 30 days (30d).
  • COOKIE_SECRET – We’ll use this secret key for cookie parsing as we discussed in the above topic.
  • NODE_ENV – It’s used to set the environment of the project.

To use all these variables, we have to add require('dotenv').config(); at the top of the server.js file that we already added in above server.js file code.

4. Manage general utility

Now it’s time to create the several functions that help us to create an authentication API. We have listed the following utilities.

  • generateToken

    This function will be used to create an access token, CSRF token and expiry time with the help of the jsonwebtoken, rand-token and moment npm packages.

    In the first step, we will create a plain object to generate an access token. We will also create a CSRF token in the same function so that can be used to create a private key for the access token.

    Let’s create a private key by combining the CSRF or XSRF token and JWT secret key that is defined in the .env file. CSRF token will help us to identify the real user when someone calls the API from the frontend side.

    So now we are able to create an access token with the help of the plain object and the private key. Also we will set the life of the access token.

    At last we have to create a variable that contains the expiry time of the token which will pass in the API response.

    We’ll call this function to create an access token and CSRF token before it’s expired. For us we have to generate tokens in every 15 mins.

  • generateRefreshToken

    We will create another function that will return the refresh token using JWT. We will consider the user id, JWT secret and expiry time to create a refresh token.

    Here we will not use the XSRF token as a private key to generate refresh token because XSRF and access token are required to generate in short time but refresh token have a longer life than access token.

  • verifyToken

    Let’s create a function that will be used to verify the both access and refresh token. We will use xsrfToken as optional parameters to create a private key for both tokens.

    We will pass the empty string for xsrfToken to verify the refresh token. We should have a callback function to process the further request that will be received via the third parameter.

  • getCleanUser

    As per the previous article of the authentication REST API, We also need a simple function that returns the basic user information so we call pass it to the user in the API response.

  • handleResponse

    Let’s create a function that helps us to create the response based on status code and some other required details.

  • clearTokens

    At last, we need a function that helps us to clear the tokens from the memory and cookie.

    Here we defined the variable refreshTokens to manage the CSRF or XSRF token that links with refresh token in the form of the object. You can also manage it on redis server.

    Also we will have a cookie option to create a secure HttpOnly cookie for refresh token and the same option will be used to remove token from the cookie. You can set the domain as well.

    To access a signed cookie, we have to use the signedCookies object of req.

Let’s combine all code together and see how it looks.

utils.js

5. Create API for user sign in

Before we create an API, we can create a static list of the users that used to validate the user’s credential. In your case it should be a database.

We will also add all dependencies in server.js file from the utils.js file.

Let’s create an API for sign-in where users can get authenticated by passing the username and password. On successful authentication, we will return the access token and expiry time along with the user details.

In the above code we have added the validation for user credentials and create a plain object from user data. Also we are creating the access token, refresh token and CSRF/XSRF token with the help of the previously created function.

We will store the XSRF token in the variable refreshTokens that link with the refresh token in the form of the object. Also we will set XSRF and refresh tokens into the cookie so we can pass it along with the API response.

In the subsequent private requests, we will use all of the tokens (access token, refresh token and XSRF token) for verification when it is required.

6. Create API for user sign out

Now let’s create an API to manage the user logout. In this API, we have to simply call a function to clear tokens.

7. Create API to verify token

Let’s have another API to verify the token. This API will be used to manage the silent authentication. Please refer to the link to get more idea about the silent authentication.

In the above code, we are trying to get the refresh token from the cookies. If it’s not exist then we simply return the no content success response.

In the next step, we have to verify the XSRF token to prevent the CSRF attack.

After validating XSRF token, we will try to verify the refresh token with the help of the predefined function. Here we passed the xsrfToken as an empty string into the function because we have generated refresh token without XSRF token.

On successful validation, we have to return the user details, access token and expiry time into the API response.

No need to generate a new refresh token in this API request.

8. Create API to get user list

In this API, We have to created a private API that will return the list of the users. Without an access token you can not get this list. We have added authMiddleware middleware to validate the route. In the next point, we will explain about the middleware.

Additionally, we have removed the password from the list before sending it via API response.

9. Implement middleware to validate the token

The middleware is the key part of the article to verify the private routes. It used to verify the access token and CSRF token from the request header.

If we will not receive the token then we can consider as unauthorized user. If a token exists then try to get the XSRF token and verify it to avoid CSRF attack. After that we need to verify the token with the help of XSRF token.

If a user gets authenticated using access token and CSRF token then attach the user object in the same request so we can get the object in next routes.

10. Output

We have already provided the .env and utils.js files so now let’s combine all of the above code together and see the server.js file.

server.js

Now let’s check these APIs in postman. Below we have provided you a collection of the APIs. If you don’t get an idea then read the description in the postman API collection.

Output - Login App with CSRF protection - Implement authentication in Node.js using JWT access token and refresh token - Clue Mediator
Output – Login App with CSRF protection – Implement authentication in Node.js using JWT access token and refresh token – Clue Mediator

That’s it for today. Thank you for reading.

In the next article, we’ll implement authentication in ReactJS using REST API with CSRF protection.

Like share and follow us. Happy Coding..!!

Demo & Source Code

Github Repository Postman Collection

You may also like...

10 Responses

  1. Tobias Dethleffsen says:

    Hei very nice article. I think there is one mistake in your article. In the overview of the server.js you have forogt the authmiddleware.
    This is the code from the complete server.js
    // get list of the users
    app.get(‘/users/getList’, (req, res) => {
    if (!req.user)
    return handleResponse(req, res, 401);

    const list = userList.map(x => {
    const user = { …x };
    delete user.password;
    return user;
    });
    return handleResponse(req, res, 200, { random: Math.random(), userList: list });
    });

    and this ist the code from the methode.

    // get list of the users
    app.get(‘/users/getList’, authMiddleware, (req, res) => {
    const list = userList.map(x => {
    const user = { …x };
    delete user.password;
    return user;
    });
    return handleResponse(req, res, 200, { random: Math.random(), userList: list });
    });

    • Clue Mediator says:

      Hello Tobias,
      I forgot to update the code in the article. Both codes will work but the new code is better than the previous one for large scale application.

      I have updated the new code.
      Thank you for drawing our attention.

      Let us know if you are facing any difficulties.

  2. Karl says:

    How do you protect yourself from login CSRF? An attacker could enter his information without the consent of the host and watch the activity of the host after. How would you prevent that?

    I really appreciate an answer of yours 🙂

    Btw, your articles are well made. It’s hard to find content right now that don’t use localstorage as a way to manage token. You are giving a full example of how to made it compare to others who tend to implement partially or in an abstract way that is hard to get for beginner in web technology.

    • Clue Mediator says:

      Hi Karl,
      In the CSRF attack, the attacker can not read the cookie because of the cross-site domain. BTW here we have implemented the demo of the login where we implemented CSRF protection after the login. You can also manage it throughout the application. It’s all depends on you that how would you like to set up your project architecture.

      BTW thank you for your kind words!

      After a lot of hard work and searching, we are able to write this article. If you have any feedback for this approach (good and bad), do let us know in the comments. So we can share it with all other readers.

      Thank you!

  3. Jun says:

    Just found a goldmine. I do really appreciate the valuable resources you guys have made on this place.

  4. O says:

    Hi Clue Mediator,

    I appreciate this article! This really helped! I do have a question though — if I have another API on another server that I also want to secure. How do I ensure that with that other API, only users authenticated through this API are allowed?

  5. O says:

    Am I allowed to use this for my application?

Leave a Reply

Your email address will not be published. Required fields are marked *