Introduction
One of the most overlooked parts of building a React app is token storage. Many developers don’t think twice and simply go with the easy choice :
“Just throw it into LocalStorage, and we’re done.”
It works, it’s persistent, and it survives page reloads. But here’s the risk: LocalStorage or SessionStorage tokens are JavaScript-readable. If your application is XSS (Cross-Site Scripting) vulnerable, attackers can inject code and steal them immediately and have complete control over user accounts.
Even with cookies, you’re not necessarily safe. With the wrong settings, you’re vulnerable to CSRF (Cross-Site Request Forgery), whereby attackers trick users into doing unwanted things when they’re logged in.
The impact can be devastating :
In fintech apps : stolen money and compromised accounts.
In healthcare : leaked medical records.
In social platforms : hijacked profiles and identity theft.
So, how do you secure tokens in React without breaking usability? That’s what we’ll uncover in this blog.
Common Storage Options (and Their Risks)
When building authentication in React, one of the first decisions you’ll face is: Where should I store my tokens?.There are three most common options developers use : LocalStorage, SessionStorage, and Cookies.
- LocalStorage
This is the most popular method because it’s fast, persistent, and easy to use. Below show examples like :
localStorage.setItem("token", userToken);
Pros :
- Dead simple to implement.
- Data persists even after refreshing or closing the browser.
- Great for storing non-sensitive data (like UI preferences).
Cons :
- Tokens here are fully exposed to JavaScript. If your app is vulnerable to XSS (Cross-Site Scripting), a malicious script can instantly read tokens and send them to an attacker.
- Once stolen, those tokens can be reused indefinitely until they expire or are revoked.
- Makes your app a big target for token hijacking.
- SessionStorage
This works just like LocalStorage, except that the data disappears as soon as the tab or browser is closed :
sessionStorage.setItem("token", userToken);
Some developers prefer this since it reduces long-term risk, but it’s not foolproof.
Pros :
- Data is temporary—cleared once the user closes the browser.
- Slightly reduces the window of attack compared to LocalStorage.
Cons :
- It is exposed to JavaScript, so that XSS attacks can steal tokens here too.
- The tokens don’t persist across tabs, which can annoy users who expect to stay logged in while multitasking.
- It doesn’t solve the root security issue.Tokens are still readable on the client side.
- Cookies
Cookies get a bad rap sometimes because of their association with tracking, but when configured properly, they’re the safest place for tokens in modern web apps. Unlike LocalStorage and SessionStorage, cookies support special flags that make them much harder to attack.
Example (server-side setup)
res.cookie("refreshToken", token, {
httpOnly: true, // prevents JavaScript access (blocks XSS)
secure: true, // only sent over HTTPS
sameSite: "Strict", // prevents CSRF
});
Props :
- HttpOnly cookies are invisible to JavaScript so it makes them resistant to XSS attacks.
- Widely supported and reliable for session management.
- When paired with short-lived access tokens, they provide a strong security model.
Cons :
- Without the SameSite or CSRF tokens, cookies can be vulnerable to CSRF attacks (Cross-Site Request Forgery).
Best Practices for Secure Token Storage in React
When building secure authentication in React, how you store tokens can make or break your app’s safety. Let’s explore the five golden rules with examples :
1) Store Access Tokens in Memory
Access tokens are short-lived (5-15 minutes) and should stay in memory only-never.
LocalStorage or SessionStorage. Storing access tokens in LocalStorage or SessionStorage makes them accessible to malicious scripts if your app suffers an XSS attack. By keeping them in memory, you limit exposure.
// React state for storing access token
const [accessToken, setAccessToken] = useState(null);
Security Benefit :
Tokens in memory aren’t exposed to XSS, making them much harder for attackers to steal.
2) Keep Refresh Tokens in HttpOnly Cookies
Refresh tokens live longer. They should be hidden from JavaScript using HttpOnly cookies.The safest place is inside an HttpOnly cookie with additional security flags.
// Express.js example
res.cookie("refreshToken", token, {
httpOnly: true, // blocks JavaScript access
secure: true, // only sent over HTTPS
sameSite: "Strict" // prevents CSRF
});
Security Benefit :
HttpOnly cookies prevent JavaScript access, while cookie flags add extra protection against CSRF attacks.
3) Use Short-Lived Tokens
Tokens should never last too long. Instead of issuing access tokens that live for hours or days, keep them short—like 5–15 minutes.
If a hacker manages to steal an access token, the damage window is small. Short-lived tokens force attackers to act quickly, reducing the risk of long-term misuse. Meanwhile, users won’t even notice this if you use refresh tokens to silently renew sessions in the background.
Security Benefit :
Even if a token is stolen, its short lifespan minimizes potential damage.
4) Rotate Refresh Tokens
Every time a refresh token is used, issue a new one and invalidate the old one.so If an attacker attacks then they won’t be able to use it after it’s rotated. It protects your app against replay attacks and ensures only the latest refresh token is valid.
// On refresh request, issue new refresh token
const newRefresh = jwt.sign({ userId }, REFRESH_SECRET, { expiresIn: "7d" });
res.cookie("refreshToken", newRefresh, { httpOnly: true, secure: true });
Security Benefit :
Stolen refresh tokens become useless once rotated, shutting down replay attacks.
5) Always Use HTTPS
Tokens should never travel over plain HTTP. So always use HTTPS for your frontend and backend communication.Without the HTTPS, anyone on the same network (like in a café Wi-Fi) can intercept requests and steal tokens using simple tools. HTTPS encrypts all traffic, protecting tokens during transmission and keeping attackers out.
https://yourapp.com/api/login
Security Benefit :
HTTPS encrypts traffic, preventing attackers from sniffing tokens on shared or public networks.
Conclusion
Safe storage of tokens in React is not a technicality.It’s an essential aspect of securing your app and users. LocalStorage or SessionStorage may be convenient.The more secure option is to store access tokens in memory, refresh tokens in HttpOnly cookies, and ensure all communications are over HTTPS.
For the best practices-short-lived tokens, token rotation, and secure cookies—you earn the trust of your users and safeguard their data from basic attacks. A bit of additional attention to token management today will keep you out of large security issues tomorrow, while providing your users with assurance that their data is secure.