Suggest an editImprove this articleRefine the answer for “What is the difference between authorization and authentication?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Authentication** confirms who you are. **Authorization** decides what you can access. ```javascript // Auth fails -> 401 (re-login needed) res.status(401).send('Invalid credentials'); // Authz fails -> 403 (logged in, but blocked) res.status(403).send('Access denied'); ``` **Key:** authentication always runs first. No identity, no permission check.Shown above the full answer for quick recall.Answer (EN)Image**Authentication** confirms who you are. **Authorization** decides what you can do after that. ## Theory ### TL;DR - Authentication is showing your ID at the door; authorization is checking if your wristband allows VIP access - Authentication always runs first, authorization second. Skip auth, skip authz too - Wrong HTTP code is a red flag in interviews: 401 = "prove who you are", 403 = "I know who you are, but no" - JWT carries both: identity (auth) and roles/scopes (authz) in one token ### Quick example ```javascript const express = require('express'); const app = express(); // Authentication: who are you? app.post('/login', (req, res) => { if (req.body.password === 'secret') { res.json({ token: 'user-jwt-token' }); // identity confirmed } else { res.status(401).send('Invalid credentials'); // 401 = auth failed } }); // Authorization: what can you do? app.get('/admin', (req, res) => { const token = req.headers.authorization; if (token !== 'Bearer admin-token') { return res.status(403).send('Access denied'); // 403 = authz failed } res.send('Admin data'); }); ``` 401 when login fails. 403 when the user is logged in but lacks the role. Two different problems, two different codes. ### Key difference Authentication answers "Are you who you claim to be?" It checks credentials: a password, a JWT, a fingerprint. Authorization answers "Can you do this?" It checks permissions: roles, scopes, ACL entries. One confirms identity, the other enforces access rules. No authentication means no authorization, because you have no identity to check permissions against. ### When to use - Public page (landing, blog): skip both - User profile page: authenticate the user, then authorize access to their own data only - Admin dashboard: authenticate + check admin role - API with rate limits: authenticate to identify the caller, authorize based on their plan tier ### Comparison table | Aspect | Authentication | Authorization | |---|---|---| | Question | Who are you? | What can you do? | | Timing | First (login) | Second (resource access) | | Mechanisms | Passwords, JWT, OAuth, biometrics | RBAC, ACL, scopes in JWT | | Failure code | 401 Unauthorized | 403 Forbidden | | Libraries | Passport.js, Auth0, NextAuth.js | Casbin, CASL, node-acl | | When to use | Entry gate (prove identity) | Room keys (grant specific access) | ### How the mechanism works In Express with Passport.js, the auth middleware reads credentials from the request body or OAuth callback, verifies them against a database or provider, and generates a JWT with the user's role embedded. The authz middleware (CASL or Casbin) then decodes that token, pulls the role, and checks it against the policy for the requested resource. If the role does not match, it returns 403 and stops there. ### Common mistakes **Using 401 for authorization failures.** ```javascript // Wrong if (!hasPermission) res.status(401).send('No access'); // Correct if (!hasPermission) res.status(403).send('Forbidden'); ``` 401 tells the client to re-authenticate (retry with valid credentials). 403 tells them they are authenticated but blocked. The wrong code triggers re-login flows the client does not need. **Storing roles in JWT without a short expiry.** ```javascript // Wrong: token lives forever, role changes are ignored jwt.sign({ role: 'admin' }, secret); // Better: short-lived token + refresh with DB check jwt.sign({ role: 'admin' }, secret, { expiresIn: '15m' }); ``` If a user's role gets downgraded in the database, a long-lived token still grants admin access. I've seen this hit teams after an employee was removed from a project but their token stayed valid for days. **Running authorization before authentication.** ```javascript // Wrong order app.get('/admin', checkRole, verifyToken); // Correct order app.get('/admin', verifyToken, checkRole); ``` Check the role before verifying the token, and a crafted request with a fake `role: admin` header slips right through. **Treating an active session as proof of permissions.** Sessions confirm the user logged in. They do not say what that user can do right now. If a role changes after login, the session still reflects the old state unless you re-check the database on each request. ### Real-world usage - Express.js: Passport.js for auth, `express-jwt` + CASL for authz - React/Next.js: NextAuth.js for auth, `useAbility` from CASL for component-level authz - AWS Lambda: Cognito for auth, IAM policies + API Gateway for authz - Spring Boot: Spring Security handles both; `@PreAuthorize` enforces method-level authz - Microservices: stateless JWT across services, short-lived tokens + Redis token blacklist for revocation ### Follow-up questions **Q:** What HTTP status do you return for each failure? **A:** 401 for authentication failures (the client needs to log in or send valid credentials). 403 for authorization failures (identity confirmed, access blocked). **Q:** How does JWT fit into both processes? **A:** JWT is the carrier. Authentication generates it after verifying credentials. Authorization reads it to extract roles and scopes. One token, two jobs. **Q:** How does OAuth 2.0 relate? **A:** OAuth handles authentication via a provider (Google, GitHub). The returned access token carries scopes that drive authorization on your API. **Q:** What is the difference between RBAC and ABAC? **A:** RBAC grants permissions based on a role (admin can delete). ABAC adds attributes like time, location, or resource owner to create more granular rules. **Q:** A user's admin role was revoked in the database, but their JWT still has `role: admin`. What do you do? **A:** Use short-lived access tokens (15 minutes) plus a refresh token flow. On each refresh, re-check the database for the current role. Or maintain a token blacklist in Redis and validate against it on every request. ## Examples ### Basic: login + admin guard ```javascript const jwt = require('jsonwebtoken'); const secret = 'mysecret'; // Authentication: verify credentials, issue token app.post('/login', (req, res) => { if (req.body.user === 'admin' && req.body.pass === 'pass') { const token = jwt.sign({ role: 'admin' }, secret, { expiresIn: '15m' }); res.json({ token }); } else { res.status(401).json({ error: 'Invalid credentials' }); } }); // Authorization: check role from token app.get('/users', authenticateToken, (req, res) => { if (req.user.role !== 'admin') { return res.status(403).json({ error: 'Admin access required' }); } res.json({ users: ['alice', 'bob'] }); }); function authenticateToken(req, res, next) { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'Token required' }); jwt.verify(token, secret, (err, user) => { if (err) return res.status(401).json({ error: 'Invalid token' }); req.user = user; next(); }); } ``` `authenticateToken` handles authentication: it verifies the token and attaches the decoded user to the request. The role check inside `/users` is authorization. Same user, different question. ### Advanced: stale role in JWT after DB change ```javascript // Token issued with admin role const token = jwt.sign( { userId: 1, role: 'admin' }, secret, { expiresIn: '1h' } // too long ); // 30 seconds later: role changed to 'user' in DB // The token still says 'admin' app.get('/secure', (req, res) => { jwt.verify(token, secret, (err, decoded) => { if (decoded.role === 'admin') { res.send('Admin access granted'); // Wrong: trusts stale token } }); }); // Fix: short-lived tokens + DB check on refresh app.post('/refresh', async (req, res) => { const user = await db.users.findById(decoded.userId); // fresh role from DB const newToken = jwt.sign( { userId: user.id, role: user.role }, secret, { expiresIn: '15m' } ); res.json({ token: newToken }); }); ``` Long-lived tokens are the most common source of stale authorization data in distributed systems. Short expiry forces a DB sync on refresh. More refresh requests, but correct permissions on every call.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.