Authentication using Cookies πͺ
βBhai login toh ho gayaβ¦ but secure hai kya?β
Authentication using Cookies πͺ

βBhai login toh ho gayaβ¦ but secure hai kya?β
Imagine this π
You enter a college fest.
At the gate:
- You show your ID card once
- The organizer puts a stamp on your hand
- Now wherever you go inside the fest β gaming zone, food court, concert β security already knows youβre authenticated.
You donβt keep showing your ID card every 2 minutes.
That stamp? Thatβs basically what cookies do on the web.
What is Authentication?
Authentication is the process of verifying who you are.
This usually happens when users:
- Signup
- Signin
- Use Google/GitHub login (SSO β Single Sign On)
Without authentication:
- Anyone could access anyoneβs account
- Websites wouldnβt know who is making requests
The old common approach
JWT + LocalStorage
Most beginners start authentication like this:
localStorage.setItem("token", jwtToken)
Then every request manually sends:
headers: {
Authorization: `Bearer ${token}`
}
Works? β But has problems too π
Especially when applications become bigger.
Why LocalStorage becomes problematic
1. You manually handle everything
Every request:
fetch(url, {
headers: {
Authorization: token
}
})j
Har jagah token bhejo π
You forget once β request fails.
2. Vulnerable to XSS attacks
If malicious JS somehow runs on your page:
const token = localStorage.getItem("token");
Boom π
Attacker now has your auth token.
3. Doesnβt work nicely with Next.js SSR
This is where things become super important.
Why LocalStorage is problematic in Next.js
In traditional React apps:
- Everything runs in browser
But in Next.js:
- Components can run on the server
- Pages can render before browser even loads
And guess what?
localStorage
does NOT exist on the server.
So this breaks:
const token = localStorage.getItem("token");
Error:
ReferenceError: localStorage is not defined
Real-world analogy π§
Imagine:
- LocalStorage = your personal diary inside your room
- Browser/server = security guards outside
Server cannot enter your room to read the diary.
But cookies?
Cookies are automatically attached with requests by the browser itself.
Meaning:
- Server can access them easily
- SSR works beautifully
- Authentication becomes smoother
This is why frameworks like:
- Next.js
- Remix
- Nuxt.js
prefer cookie-based authentication.
Authentication using Cookies
What are Cookies?
Cookies are small pieces of data stored by the browser.
They are designed to:
- Remember users
- Maintain sessions
- Store auth information
- Improve security
Very similar to LocalStorageβ¦
β¦but with one SUPERPOWER β‘
Cookies are automatically sent with requests
You DONβT need this:
Authorization: Bearer token
Browser khud hi sambhaal leta hai π
Flow of Cookie Authentication
User logs in
β
Server verifies credentials
β
Server sends cookie
β
Browser stores cookie
β
Browser automatically sends cookie
with every request
β
Server identifies user
Why Cookies are better for Authentication
1. Automatic handling
Browser automatically attaches cookies.
Less headache. Cleaner code.
2. Better security
Cookies support:
- HttpOnly
- Secure
- SameSite
These are HUGE security advantages.
3. Better with SSR frameworks
Works naturally with:
- Next.js
- Server Components
- Middleware
- API routes
4. Expiry support
Cookies can expire automatically.
Token expires after 7 days
Simple.
Types of Cookies
Persistent Cookies
Stay even after browser closes.
Example:
- βRemember meβ
Session Cookies
Destroyed when browser closes.
Example:
- Banking websites
Secure Cookies
Only sent over HTTPS.
No HTTP = No cookie.
Security level π
Important Cookie Properties
These properties are what make cookies powerful.
HttpOnly
HttpOnly = JavaScript cannot access cookie
This means:
document.cookie
cannot read it.
So even if malicious JS enters your website:
- Token remains protected
Massive win against XSS attacks.
SameSite
Controls whether cookies are sent on cross-origin requests.
This was introduced mainly to reduce:
- CSRF attacks
- Cross-site abuse
There are 3 modes:
1. SameSite: Strict
Only same-site requests allowed.
Most secure.
BUTβ¦
Problem π
If user clicks your website from another website:
- Cookie may not be sent
User experience becomes annoying.
2. SameSite: Lax
Balanced mode βοΈ
Cookies sent:
- On top-level navigation
- Mostly GET requests
This is why many apps use:
SameSite=Lax
Best balance of:
- Security
- Usability
3. SameSite: None
Cookies sent everywhere.
BUT requires:
Secure=true
Useful for:
- Different frontend/backend domains
Example:
- frontend.com
- api.backend.com
Domains
You can also define:
Which domains can access cookie
Useful in:
- Subdomains
- Multi-service apps
Example:
app.example.com
api.example.com
CSRF Attacks π¨
Cross Site Request Forgery.
One of the biggest reasons cookie auth became controversial.
Real-life example
Imagine you are logged into:
bank.com
Now you visit:
evilwebsite.com
That evil site secretly sends:
<form action="https://bank.com/transfer-money">
Since browser automatically sends cookiesβ¦
π Bank thinks YOU made the request.
Money gone.
Daya⦠kuch toh gadbad hai.
Solution? SameSite
This is exactly why:
SameSite- CSRF protection
became important.
Authentication using Cookies : Now letβs build it π₯
Backend Setup (Express + TypeScript)
Initialize project
npm init -y
npx tsc --init
Update tsconfig
{
"rootDir": "./src",
"outDir": "./dist"
}
Install dependencies
npm install express cookie-parser cors jsonwebtoken
npm install -D typescript @types/express @types/cookie-parser @types/jsonwebtoken
Create Express App
import express from "express";
import cookieParser from "cookie-parser";
import cors from "cors";
import jwt, { JwtPayload } from "jsonwebtoken";
const app = express();
app.use(cookieParser());
app.use(express.json());
app.use(cors({
credentials: true,
origin: "http://localhost:5173"
}));
Why credentials: true matters
Without this:
Browser will NOT send cookies
Even if cookie exists.
This line is extremely important.
Signin Endpoint
app.post("/signin", (req, res) => {
const email = req.body.email;
const password = req.body.password;
// Validate user from DB
const token = jwt.sign({
id: 1
}, JWT_SECRET);
res.cookie("token", token, {
httpOnly: true,
secure: false,
sameSite: "lax"
});
res.send("Logged in!");
});
Important part
res.cookie(...)
This sends cookie to browser.
Browser automatically stores it.
No manual work.
Protected Route
app.get("/user", (req, res) => {
const token = req.cookies.token;
const decoded = jwt.verify(token, JWT_SECRET) as JwtPayload;
res.send({
userId: decoded.id
})
});
Notice something beautiful? β¨
No:
Authorization headers
No:
localStorage.getItem()
Browser automatically handled everything.
Logout Route
app.post("/logout", (req, res) => {
res.clearCookie("token");
res.json({
message: "Logged out!"
})
});
Cleaner than replacing token manually.
Listen on Port
app.listen(3000);
Frontend in React
Signin Page
import { useState } from "react"
import axios from "axios"
export const Signin = () => {
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
return <div>
<input
type="text"
placeholder="username"
onChange={(e) => {
setUsername(e.target.value);
}}
/>
<input
type="password"
placeholder="password"
onChange={(e) => {
setPassword(e.target.value);
}}
/>
<button onClick={async () => {
await axios.post("http://localhost:3000/signin", {
username,
password
}, {
withCredentials: true
});
alert("Logged in")
}}>
Submit
</button>
</div>
}
SUPER IMPORTANT β οΈ
withCredentials: true
Without this:
- Cookies wonβt be sent
- Authentication breaks
Most beginners miss this line π
User Page
import axios from "axios";
import { useEffect, useState } from "react"
export const User = () => {
const [userData, setUserData] = useState();
useEffect(() => {
axios.get("http://localhost:3000/user", {
withCredentials: true
})
.then(res => {
setUserData(res.data);
})
}, []);
return <div>
You're id is {userData?.userId}
<br /><br />
<button onClick={() => {
axios.post("http://localhost:3000/logout", {}, {
withCredentials: true,
})
}}>
Logout
</button>
</div>
}
Authentication Flow Diagram
ββββββββββββββ
β User Login β
βββββββ¬βββββββ
β
ββββββββββββββββββββββ
β Backend validates β
β credentials β
βββββββ¬βββββββββββββββ
β
ββββββββββββββββββββββ
β Server sends β
β cookie β
βββββββ¬βββββββββββββββ
β
ββββββββββββββββββββββ
β Browser stores β
β cookie β
βββββββ¬βββββββββββββββ
β
ββββββββββββββββββββββ
β Every future β
β request carries β
β cookie automaticallyβ
ββββββββββββββββββββββ
Final Thoughts
Cookie-based authentication feels slightly confusing initiallyβ¦
But once it clicks π€
β¦it becomes one of the cleanest authentication systems for modern web apps.
Especially in frameworks like:
- Next.js
- SSR applications
- Fullstack apps
Cookies solve many real-world problems elegantly.
The key takeaway:
JWT is not the problem.
WHERE you store the JWT matters.
- JWT + LocalStorage β Easier initially, weaker security
- JWT + HttpOnly Cookies β Cleaner + safer + SSR friendly
And in real production appsβ¦
Most companies prefer:
- Cookies
- Session-based auth
- HttpOnly tokens
for exactly these reasons.
Tiny Homework π
Try implementing:
- Access Token
- Refresh Token
- HttpOnly cookies
- Protected routes
And suddenlyβ¦
youβll realize authentication is not βjust login/signupβ.
Itβs an entire security system sitting behind your app π₯
webdevelopment #javascript #typescript #reactjs #nextjs #nodejs #expressjs #authentication #websecurity #cookies #jwt #frontenddevelopment #backenddevelopment #fullstackdevelopment #softwareengineering #coding #programming #developers #100DaysOfCode #tech #devcommunity #httpOnly #csrf #webdev #reactdeveloper #nextjsdeveloper
λ©νλ°μ΄ν°
- post_id
- b105efd0a944
- slug
- authentication-using-cookies-b105efd0a944
- url
- https://medium.com/@yasharesofficial/authentication-using-cookies-b105efd0a944
- canonical_url
- https://medium.com/@yasharesofficial/authentication-using-cookies-b105efd0a944
- author_url
- https://medium.com/@yasharesofficial
- status
- ok
- fetched_at
- 2026-06-09 15:37:30