skip to content
logo
Table of Contents

React Authentication Pitfalls and How to Fix Them

Authentication is a critical part of web applications, but mistakes can lead to token leaks, session hijacking, and unauthorized access.
This guide covers:

  • Handling JWTs securely
  • Preventing session fixation
  • OAuth implementation risks
  • Secure login form best practices

1. Storing JWTs in Local Storage is a Security Risk

JWTs stored in localStorage are vulnerable to XSS attacks.
If an attacker injects malicious JavaScript, they can steal the token.

Bad: Storing JWTs in localStorage

localStorage.setItem("token", jwtToken);

Fix: Use Secure HTTP-Only Cookies

HTTP-only cookies prevent JavaScript access, protecting tokens from XSS.

// Server-side (Express.js)
import cookieParser from "cookie-parser";
app.use(cookieParser());
app.post("/login", (req, res) => {
res.cookie("auth_token", "secure-token", {
httpOnly: true,
secure: true,
sameSite: "Strict",
});
res.send("Logged in");
});

2. Session Fixation: Reusing Session IDs After Login

If a session ID is not refreshed after login, an attacker with a pre-login session can hijack it.

Fix: Regenerate Session After Authentication

// Server-side (Express.js)
app.post("/login", (req, res) => {
req.session.regenerate(() => {
req.session.userId = user.id;
res.send("Session refreshed");
});
});

3. OAuth Redirect Vulnerabilities

OAuth login redirects users to an authentication provider, but attackers can manipulate redirect URIs to steal authentication tokens.

Bad: Accepting Any Redirect URL

const redirectUrl = new URLSearchParams(window.location.search).get("redirect");
window.location.href = redirectUrl;

Fix: Validate Allowed Redirects

const allowedRedirects = ["https://yourapp.com/dashboard"];
const redirectUrl = new URLSearchParams(window.location.search).get("redirect");
if (!redirectUrl || !allowedRedirects.includes(redirectUrl)) {
throw new Error("Invalid redirect URL");
}
window.location.href = redirectUrl;

4. Exposing User Data in JWT Payloads

JWTs store data in base64 format, meaning anyone can decode them.
If sensitive information is stored inside, it can be exposed in the frontend.

Bad: Storing Sensitive Data in JWT

// JWT Payload
{
"userId": "123",
"role": "admin",
"email": "user@example.com"
}

Fix: Store Minimal Information in JWT

Use the JWT only for authentication, and store user details in the database.

// Safe JWT Payload
{
"userId": "123"
}

5. Weak Password Handling and Bruteforce Risks

Allowing short, simple passwords and lacking brute-force protection makes logins vulnerable.

Fix: Enforce Strong Passwords

Use libraries like zxcvbn to validate passwords.

import zxcvbn from "zxcvbn";
const passwordStrength = zxcvbn(password);
if (passwordStrength.score < 3) {
console.log("Weak password");
}

Fix: Implement Rate Limiting

Prevent brute-force attacks by limiting login attempts.

import rateLimit from "express-rate-limit";
const limiter = rateLimit({ windowMs: 15 _ 60 _ 1000, max: 5 });
app.use("/login", limiter);

Conclusion

  • Store JWTs in HTTP-only cookies, not localStorage.
  • Regenerate session IDs after login to prevent fixation.
  • Validate OAuth redirects to prevent token leaks.
  • Minimize data stored in JWTs to prevent exposure.
  • Enforce strong passwords and rate limiting to secure logins.

Securing authentication requires multiple layers of defense.
Implement these fixes before attackers find the weaknesses.