skip to Main Content

Is it safe to generate a JWT token on server-side and to pass it in a sign-up request within the response JSON back to the client?

Currently I’m generating a JWT in the following way:

import express from "express";
import jwt from "jsonwebtoken";

const generateToken = (id: any, res: express.Response) => {
    const token = jwt.sign({ id }, process.env.JWT_SECRET as string, {
        expiresIn: "15d",
    });

    return token;
};

export default generateToken;

Which is being used by the following auth.controller:

import express from "express";
import User from "../models/user.model";
import bcrypt from "bcrypt";
import generateToken from "../utils/generateToken";

export const signup = async (req: express.Request, res: express.Response) => {
    try {
        const { username, password, email } = req.body;

        // Check if all fields are filled in and been sent
        if (!username || !password || !email) {
            return res
                .status(400)
                .json({ message: "Please fill in all fields" });
        }

        // Check if the user already exists
        const userCheck = await User.findOne({ username });

        if (userCheck) {
            return res.status(400).json({ message: "User already exists" });
        }

        // Check if the email already exists
        const emailCheck = await User.findOne({ email });

        if (emailCheck) {
            return res.status(400).json({ message: "Email already exists" });
        }

        // Hash the password
        const salt = await bcrypt.genSalt(12);
        const hashedPassword = await bcrypt.hash(password, salt);

        // Create a new user
        const newUser = new User({
            username,
            password: hashedPassword,
            email,
        });

        // Generate and send token
        if (newUser) {
            const token = generateToken(newUser._id, res);
            await newUser.save();

            return res.status(201).json({
                user: newUser,
                token: token,
            });
        } else {
            return res.status(500).json({ message: "Something went wrong" });
        }
    } catch (error) {
        console.log(`Error in signup controller: ${error}`);
        return res.status(500).json({ message: "Something went wrong" });
    }
};

As the code shows, I’m generating the JWT token and send it from the backend to the client within a response from a client post.

// Generate and send token
if (newUser) {
    const token = generateToken(newUser._id, res);
    await newUser.save();

    return res.status(201).json({
        user: newUser,
        token: token,
    });
} else {
    return res.status(500).json({ message: "Something went wrong" });
}

In the client it will be saved to redux local-storage.

auth:"{"session":{"signedIn":true,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2MWE1Y2U5NjAzYWRhNTMzODA2YjVkZSIsImlhdCI6MTcxMzAwMzc1MywiZXhwIjoxNzE0Mjk5NzUzfQ.v23o2WIIz6bYqxpP_wwG5Q3DZgwXtdPX1pMAnGsDKs4"},"user":{"avatar":"https://via.placeholder.com/150","username":"test","email":"[email protected]","authority":"user"}}"locale:"{"currentLang":"en"}"_persist:"{"version":-1,"rehydrated":true}"

Question: Is this a safe enough way to store it within the local-storage or is the set-header way required?

3

Answers


  1. Storing JWT tokens in local storage can expose your application to security risks, particularly XSS (Cross-Site Scripting) attacks. If malicious scripts are injected into your website, they can access tokens stored in local storage, potentially leading to unauthorized access.

    A more secure approach is to use HTTP-only cookies for storing JWT tokens. By setting the token as an HTTP-only cookie in the response header, you prevent client-side scripts from accessing it, mitigating the risk of XSS attacks. Additionally, you can enhance security by setting the Secure and SameSite attributes for the cookie.

    Example

    if (newUser) {
        const token = generateToken(newUser._id);
        await newUser.save();
    
        res.cookie("jwt", token, {
            httpOnly: true,
            secure: true,
            sameSite: "strict"
        });
    
        return res.status(201).json({
            user: newUser
        });
    } else {
        return res.status(500).json({ message: "Something went wrong" });
    }
    

    This approach ensures that the JWT token is securely stored and transmitted between the client and server, reducing the risk of security vulnerabilities. However, remember to implement proper CSRF (Cross-Site Request Forgery) protection to safeguard against CSRF attacks when using cookies for authentication.

    Login or Signup to reply.
  2. In most of the websites, JWT is created on server side, and sent back to client.

    There are couple of ways you can send this JWT to client

    1. Sending JWT in URL as params, in case if its a redirect from backend domain to frontend domain
    2. Sending JWT as a HTTP Cookie, as discussed by Tunay in above message

    Storing JWT in localstorage is a common practice. Reading that in client side is very hard unless you expose the JWT secret to your users(you are not doing it). You can use localstorage, unless your application has very sensitive functionality such as banking or stocks, in that case a session based would be preferred..

    Login or Signup to reply.
  3. Cookie will be nice.It will be sent with request.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search