Skip to content
Related Articles
Open in App
Not now

Related Articles

JWT Authentication With Refresh Tokens

Improve Article
Save Article
  • Difficulty Level : Medium
  • Last Updated : 08 Sep, 2022
Improve Article
Save Article

When building a web application, authentication is one of the important aspects, and we usually implement authentication using JWT tokens (You can learn more about JWT here). We create an access token and store it in the local storage or session or cookie. But there is a more secure way to implement this using Refresh Tokens.

Refresh Tokens: It is a unique token that is used to obtain additional access tokens. This allows you to have short-lived access tokens without having to collect credentials every time one expires.

Since access tokens aren’t valid for an extended period because of security reasons, a refresh token helps to re-authenticate a user without login credentials. This Refresh token is never exposed to the client-side Javascript, even if our access token gets compromised it’ll be expired in a very short duration. So, we will be sending two tokens instead of one, an access token and a refresh token. The access token will contain all the user information and will be stored in Javascript runtime, but the refresh token will be stored securely in an HTTP-only cookie. 

 

Auth Persistence: We can easily persist users between refreshes and login without any credentials. We can create a new route called refresh, whenever a token expires or a user refreshes we can get a new access token by sending a request to this route

 

Implementation: Now Let’s implement authentication with JWT and Refresh tokens. We’ll start by creating a new Express app and installing all the required dependencies. 

Step 1: Run the following commands to initialize the project and create an index file & env file. (Make sure you have node and npm installed)

npm init -y
touch index.js .env

Step 2: Install all the required dependencies and open the project in the code editor.

npm install express cookie-parser dotenv jsonwebtoken 

Project Structure:

 

We will write our authentication logic in Index.js. We’ll first create an express app and then implement two routes login & refresh. The login route will get a post request, then it will check the credentials if they match it’ll send a refresh token and access token in response. The refresh route will also get a post request it’ll verify the refresh token, if it is correct then it will respond with a new access token otherwise throw an authorization error.

Example: We will now implement two routes login & refresh. The below code is for index.js:

index.js

const dotenv = require('dotenv');
const express = require('express');
const cookieparser = require('cookie-parser');
const jwt = require('jsonwebtoken')
const bodyParser = require('body-parser');

// Configuring dotenv
dotenv.config();

const app = express();

// Setting up middlewares to parse request body and cookies
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieparser());

const userCredentials = {
    username: 'admin',
    password: 'admin123',
    email: 'admin@gmail.com'
}

app.post('/login', (req, res) => {
    // Destructuring username & password from body
    const { username, password } = req.body;

    // Checking if credentials match
    if (username === userCredentials.username && 
        password === userCredentials.password) {
        
        //creating a access token
        const accessToken = jwt.sign({
            username: userCredentials.username,
            email: userCredentials.email
        }, process.env.ACCESS_TOKEN_SECRET, {
            expiresIn: '10m'
        });
        // Creating refresh token not that expiry of refresh 
        //token is greater than the access token
        
        const refreshToken = jwt.sign({
            username: userCredentials.username,
        }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '1d' });

        // Assigning refresh token in http-only cookie 
        res.cookie('jwt', refreshToken, { httpOnly: true, 
            sameSite: 'None', secure: true, 
            maxAge: 24 * 60 * 60 * 1000 });
        return res.json({ accessToken });
    }
    else {
        // Return unauthorized error if credentials don't match
        return res.status(406).json({ 
            message: 'Invalid credentials' });
    }
})

app.post('/refresh', (req, res) => {
    if (req.cookies?.jwt) {

        // Destructuring refreshToken from cookie
        const refreshToken = req.cookies.jwt;

        // Verifying refresh token
        jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, 
        (err, decoded) => {
            if (err) {

                // Wrong Refesh Token
                return res.status(406).json({ message: 'Unauthorized' });
            }
            else {
                // Correct token we send a new access token
                const accessToken = jwt.sign({
                    username: userCredentials.username,
                    email: userCredentials.email
                }, process.env.ACCESS_TOKEN_SECRET, {
                    expiresIn: '10m'
                });
                return res.json({ accessToken });
            }
        })
    } else {
        return res.status(406).json({ message: 'Unauthorized' });
    }
})

app.listen(process.env.PORT, () => {
    console.log(`Server active on http://localhost:${process.env.PORT}!`);
})
  • .env: The below code is for .env which is used to store your sensitive credentials like API keys:

env




PORT = 5000
ACCESS_TOKEN_SECRET=MYSECRETACCESS
REFRESH_TOKEN_SECRET=MYREFRESHTOKENSECRET

Output:

 

My Personal Notes arrow_drop_up
Related Articles

Start Your Coding Journey Now!