Authentication
Most APIs need to know who is making a request and whether they're allowed to. These are two distinct concerns:
- Authentication — verifying who you are (identity). Fails with 401 Unauthorized.
- Authorization — verifying what you're allowed to do (permissions). Fails with 403 Forbidden.
You authenticate first, then authorize.
Token-based authentication (JWT)
The most common modern approach: the client logs in, receives a token, and sends it on every subsequent request in the Authorization header.
// 1. Log in and receive a token
const res = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
const { token } = await res.json();
// 2. Send the token on protected requests
await fetch("/api/profile", {
headers: { Authorization: `Bearer ${token}` },
});
A JWT (JSON Web Token) is a signed token (header.payload.signature) containing claims like the user ID and expiry. The signature lets the server verify it without storing session state.
Never store passwords in plain text
Servers must hash passwords (e.g., with bcrypt) before storing them:
import bcrypt from "bcryptjs";
// On signup
const hash = await bcrypt.hash(password, 12); // store this
// On login
const valid = await bcrypt.compare(inputPassword, storedHash);
Common security practices
- Always use HTTPS so tokens and data are encrypted in transit.
- Use short-lived access tokens plus refresh tokens.
- Rate-limit login attempts to prevent brute-force attacks.
- Check ownership, not just authentication — verify the user owns the resource they're accessing (prevents IDOR vulnerabilities).
OAuth 2.0 is the framework behind "Log in with Google/GitHub" — it lets an app get a scoped access token to act on your behalf without ever seeing your password.
Watch & Learn
A recommended video to watch alongside this chapter.
More “Authentication” videos on YouTube