In today's web development landscape, JSON Web Tokens (JWT) have become a popular choice for authentication and authorization. However, securely storing JSON web tokens in an application's frontend poses a significant challenge. In this article, we will explore various techniques to address this issue and ensure the protection of sensitive user information. We will cover the pros and cons of using Before delving into storage options, it's crucial to understand the nature of a JWT token - its an "open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object." A JSON web token consists of three parts: a header (think authorization header), a JWT payload, and a signature. The payload contains claims, such as user information or permissions (eg. used as an access token), while the signature ensures the token's integrity. When it comes to storing JSON web tokens in the frontend, two primary concerns arise: Protection against XSS attacks (cross-site scripting); and Mitigation of token theft. XSS attacks occur when an attacker injects malicious code into a website, gaining unauthorized access to sensitive data. Storing these tokens in insecure locations can make them vulnerable to XSS. If an attacker manages to obtain a user's JWT token, they can impersonate the user and gain unauthorized access to protected resources. Therefore, it is essential to employ secure storage techniques to prevent token theft. Simplicity: The Persistence: Data stored in XSS attack: storing JSON web tokens in Lack of Encryption: LocalStorage does not provide built-in encryption, encrypted tokens make the stored data virtually inaccessible if an attacker gains access to the user's device. Cookies are another popular storage mechanism for web applications. You get certain advantages when you use cookies for storing JSON web tokens. Built-in Security: Cookies provide a built-in Same-Origin Policy: Cookies are subject to the Options for Complexity: working with cookies can be more complex compared to LocalStorage. Limited Storage: cookies have a size limit of approximately 4KB, which may pose a constraint when storing large JWT tokens. To securely store a JSON web token in the frontend, consider the following best practices: Encryption:If you choose to use LocalStorage, encrypt the JWT tokens before storing them to enhance their security. Various encryption libraries and algorithms are available for this purpose. Short validity:Set a short lifespan for JWT tokens to minimize the window of opportunity for attackers to exploit stolen tokens. Refresh token:a mechanism that utilizes simple web tokens to refresh tokens and reject tokens that have expired will help to protect your user's data and minimize the chances of data theft. Cookies provide enhanced security, compatibility, session persistence, and scalability, making them the preferred option for persistently storing JSON web tokens in the frontend of a Node.js web application for the following reasons: Cookies offer built-in security features such as the Here's an example of how to set these flags in a Node.js application using the cookie package: Cookies adhere to the same-origin policy, limiting their access to the originating domain. This strengthens protection against a XSS attack and makes it harder for attackers to compromise tokens. Cookies support setting expiration dates, enforcing token validity periods, and can be easily invalidated on the server side to revoke access if necessary. Here's an example of setting an expiration date for a cookie: To revoke a cookie, you can set its expiration date to a past date, rendering it invalid. Cookies can be sent with requests to different domains, facilitating authentication and authorization in cross-domain scenarios. This behavior is achieved by configuring the CORS (Cross-Origin Resource Sharing) settings on the server. Here's an example using the CORS package in a Node.js application: Make sure to replace 'http://example.com' with the appropriate domain or origins that should be allowed to make cross-domain requests. Unlike Cookies have a larger storage capacity compared to When it comes to securely storing this type of access token in your web application, an optimal solution is to save the token in browser session storage while storing the refresh token in a cookie protected by the This approach offers a balance between security and convenience. The JSON web token in session storage provides quick access during the user's session, while the refresh token in a cookie ensures long-term persistence and protection against CSRF attacks. Even if a new refresh token is generated by the attacker, they won't be able to read the response if they're using an HTML form. It's important to understand that preventing attackers from making a succesful fetch or AJAX request in order to read the response requires that your authorization server's CORS policy is set up correctly to prevent requests from unauthorized websites. Here's a step-by-step guide with Node.js code snippets for each step: When a user successfully authenticates, generate both a JSON web token and a refresh token on the server-side. The JSON token contains short-lived access information, while the refresh token is a long-lived token used for obtaining new JSON web tokens when they expire. On the client-side, save the JSON web token in browser session storage upon successful authentication. This ensures the token remains available during the user's session but is cleared when the browser tab is closed. This ensures it is securely stored and inaccessible to client-side JavaScript code. It also helps protect against a CSRF attack. This step utilises 3 key cookie-specific security controls: When the JSON web token expires, the client can use the refresh token stored in the cookie to request a new JSON web token from the server. This process ensures continuous authentication without requiring the user to manually log in again. There are very few tools that allow software developers to protect their web applications against hackers - without the help of specialist application security experts. However, Cyber Chief is one such tool that allows you to run regular vulnerability scans with a web app vulnerability scanning tool. See how Cyber Chief works now to see not only how it can help to keep attackers out, but also to see how you can ensure that you ship every release with zero known critical vulnerabilities like CSV formula injection attacks and thousands more. Cyber Chief helps you run automated tests for your web app security, API security and cloud infrastructure security and each subscription comes with: Here's what you can do next. See Cyber Chief In Action >> LocalStorage
and cookies and provide code snippets to implement these solutions effectively.What is a JSON web token (JWT token)?
What are the security concerns with storing JSON web tokens in an application frontend?
Cross-Site Scripting (XSS) attacks with insecure JWT token storage
JWT token theft
Storing a JSON web token in Local Storage
LocalStorage
is a built-in browser storage mechanism that allows web applications to store data persistently. However, it is crucial to consider its advantages and disadvantages when using it to store a JSON web token.Pros of LocalStorage
LocalStorage
API is straightforward to use, making implementation easier. LocalStorage
remains available even after the user closes the browser or reboots the system. Cons of LocalStorage
LocalStorage
makes them susceptible to a XSS attack. Storing JSON web tokens in cookies
Pros of cookies
secure
flag that allows only encrypted transmission over HTTPS. same-origin
policy, which helps mitigate XSS vulnerabilities. Secure
and HttpOnly
Flags: By setting the secure flag and HttpOnly flag on cookies, you can enhance their security. Cons of cookies
Best Practices for securely storing JSON web tokens
Secure
and HttpOnly
flags: If you opt for cookies, utilize the secure and HttpOnly
flags to enhance their security. The secure setting in a cookie ensures transmission only over HTTPS, while the HttpOnly
flagforbids JavaScript code from accessing the cookie, for example, through the Document.cookie
property. Preferred Approach: Storing JSON web tokens with cookies for persistent storage
Enhanced security features
secure
setting for encrypted transmission over HTTPS and the HttpOnly
flag to prevent client-side JavaScript code access, minimizing the risk of token theft through XSS attacks.
const cookie = require('cookie'); // Set a cookie with the secure and HttpOnly flags const token = 'your-jwt-token'; const secureCookie = true; const httpOnlyCookie = true; const cookieOptions = { secure: secureCookie, httpOnly: httpOnlyCookie, }; const cookieString = cookie.serialize('jwtToken', token, cookieOptions); // Set the cookie in the response header res.setHeader('Set-Cookie', cookieString);
Same-Origin policy enforcement
Support for token expiration and revocation
const cookie = require('cookie'); // Set the expiration date for the cookie (e.g., 7 days from now) const expirationDate = new Date(); expirationDate.setDate(expirationDate.getDate() + 7); const cookieOptions = { expires: expirationDate, }; const cookieString = cookie.serialize('jwtToken', token, cookieOptions); // Set the cookie in the response header res.setHeader('Set-Cookie', cookieString);
Compatibility with cross-domain requests
const cors = require('cors'); // Enable cross-origin requests and allow credentials (cookies) app.use(cors({ credentials: true, origin: 'http://example.com' }));
Persistence across browser sessions
LocalStorage
, cookies persist across browser sessions, ensuring users remain authenticated even after closing and reopening the browser.Scalability for large tokens
LocalStorage
, making them suitable for storing larger JSON web tokens or additional metadata. Optimal Secure Solution: Save JWT Tokens in the browser's memory and store the refresh token in a cookie
secure
and HttpOnly
settings.How does this approach help mitigate a CSRF attack?
Step 1: Generate and issue tokens
// Generate JWT token const jwt = require('jsonwebtoken'); const payload = { userId: 'user123' }; const secretKey = 'your-secret-key'; const jwtToken = jwt.sign(payload, secretKey, { expiresIn: '1h' }); // Generate refresh token const refreshToken = jwt.sign(payload, secretKey, { expiresIn: '7d' }); // Return both tokens to the client res.json({ jwtToken, refreshToken });
Step 2: Save the JSON web token in the browser session
// Save JWT token in session storage sessionStorage.setItem('jwtToken', jwtToken);
Step 3: Save the refresh token in a secure HttpOnly Cookie
httpOnly
flag to prevent JavaScript from reading it.secure=true
flag so it can only be sent over HTTPS.SameSite=strict
flag whenever possible to prevent CSRF. This can only be used if your Authorization Server has the same domain as your front end. If the domains are different, your Authorization Server must set CORS headers in the backend or use other methods to ensure that the refresh token request can only be successfully performed by authorized websites.
// Set refresh token as an HttpOnly cookie res.cookie('refreshToken', refreshToken, { secure: true, // Set to true if using HTTPS httpOnly: true, sameSite: 'strict', // Adjust to your requirements maxAge: 7 * 24 * 60 * 60 * 1000, // Set the expiration time (7 days in this example) });
Step 4: How to refresh the JSON web tokens
// Refreshing JWT token using the refresh token const cookie = req.cookies.refreshToken; if (cookie) { jwt.verify(cookie, secretKey, (err, decoded) => { if (err) { // Handle invalid or expired refresh token res.status(401).json({ error: 'Invalid or expired refresh token' }); } else { // Generate a new JWT token const newJwtToken = jwt.sign({ userId: decoded.userId }, secretKey, { expiresIn: '1h' }); // Update the JWT token in session storage sessionStorage.setItem('jwtToken', newJwtToken); // Return the new JWT token to the client res.json({ jwtToken: newJwtToken }); } }); } else { // No refresh token found, prompt user to log in res.status(401).json({ error: 'Refresh token not found' }); }
How to test security of tokens and cookies?
LocalStorage vs Cookies: the best-practice guide to storing JWT tokens securely in your front-end (2024)
Author: The Hon. Margery Christiansen
Last Updated:
Views: 6473
Rating: 5 / 5 (50 voted)
Reviews: 81% of readers found this page helpful
Name: The Hon. Margery Christiansen
Birthday: 2000-07-07
Address: 5050 Breitenberg Knoll, New Robert, MI 45409
Phone: +2556892639372
Job: Investor Mining Engineer
Hobby: Sketching, Cosplaying, Glassblowing, Genealogy, Crocheting, Archery, Skateboarding
Introduction: My name is The Hon. Margery Christiansen, I am a bright, adorable, precious, inexpensive, gorgeous, comfortable, happy person who loves writing and wants to share my knowledge and understanding with you.