Authentication sounds simple… until you actually have to implement it.
At first, it feels easy: user logs in, backend gives a token, frontend stores it somewhere, and boom — done.
Then reality enters the chat:
- Where should I store the token?
- LocalStorage or cookies?
- Why is my cookie not being set?
- Why does it work in Postman but not in the browser?
- Why am I debugging CORS at 2 AM?
If you’ve been there, welcome. This blog will explain client-side authentication vs server-side authentication, how LocalStorage auth works, how cookie-based auth works, and how to properly set it up in both backend and frontend.
No unnecessary theory. Just practical understanding.
Table of Contents
- The Basic Authentication Flow
- Client-Side Auth Using LocalStorage
- Server-Side Auth Using Cookies
- Backend Setup for Cookie Authentication
- Frontend Access with Cookies
- LocalStorage vs Cookies: Which One is Better?
- Common Mistakes Developers Make
- Final Thoughts
1. The Basic Authentication Flow
Most authentication systems follow this flow:
- User enters email + password
- Frontend sends credentials to backend
- Backend verifies the user
- Backend generates a token (usually JWT)
- Token is stored somewhere
- Future requests use that token for authorization
The real question is:
Where should Step 5 happen?
That’s where LocalStorage vs Cookies begins.
2. Client-Side Auth Using LocalStorage
This is the “easy and fast” approach most developers start with.
Flow:
- Backend sends JWT token in response body
- Frontend stores token in
localStorage - Every API request manually sends token in headers
Example:
Backend Response
{"token":"your_jwt_token_here"}
Frontend Store
localStorage.setItem("token", response.data.token);
Sending Token
axios.get("/profile", {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`
}
});
Why Developers Like It
- Very easy to implement
- Easy to debug
- Works quickly for prototypes
- No cookie/CORS headaches
The Problem
localStorage is vulnerable to XSS attacks.
If malicious JavaScript enters your app, it can access:
localStorage.getItem("token")
…and congratulations, your auth token is now on a vacation it never planned.
That’s why production apps usually avoid storing sensitive auth tokens there.
3. Server-Side Auth Using Cookies
This is the more secure and professional approach.
Instead of giving the token directly to frontend JavaScript:
- Backend stores token inside an HttpOnly Cookie
- Browser automatically sends it with requests
- Frontend cannot access it using JavaScript
This means:
document.cookie
won’t expose your auth token if it’s HttpOnly.
That’s a big security win.
4. Backend Setup for Cookie Authentication
Let’s use Node.js + Express.
Install Packages
npm install express cookie-parser jsonwebtoken cors
Basic Server Setup
const express = require("express");
const cookieParser = require("cookie-parser");
const cors = require("cors");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
app.use(cookieParser());
app.use(cors({
origin: "http://localhost:3000",
credentials: true
}));
Login Route
app.post("/login", (req, res) => {
const { email, password } = req.body;
// Assume user is valid
const token = jwt.sign(
{ email },
"your_secret_key",
{ expiresIn: "1h" }
);
res.cookie("token", token, {
httpOnly: true,
secure: false, // true in production (HTTPS)
sameSite: "lax",
maxAge: 60 * 60 * 1000
});
res.json({
message: "Login successful"
});
});
Now the browser stores the cookie automatically.
No frontend localStorage.setItem() needed.
Beautiful.
5. Frontend Access with Cookies
This is where many developers suffer.
The secret:
You MUST use withCredentials: true
Without this, cookies will behave like your gym motivation — gone.
Axios Example
axios.post(
"http://localhost:5000/login",
{
email,
password
},
{
withCredentials: true
}
);
Protected Route Example
axios.get(
"http://localhost:5000/profile",
{
withCredentials: true
}
);
That’s it.
The browser sends cookies automatically.
No manual headers.
No token handling.
Less headache.
6. LocalStorage vs Cookies: Which One is Better?
| Feature | LocalStorage | HttpOnly Cookie |
|---|---|---|
| Easy to implement | ✅ Yes | Slightly harder |
| Accessible in JS | ✅ Yes | ❌ No |
| XSS Safe | ❌ No | ✅ Better |
| CSRF Risk | ✅ Safe | ⚠ Needs protection |
| Automatic sending | ❌ No | ✅ Yes |
| Production Ready | ⚠ Depends | ✅ Preferred |
My Recommendation
- Small project / prototype → LocalStorage is okay
- Production app → Prefer HttpOnly cookies
Security always wins.
Even if it makes debugging slightly more annoying.
7. Common Mistakes Developers Make
Mistake 1: Forgetting withCredentials
Most common issue.
Most painful issue.
Most “why is this not working?” issue.
Mistake 2: Wrong CORS Config
You must allow:
credentials: true
and exact frontend origin.
Not *
Never *
Browsers hate that.
Mistake 3: secure: true in Localhost
This breaks cookies locally.
Use:
secure: false
for development.
Mistake 4: Trying to Read HttpOnly Cookie in JS
You can’t.
That’s literally the point.
8. Final Thoughts
Authentication is one of those things that looks easy until you build it yourself.
LocalStorage works.
Cookies work better.
Understanding the trade-offs helps you choose the right approach for your project instead of blindly copying random Stack Overflow answers from 2017.
If you’re building serious production applications:
HttpOnly cookies + proper backend validation is the way to go.
Your future self will thank you.
Your security team will thank you.
And most importantly…
you’ll finally stop debugging CORS at 2 AM.