Skip to content

Authentication API that implements a refresh token rotation scheme and token reuse detection using Node.js, Express and Typescript πŸ”’

License

Notifications You must be signed in to change notification settings

rafaelfl/express-typescript-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Authentication Node.js API

Licença Coverage Status PRs Welcome code style: prettier

If you like this project then give it a Github star! 🀩⭐

About

Authentication API that implements a refresh token rotation scheme (based on JWT tokens) and token reuse detection using Node.js, Express and Typescript

Table of Contents

=================


πŸ’» Project Description

This project showcases an authentication API built with Node.js and Express.js, featuring the following major functionalities:

  1. User registration
  2. Sign in/Sign out
  3. Refresh access tokens
  4. Retrieve and update user profiles
  5. Admin features (create new accounts, retrieve complete user lists, retrieve and update individual users, delete users)
  6. Mock protected routes
  7. Retrieve sample data with pagination

The project was developed using the following technologies and tools: Node.js, Express, Husky, lint-staged, eslint, prettier and commitlint with conventional commits.

To store user data, sign-in tokens, and serve dummy data (i.e., the Airbnb sample data), the API utilizes a MongoDB server. Additionally, a Redis server instance is employed to block access to signed-out access tokens, thereby preventing unauthorized access by unexpired access tokens.

You can try out this API by running it in your local environment, within a Docker container, or by accessing it directly from the web. The installation instructions vary depending on the target environment.

πŸ’Ύ Database schema design

MongoDB database schema design

πŸ”‘ How Refresh Token Rotation Works

Refresh token rotation is a secure technique used to manage authentication tokens that keep user sessions active. When authenticating with user/password credentials, the API generates two types of tokens: the refresh token and the access token. These tokens can be opaque strings or use a specific technology like JWT (JSON Web Token).

The refresh token should be stored as a browser cookie with the following flags: (a) Secure - it can only be transferred over HTTPS connections; and (b) HttpOnly - it cannot be accessed using JavaScript code. On the other hand, the access token is returned in the API response body and should be stored in the browser memory (preferably avoiding the use of the localStorage due to security concerns). The access token should have a short lifespan for increased security, meaning it should expire within a short period.

To access restricted API endpoints, all requests need to include the access token in the Authorization Header. The API validates the access token, granting access if it's valid. If the access token has expired, the client needs to refresh both the access token and the refresh token. If the old tokens are still valid, they should be temporarily stored in MongoDB and Redis to prevent unauthorized access attempts.

If the current refresh token is not valid during the token refresh process, it indicates that the user needs to perform a new authentication. The following image illustrates the communication flow between the client and server:

Refresh token rotation flow

(image credits to Supertokens)

βš™οΈ Prerequisites

Before starting, you need Node.js, Yarn and Git. Docker also needs to be installed and configured, since this application depends on MongoDB and Redis servers.

# Clone this repository
$ git clone https://github.com/rafaelfl/express-typescript-auth

# Enter in the project folder in terminal/cmd
$ cd express-typescript-auth

πŸš€ Installation

After installing the tools and the source code, you need to configure the project using a .env file. Using that file one can customize the API secrets and configuration. An example file .env.example is provided and can be copied and updated. The content and configuration variables available in the .env file are:

DATABASE_URL="<MONGODB DATABASE URL>"
DATABASE_USER="<MONGODB ACCESS USERNAME>"
DATABASE_PASSWORD="<MONGODB ACCESS PASSWORD>"

PORT=<PORT NUMBER (3000 IS THE DEFAULT)>
SALT=<PASSWORD HASH GENERATION SALT NUMBER (10 IS THE DEFAULT)>

REFRESH_TOKEN_PRIVATE_KEY="<SECRET KEY USED TO ENCRYPT THE JWT REFRESH TOKEN>"
ACCESS_TOKEN_PRIVATE_KEY="<SECRET KEY USED TO ENCRYPT THE JWT ACCESS TOKEN>"

REFRESH_TOKEN_EXPIRATION="<EXPIRATION TIME FOR THE REFRESH TOKEN>"
ACCESS_TOKEN_EXPIRATION="<EXPIRATION TIME FOR THE ACCESS TOKEN>"

COOKIE_DOMAIN="<DOMAIN TO BE USED WHEN CREATING THE REFRESH TOKEN COOKIE>"

REDIS_URL="<REDIS DATABASE URL>"

The REFRESH_TOKEN_EXPIRATION and ACCESS_TOKEN_EXPIRATION can be expressed as a time formatted string with a value and a time unit, such as: "5h", "40m", "320". They accept "h" for hours, "m" for minutes and any other value is considered as seconds (important: the "s" for seconds is NOT supported - any other numerical value is considered as seconds by default).

After configuring the .env file, you can start installing the dependencies, building and running the project.

🏑 Local installation

To run the project locally, you need to make sure you have MongoDB and Redis servers running locally. The necessary URLs and credentials should be configured in the .env file. If you don't have the servers running locally, you can utilize the servers available in the Docker environment (follow the instructions available in the Section Running the project in a Docker container), or you can connect to remote instances of MongoDB and Redis servers (such as, MongoDB Atlas and Redis Cloud).

Before running the API service, the MongoDB database needs to be populated with initial data. To accomplish this, you must install the MongoDB database tools on your system (you can follow the installation instructions available in this link). After the installation, the mongorestore command should be available for use in the current path.

