Яка різниця між авторизацією та автентифікацією?
Автентифікація підтверджує, хто ти є. Авторизація визначає, що ти можеш робити після цього.
Теорія
TL;DR
- Автентифікація - показуєш ID на вході; авторизація - перевіряють, чи дає твій браслет доступ у VIP-зону
- Автентифікація завжди перша, авторизація - після неї. Без першої немає другої
- Переплутати HTTP-коди - класична помилка на співбесіді: 401 = "доведи, хто ти", 403 = "знаю хто ти, але ні"
- JWT несе обидва: особу (автентифікація) і ролі/scopes (авторизація) в одному токені
Швидкий приклад
const express = require('express');
const app = express();
// Автентифікація: хто ти?
app.post('/login', (req, res) => {
if (req.body.password === 'secret') {
res.json({ token: 'user-jwt-token' }); // особу підтверджено
} else {
res.status(401).send('Невірні облікові дані'); // 401 = автентифікація не пройшла
}
});
// Авторизація: що ти можеш?
app.get('/admin', (req, res) => {
const token = req.headers.authorization;
if (token !== 'Bearer admin-token') {
return res.status(403).send('Доступ заборонено'); // 403 = авторизація не пройшла
}
res.send('Дані адміна');
});401 коли логін не пройшов. 403 коли користувач залогінений, але не має потрібної ролі. Дві різні проблеми, два різні коди.
Головна різниця
Автентифікація відповідає на питання "Ти той, за кого себе видаєш?" Вона перевіряє облікові дані: пароль, JWT, відбиток пальця. Авторизація відповідає "Чи можеш ти це зробити?" Вона перевіряє дозволи: ролі, scopes, записи ACL. Одна підтверджує особу, інша застосовує правила доступу. Без автентифікації авторизація неможлива - немає особи, яку можна перевіряти.
Коли що застосовувати
- Публічна сторінка (лендінг, блог): пропустити обидва
- Сторінка профілю: автентифікувати користувача, потім дозволити доступ тільки до його власних даних
- Адмін-панель: автентифікація + перевірка ролі admin
- API з rate limiting: автентифікація для ідентифікації запиту, авторизація на основі тарифного плану
Порівняльна таблиця
| Аспект | Автентифікація | Авторизація |
|---|---|---|
| Питання | Хто ти? | Що ти можеш? |
| Порядок | Перша (логін) | Друга (доступ до ресурсу) |
| Механізми | Паролі, JWT, OAuth, біометрія | RBAC, ACL, scopes у JWT |
| HTTP-код при помилці | 401 Unauthorized | 403 Forbidden |
| Бібліотеки | Passport.js, Auth0, NextAuth.js | Casbin, CASL, node-acl |
| Коли використовувати | Вхідні ворота (доведи особу) | Ключі від кімнат (отримай доступ) |
Як це працює зсередини
В Express з Passport.js, middleware автентифікації читає облікові дані з тіла запиту або OAuth-колбеку, перевіряє їх через базу даних або зовнішній провайдер і генерує JWT з роллю користувача всередині. Middleware авторизації (CASL або Casbin) декодує цей токен, витягує роль і порівнює її з правилами для запитуваного ресурсу. Якщо роль не підходить - повертається 403.
Типові помилки
Повертати 401 замість 403 при помилці авторизації.
// Неправильно
if (!hasPermission) res.status(401).send('Немає доступу');
// Правильно
if (!hasPermission) res.status(403).send('Forbidden');401 каже клієнту повторити логін. 403 каже, що ти залогінений, але тебе заблоковано. Неправильний код запускає flow повторного входу, якого клієнт не потребує.
Зберігати роль у JWT без короткого терміну дії.
// Неправильно: токен живе вічно, зміни ролі ігноруються
jwt.sign({ role: 'admin' }, secret);
// Краще: короткий термін + refresh з перевіркою у БД
jwt.sign({ role: 'admin' }, secret, { expiresIn: '15m' });Якщо роль користувача понижена в базі, довгоживучий токен все одно дає адмін-доступ. Я бачив як це підводило команди: зі звільненого співробітника знімали права у БД, але токен ще кілька днів залишався дійсним.
Запускати авторизацію до автентифікації.
// Неправильний порядок
app.get('/admin', checkRole, verifyToken);
// Правильний порядок
app.get('/admin', verifyToken, checkRole);Якщо перевіряти роль до верифікації токена, запит з підробленим заголовком role: admin пройде перевірку без проблем.
Вважати активну сесію підтвердженням дозволів.
Сесії підтверджують, що користувач залогінився. Вони не показують, що він може робити прямо зараз. Якщо роль змінилася після логіну, сесія відображає старий стан - якщо не перечитувати БД при кожному запиті.
Де це зустрічається у реальних проектах
- Express.js: Passport.js для автентифікації,
express-jwt+ CASL для авторизації - React/Next.js: NextAuth.js для автентифікації,
useAbilityз CASL для авторизації на рівні компонентів - AWS Lambda: Cognito для автентифікації, IAM-політики + API Gateway для авторизації
- Spring Boot: Spring Security закриває обидва процеси;
@PreAuthorizeдля авторизації на рівні методів - Мікросервіси: stateless JWT між сервісами, короткоживучі токени + Redis-блокліст для відкликання доступу
Питання на співбесіді
Q: Який HTTP-статус повертати при кожній з помилок?
A: 401 при помилці автентифікації (клієнту потрібно залогінитися або надіслати валідні дані). 403 при помилці авторизації (особу підтверджено, але доступ закрито).
Q: Як JWT пов'язаний з обома процесами?
A: JWT - це носій. Автентифікація його генерує після перевірки облікових даних. Авторизація його читає, щоб витягти ролі та scopes. Один токен, дві функції.
Q: Як OAuth 2.0 вписується у цю схему?
A: OAuth займається автентифікацією через зовнішнього провайдера (Google, GitHub). Отриманий access token містить scopes, які керують авторизацією на твоєму API.
Q: Яка різниця між RBAC та ABAC?
A: RBAC (контроль доступу на основі ролей) дає дозволи на основі ролі (адмін може видаляти). ABAC (на основі атрибутів) додає час, місце, власника ресурсу - що дає детальніші правила.
Q: Роль admin забрали у БД, але JWT ще має role: admin. Що робити?
A: Короткоживучі access токени (15 хвилин) плюс refresh token flow. При кожному refresh - запит до БД на актуальну роль. Альтернатива: блокліст токенів у Redis з перевіркою при кожному запиті.
Приклади
Базовий: логін + захист адмін-маршруту
const jwt = require('jsonwebtoken');
const secret = 'mysecret';
// Автентифікація: перевірити облікові дані, видати токен
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: 'Невірні облікові дані' });
}
});
// Авторизація: перевірити роль з токена
app.get('/users', authenticateToken, (req, res) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Потрібен доступ адміна' });
}
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: 'Потрібен токен' });
jwt.verify(token, secret, (err, user) => {
if (err) return res.status(401).json({ error: 'Невірний токен' });
req.user = user;
next();
});
}authenticateToken займається автентифікацією: перевіряє токен і прикріплює декодованого користувача до запиту. Перевірка ролі всередині /users - це авторизація. Той самий користувач, інше питання.
Просунутий: застаріла роль у JWT після зміни у БД
// Токен виданий з роллю admin
const token = jwt.sign(
{ userId: 1, role: 'admin' },
secret,
{ expiresIn: '1h' } // занадто довго
);
// Через 30 секунд: роль змінена на 'user' у БД
// Але токен досі каже 'admin'
app.get('/secure', (req, res) => {
jwt.verify(token, secret, (err, decoded) => {
if (decoded.role === 'admin') {
res.send('Доступ адміна надано'); // Неправильно: довіряє застарілому токену
}
});
});
// Виправлення: короткоживучі токени + перевірка БД при refresh
app.post('/refresh', async (req, res) => {
const user = await db.users.findById(decoded.userId); // актуальна роль з БД
const newToken = jwt.sign(
{ userId: user.id, role: user.role },
secret,
{ expiresIn: '15m' }
);
res.json({ token: newToken });
});Довгоживучі токени - найпоширеніше джерело застарілих даних авторизації в розподілених системах. Короткий термін дії змушує синхронізуватись із БД при кожному refresh. Більше запитів, але правильні дозволи на кожному зверненні.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.