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\":\"test@gmail.com\",\"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?
>Solution :
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.