# Verifying the mongorestore version
$ mongorestore --version

mongorestore version: 100.7.3
git version: ad89a1c6dbe283012012cf0e5f4cb7fb1fcdf75d
Go version: go1.19.9
   os: darwin
   arch: arm64
   compiler: gc

With mongorestore available in the current path, you can execute the following command to seed the database:

# Seeding the database with initial data
$ yarn db:seed

IMPORTANT: If you are running the API service in your local environment, ensure that your MongoDB and Redis URLs are configured to point to your localhost (127.0.0.1) or other appropriate hosts. The default settings in the .env.example file specify the hostnames mongo ("mongodb://mongo:27017") and redis ("redis://redis:6379"), which are accessible only within the Docker runtime environment.

Once you have installed the necessary tools and obtained the source code, the next step is to install the required dependencies, to build and run the project.

# Install dependencies
$ yarn install

# Build project
$ yarn build

# Run the project
$ yarn start

In case you need to run the code in development mode, you can use the following command:

# Run the API in development mode
$ yarn dev

In the development mode, any change in the API source code is automatically reloaded and applied.

If you are using the PORT=3000 environment variable (the default), the application will be available on http://localhost:3000.

Some other interesting commands:

  • yarn clean - clean the build files
  • yarn dev - run the service in watch mode (with nodemon)
  • yarn build - build the page for deploying
  • yarn start-ts - run the application directly transpiling from the Typescript files (with ts-node)
  • yarn lint - run the linter to identify some problems in code
  • yarn lint-fix - run the linter to identify and fix problems in code
  • yarn prettier - run the prettier formatter
  • yarn test - run the integration tests
  • yarn test-coverage - run the tests coverage

🐟 Docker installation

To run the project directly in a Docker container, you can build and run the image using the docker-compose command.

# Build and run the current Docker image
$ docker-compose up -d

Running the command in the project directory initiates the installation of dependencies, builds the image, and runs the container. Following this process, the service should be operational, utilizing the configuration specified in the .env file.

For the API service to have access to MongoDB and Redis instances, the .env file used during container construction should use the hostnames mongo and redis (since they are used to provide access to other containers). If you wish to seed the MongoDB instance running in the Docker container, directly from your system, update your local .env file to utilize localhost (127.0.0.1) servers before running yarn db:seed.

The applications will be available in the following ports:

  • Authentication API - http://localhost:3000
  • MongoDB - mongodb://localhost:27017/authusers
  • Redis - redis://localhost:6379
  • Mongo Express (used to manage MongoDB through a web interface) - http://localhost:8081/db/authusers/

IMPORTANT: To utilize the Dockerized MongoDB and Redis servers, while running the API service locally, follow these steps:

  1. Start the containers containing the MongoDB and Redis servers using the appropriate command for your Docker setup (i.e., docker-compose up -d);
  2. Stop the API service, executing the following command:
# Stop the API service container
$ docker stop server

By following these instructions, you can have the MongoDB and Redis servers running in Docker and available to be used by the local API service instance.

πŸ•ΈοΈ Testing the project online

This API was deployed on Render and it is available for testing through the following URL:

Authentication API

IMPORTANT: When accessing the application for the first time using the previous URL, it may take a while due to its startup time. Free Render instances are automatically shut down after 15 minutes of inactivity.

IMPORTANT 2: The project uses a dynamic origin CORS configuration. It verifies if the origin domain exists in a list of allowed domains during the browser CORS check. The allowed domains are: "http://localhost:3000", "http://localhost:3001", "https://localhost:3000" and "https://login.rafaelf.dev:3000". As can be seen, some domains are HTTP domains. They can be used for testing, but since the refresh token has the "Secure" flag, no token refreshing can be performed in HTTP domains (so, when the access token expires, a new login should be performed).


πŸŽ‰ How to use

Once the service is up and running, you have the flexibility to access its endpoints using any REST client of your choice. The API documentation has been prepared using Swagger, and you can access it through the following link:

Authentication API Documentation

If you have seeded the database or are utilizing the online API, the following demo users can be used to log in to the API:

Email Password Access
[email protected] 123456 User Access
[email protected] admin Admin Access

A Postman collection is available and can be accessed through the following button:

Run in Postman

To access restricted API endpoints, you will need a (short-lived) access token. To obtain your access token, send a request to /login along with your username and password credentials. The API will return both the access token and a refresh token, which can be used by the client to request new short-lived access tokens when needed.

Sample Response:

POST http://localhost:3000/login
HTTP/1.1
Accept: application/json

{
    "email": "[email protected]",
    "password": "123456"
}

HTTP/1.1 200 OK
Content-Type: application/json

Headers:
Set-Cookie refreshToken=...; Max-Age=86400; Domain=localhost; Path=/; Expires=Tue, 18 Jul 2023 02:16:54 GMT; HttpOnly; Secure; SameSite=None

{
    "success": true,
    "data": {
        "token": "..."
    }
}

πŸ›  Technologies


πŸ›  TODO list

  • Forgot password
  • Change password

πŸ“ License

This project is open-sourced software licensed under the MIT license.


πŸ‘¨β€πŸ’» Author


Rafael Fernandes Lopes

Developed with πŸ’œ by Rafael Fernandes Lopes

Linkedin Badge