Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Яка різниця між авторизацією та автентифікацією?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Автентифікація** підтверджує, хто ти є. **Авторизація** визначає, що ти можеш робити. ```javascript // Автентифікація не пройшла -> 401 (потрібен повторний логін) res.status(401).send('Невірні облікові дані'); // Авторизація не пройшла -> 403 (залогінений, але заблокований) res.status(403).send('Доступ заборонено'); ``` **Головне:** автентифікація завжди перша. 401 = особу не підтверджено, 403 = доступ закрито.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Автентифікація** підтверджує, хто ти є. **Авторизація** визначає, що ти можеш робити після цього. ## Теорія ### TL;DR - Автентифікація - показуєш ID на вході; авторизація - перевіряють, чи дає твій браслет доступ у VIP-зону - Автентифікація завжди перша, авторизація - після неї. Без першої немає другої - Переплутати HTTP-коди - класична помилка на співбесіді: 401 = "доведи, хто ти", 403 = "знаю хто ти, але ні" - JWT несе обидва: особу (автентифікація) і ролі/scopes (авторизація) в одному токені ### Швидкий приклад ```javascript 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 при помилці авторизації.** ```javascript // Неправильно if (!hasPermission) res.status(401).send('Немає доступу'); // Правильно if (!hasPermission) res.status(403).send('Forbidden'); ``` 401 каже клієнту повторити логін. 403 каже, що ти залогінений, але тебе заблоковано. Неправильний код запускає flow повторного входу, якого клієнт не потребує. **Зберігати роль у JWT без короткого терміну дії.** ```javascript // Неправильно: токен живе вічно, зміни ролі ігноруються jwt.sign({ role: 'admin' }, secret); // Краще: короткий термін + refresh з перевіркою у БД jwt.sign({ role: 'admin' }, secret, { expiresIn: '15m' }); ``` Якщо роль користувача понижена в базі, довгоживучий токен все одно дає адмін-доступ. Я бачив як це підводило команди: зі звільненого співробітника знімали права у БД, але токен ще кілька днів залишався дійсним. **Запускати авторизацію до автентифікації.** ```javascript // Неправильний порядок 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 з перевіркою при кожному запиті. ## Приклади ### Базовий: логін + захист адмін-маршруту ```javascript 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 після зміни у БД ```javascript // Токен виданий з роллю 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. Більше запитів, але правильні дозволи на кожному зверненні.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.