Understanding Login with Cookies and Sessions
The sample management API server we've built so far has one big problem. Anyone can register, modify, and delete samples. Just as lab equipment rooms require a key card for access, web services need a procedure to verify "who this person is" β authentication.
How does login work? To answer this, you first need to understand a fundamental property of the web.
HTTP Has No Memory
HTTP, the web's communication protocol, is stateless. The moment the server processes a request and sends a response, it immediately forgets who that user was.
Imagine a reception desk where the staff's memory resets every 5 seconds. "Can I get the results for sample S001?" β Results delivered β (reset) β "I asked about sample S001 just now" β "Who are you?"
You'd have to re-introduce yourself with every request. To solve this inconvenience, cookies were invented in 1994.
Cookies: Name Tags Stuck on the Browser
A cookie is a small piece of text data the server gives to the browser. The browser stores it and automatically sends it along with every request to the same server.
[1] Login request
Browser β Server: "Username kim, password 1234"
Server β Browser: "Verified! Carry this cookie with you"
Set-Cookie: user=kim
[2] Subsequent request (automatic)
Browser β Server: "Give me sample list" + Cookie: user=kim
Server: "Ah, it's researcher Kim. Sending sample list"
[3] Another request (automatic)
Browser β Server: "S001 details" + Cookie: user=kim
Server: "Request from researcher Kim. Sending S001 info"It's like a lab access badge. Once issued, you don't need to prove your identity every time β just show the badge.
Handling Cookies in Express
const express = require("express");
const app = express();
app.get("/login", function(req, res) {
res.setHeader("Set-Cookie", "user=kim; Path=/");
res.send("Login complete");
});
app.get("/whoami", function(req, res) {
const cookies = req.headers.cookie;
res.send("Current cookies: " + cookies);
});
app.listen(3000);Visit /login and the server sends a cookie via the Set-Cookie header. Then visit /whoami and you can verify the browser automatically sends the cookie.
Cookie Limitations: Why Sessions Are Needed
Using only cookies for authentication has a serious problem. Cookies are stored in the browser. Anyone can open browser DevTools to see and modify cookie contents.
If you put the username directly in the cookie like Cookie: user=kim, someone could change it to Cookie: user=admin and impersonate an administrator.
Lab analogy β if the access badge says "Name: Kim" in plain text and anyone can change it with correction tape, access control is meaningless.
Sessions solve this problem.
Sessions: Access Records Managed by the Server
The core session idea β store sensitive information on the server and give the browser only an identification number.
[1] Login successful
Server internal: { session_abc123: { user: "kim", role: "researcher" } }
Server β Browser: Set-Cookie: session_id=abc123
[2] Subsequent request
Browser β Server: Cookie: session_id=abc123
Server: Look up session abc123 β { user: "kim", role: "researcher" }
β "It's researcher Kim"All the browser has is a meaningless number abc123. Even if you change this number, it's invalid if it's not registered as a session on the server.
Lab analogy β the access badge only has a barcode, and the actual identity information is on the security office computer. Even if someone copies the badge, the door won't open if the barcode isn't in the database.
| Cookies Only | Session (Cookie + Server Storage) | |
|---|---|---|
| Data location | Browser | Server |
| Security | User can modify (risky) | Only server can modify (safe) |
| Capacity | 4KB limit | Server memory/DB-based (no limit) |
| Analogy | Badge with name written on it | Badge with barcode only + security office DB |
Cookie Security Options
You can set security measures on the cookie itself:
Set-Cookie: session_id=abc123; HttpOnly; Secure; Path=/; Max-Age=3600| Option | Meaning | Why It's Needed |
|---|---|---|
HttpOnly | JavaScript cannot access the cookie | Prevents malicious scripts (XSS) from stealing cookies |
Secure | Cookie only sent over HTTPS | Prevents cookie exposure through network eavesdropping |
Max-Age=3600 | Auto-delete after 3600 seconds (1 hour) | Prevents login state from persisting indefinitely |
Path=/ | Cookie only sent for requests under this path | Limits unnecessary cookie transmission scope |
A cookie without Max-Age disappears when the browser closes β this is called a session cookie. A cookie with Max-Age persists even after closing the browser β this is called a permanent cookie.
Modern Authentication: Token-Based
Modern web services often use JWT (JSON Web Token) instead of session IDs. Supabase, which BioPlayground uses, also uses JWT-based authentication.
The principle is similar to sessions, but with differences:
| Session Method | Token (JWT) Method | |
|---|---|---|
| Server state | Session storage required | No server storage needed |
| Verification | Session DB lookup | Signature verification on the token itself |
| Scalability | Multiple servers need shared sessions | No sharing between servers needed |
Deep implementation isn't covered at this stage. What matters is understanding the mechanism:
- User sends username/password
- Server verifies and issues an identifier (session ID or token)
- Browser automatically attaches the identifier to subsequent requests
- Server uses the identifier to confirm "who this person is"
Every login system is a variation of these 4 steps.
Try It Yourself (Faded Example)
Fill in the blanks to complete the cookie-based authentication flow.
// 1. Login: server issues a cookieapp.post("/login", function(req, res) {res.setHeader("", "session_id=xyz789; HttpOnly; Path=/");res.json({ message: "Login successful" });});// 2. Protected API: verify user via cookieapp.get("/my-samples", function(req, res) {const cookies = req.headers.;if (!cookies || !cookies.includes("session_id")) {return res.status().json({ error: "Login required" });}res.json({ samples: ["S001", "S002"] });});
Common Errors & Solutions
Q: Cookie isn't being sent (req.headers.cookie is undefined)
Check browser DevTools (F12) β Application β Cookies to see if a cookie exists for the domain. If the Secure option is set, cookies won't be sent over HTTP (localhost). During development, either remove Secure or use HTTPS.
Q: Sent Set-Cookie header but browser doesn't save the cookie
If the frontend and backend have different domains (or ports), cookies are blocked by CORS policy. Add credentials: 'include' to your fetch call, and set Access-Control-Allow-Credentials: true header on the server.
Q: Explain the difference between session and cookie in one sentence
A cookie is "data stored in the browser," while a session is "a system that finds data stored on the server using a cookie's ID." Sessions are a concept built on top of cookies.