Backend
Intermediate 18 min read Backend

Authentication & Authorization

Authentication proves who you are; authorization decides what you may do. This concept covers sessions, hashed passwords, JWT, refresh tokens, OAuth 2.0 and the role/permission model that production APIs use to guard resources.

authenticationauthorizationJWTsessionsbcryptOAuth 2.0refresh tokens
Introduction

What is this?

Authentication verifies identity using something you know (password), have (TOTP key) or are (biometric). Authorization then enforces what that identity may do — typically via roles, scopes or fine-grained permissions. The three mainstream approaches are opaque sessions stored server-side, signed stateless JWTs, and delegated OAuth 2.0/OIDC flows for third-party logins.

Motivation

Why do you need it?

  • Passwords must never be stored in plaintext; hashing with a salt (bcrypt/argon2) protects users when a database leaks.
  • JWTs let any service verify a token without a session lookup, which simplifies scaling and microservice auth.
  • Short-lived access tokens plus rotating refresh tokens limit the blast radius if a token is stolen.
  • Separating authentication from authorization lets you grant or revoke permissions without logging users out.
Think about it

Mental model

Imagine

An airport boarding pass

Think of

A JWT access token

The pass proves you checked in (authenticated), names your gate (role), and is signed by the airline so it can't be forged. You show it at every gate (service) — each one just verifies the signature, no phone call to head office (session store) needed.

Mechanics

How it works

  • On login, the server verifies the credential (password hash compare, or OAuth code exchange) and issues a credential: a session ID or a signed JWT.
  • The client stores the credential (cookie or memory/secure storage) and sends it with each request, usually in the Authorization: Bearer header or a cookie.
  • The server verifies it — session lookup, or JWT signature + expiry check — and establishes the identity.
  • An authorization layer (middleware) maps identity to roles or scopes and checks them against the resource's required permissions.
  • On expiry or logout, the access token is dropped and a refresh token (or session revoke) obtains a new one without re-entering the password.

JWT authentication flow across login, requests and refresh.

User submits credentials
Verify password hash
Sign access token
Client stores token
Request with Bearer token
Verify signature + role check
Resource returned
Reference

Key commands

npm install jsonwebtoken bcrypt

Install JWT signing/verification and password hashing libraries.

added 12 packages in 1s
openssl rand -base64 48

Generate a cryptographically random secret for signing tokens.

q3VtFp9x... (random 48 bytes base64)
node -e "console.log(require('bcrypt').hashSync('hunter2', 12))"

Hash a password with a salt using 12 bcrypt rounds.

$2b$12$C6UzMDM.H6GQ7vp... (the salted hash)
curl -s -H 'Authorization: Bearer <token>' https://api.example.com/me

Call an endpoint with a JWT in the Authorization header.

{"sub":"user_42","role":"admin"}
Hands-on

Examples

Issue and verify a JWT with Node

Issue and verify a JWT with Node
const jwt = require("jsonwebtoken")
const SECRET = process.env.JWT_SECRET // must come from env, never source
// Issue: sign an access token valid for 15 minutes
const accessToken = jwt.sign(
{ sub: "user_42", role: "admin", scopes: ["todos:write"] },
SECRET,
{ expiresIn: "15m" }
)
// Verify: throw if expired or signature invalid
function verifyToken(token) {
try {
const payload = jwt.verify(token, SECRET)
return payload
} catch (err) {
throw new Error("invalid or expired token")
}
}
console.log(verifyToken(accessToken).role) // "admin"

jwt.sign produces a base64 payload signed with the secret. jwt.verify recomputes the signature and checks the exp claim, so a tampered or expired token throws. Because the token is self-contained, any service that knows the secret can verify it without touching a database — the trade-off is you cannot revoke it before it expires.

Real world

In production

  • SPAs and mobile apps authenticate with an authorization server (Auth0, Cognito, Keycloak) and attach JWTs to every API call.
  • Refresh-token rotation with httpOnly cookies is the standard pattern for long-lived web sessions.
  • OAuth 2.0 + OIDC gives 'Sign in with Google/GitHub' and lets third-party apps act on behalf of users with scoped tokens.
  • Service-to-service auth uses client-credentials flows or mTLS instead of user passwords.
Guidelines

Best practices

  • Hash passwords with bcrypt/argon2 (cost factor >= 10) — never encrypt or store plaintext.
  • Keep JWTs small, put only non-sensitive claims in them, and set short expiries (minutes to an hour).
  • Use RS256 with a public/private key pair so services verify without the signing key.
  • Store access tokens in memory on the client; use httpOnly + SameSite cookies and CSRF protection for session-based auth.
  • Implement rate limiting and account lockout on the login endpoint to slow brute-force attacks.
Watch out

Common mistakes

  • Putting secrets or a JWT_SECRET literal in source code or client bundles.
  • Using tokens as sessions: trying to 'log out' a JWT without a blocklist does not work.
  • Checking only that a token is valid, not whether the user/role is allowed — returning 401 instead of 403 for authorization failures.
  • Never invalidating tokens after a password change, leaving compromised sessions alive.
Root cause analysis

Common issues

Error
jwt expired
Root cause

The access token is past its exp claim.

Fix

Have the client use the refresh token to mint a new access token.

Error
invalid signature
Root cause

Token signed with a different secret or key, or tampered.

Fix

Ensure both sides use the same secret/keyset and correct algorithm (HS256 vs RS256).

Error
No auth token found
Root cause

The client sent no Authorization header or cookie.

Fix

Attach Authorization: Bearer <token> or ensure the cookie is being sent with the request.

When things break

Debugging tips

  • Decode a JWT locally with jwt.io or jwt.decode() to inspect the payload and exp claim.
  • Verify the signature algorithm: a token signed with HS256 will fail RS256 verification and vice versa.
  • Check Authorization header spelling: 'Bearer' with a capital B is required by most parsers.
  • Log sub, role and request path at the authorization middleware to trace deny decisions.
Interviews

Interview checkpoint

Practice

Hands-on lab

Password hashing, JWT and an auth middleware
40 min · intermediate
In progress
Objectives
  • Hash and verify passwords with bcrypt
  • Issue signed JWTs with expiry
  • Build a requireAuth middleware
  • Protect an endpoint and test 401 vs 200

Password hashing, JWT and an auth middleware

40 min · intermediate

0/5 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/3
1

Which status code should an API return when a user is authenticated but lacks permission for an action?

2

Why should passwords be hashed with a salt using bcrypt or argon2?

3

A JWT access token can be instantly revoked before it expires without extra server-side infrastructure.

Quick reference

Cheat sheet

Tokens
jwt.sign(payload, secret, { expiresIn: '15m' })Issue an access token
jwt.verify(token, secret)Verify signature + expiry
Authorization: Bearer <token>Standard header format
Passwords
bcrypt.hash(password, 12)Hash with a salt
bcrypt.compare(password, hash)Verify a login
openssl rand -base64 48Generate a signing secret
Go deeper

Official documentation