Стратегії автентифікації
Стратегії автентифікації
Короткий зміст
У цій лекції розглядається огляд існуючих підходів до автентифікації у веб-застосунках:
- Session-based (Stateful) — автентифікація на основі серверних сесій з використанням cookies, зберігання стану на сервері (Redis, БД)
- Token-based (Stateless) — автентифікація за допомогою JWT токенів, відсутність серверного стану, передача токена у заголовку Authorization
- OAuth 2.0 — делегована авторизація через сторонні провайдери (Google, GitHub, Facebook), потоки авторизації
- Single Sign-On (SSO) — єдиний вхід для корпоративних застосунків через SAML або OpenID Connect
Вивчається порівняльний аналіз кожного підходу з точки зору безпеки, масштабованості та складності реалізації. Розглядаються критерії вибору стратегії залежно від вимог проєкту: розмір системи, необхідність горизонтального масштабування, вимоги до безпеки, підтримка мобільних клієнтів.
🎯 Мета лекції
- Опанувати фундаментальні підходи до автентифікації у сучасних веб-застосунках.
- Зрозуміти концептуальну різницю між stateful та stateless архітектурами автентифікації.
- Навчитися обирати оптимальну стратегію залежно від вимог проєкту та архітектурних обмежень.
- Ознайомитися з делегованою автентифікацією через OAuth 2.0 та концепцією Single Sign-On для корпоративних систем.
🔑 Ключові терміни
- Stateful Authentication: підхід, де стан сесії користувача зберігається на сервері або у розподіленому сховищі.
- Stateless Authentication: підхід, де всі дані про автентифікацію містяться у токені, переданому клієнтом.
- Session ID: унікальний ідентифікатор серверної сесії, що зберігається у cookie браузера.
- Bearer Token: токен доступу, що передається у заголовку
Authorization: Bearer <token>кожного запиту.
Фундаментальна дихотомія: Stateful vs Stateless
Вибір стратегії автентифікації визначається фундаментальним архітектурним рішенням — де система зберігатиме стан сесії користувача (session state). Це рішення впливає на масштабованість, безпеку, складність інфраструктури та можливості горизонтального розширення системи.
Stateful автентифікація (автентифікація зі збереженням стану) означає, що після успішного входу сервер створює запис сесії у своїй пам'яті або у зовнішньому сховищі (Redis, PostgreSQL) і присвоює їй унікальний ідентифікатор — session ID. Цей ідентифікатор передається клієнту у вигляді cookie, і кожен наступний запит від браузера автоматично включає цей cookie. Сервер отримує session ID, шукає відповідну сесію у сховищі і витягує з неї дані користувача — ідентифікатор, ролі, час останньої активності.
Stateless автентифікація (автентифікація без збереження стану) означає, що сервер не зберігає жодної інформації про активні сесії користувачів. Замість цього після успішного входу сервер генерує токен (token) — підписану структуру даних, що містить ідентифікатор користувача, ролі, час видачі та час закінчення терміну дії. Цей токен передається клієнту, який зберігає його локально (у localStorage, sessionStorage або cookie) і відправляє у заголовку Authorization кожного запиту. Сервер перевіряє підпис токена, витягує дані користувача безпосередньо з токена і не звертається до бази даних чи Redis для пошуку сесії.
Діаграма архітектурних підходів
Розглянемо візуальне порівняння двох підходів у момент обробки запиту від автентифікованого користувача:
Ключова відмінність: у stateful підході сервер залежить від зовнішнього сховища для отримання даних користувача, тоді як у stateless підході сервер самодостатній — йому достатньо перевірити підпис токена за допомогою свого секретного ключа.
Session-Based автентифікація (Stateful)
Session-based автентифікація є історично першим та інтуїтивно зрозумілим підходом, що виник разом із динамічними веб-застосунками у середині 1990-х років. Цей підхід природно поєднується з механізмом HTTP cookies, вбудованим у всі сучасні браузери.
Життєвий цикл сесії
Розглянемо покрокову послідовність створення, використання та знищення сесії у типовому веб-застосунку:
Крок 1. Вхід користувача (Login)
Користувач надсилає POST-запит на ендпоінт /auth/login з обліковими даними — email та паролем. Сервер перевіряє пароль (порівнює bcrypt хеш), і якщо автентифікація успішна, генерує унікальний session ID за допомогою криптографічно стійкого генератора випадкових чисел.
// Спрощений приклад генерації session ID
import { randomBytes } from 'crypto';
function generateSessionId(): string {
return randomBytes(32).toString('hex'); // 64 символи у hex форматі
}
const sessionId = generateSessionId();
// Приклад: "a3f5c8e2d9b1f4e7a6c3d8e9f2b5a7c4..."
Крок 2. Збереження сесії на сервері
Сервер створює запис сесії у сховищі (Redis, PostgreSQL, MongoDB) з асоційованими даними користувача:
// Структура даних сесії
interface Session {
sessionId: string;
userId: string;
email: string;
roles: string[];
createdAt: Date;
expiresAt: Date;
lastActivityAt: Date;
ipAddress: string;
userAgent: string;
}
Для швидкого доступу часто використовують Redis з автоматичним TTL (time to live):
await redis.setex(
`session:${sessionId}`,
3600, // TTL 1 година
JSON.stringify({
userId: user.id,
email: user.email,
roles: user.roles,
})
);
Крок 3. Передача session ID клієнту через cookie
Сервер встановлює HTTP cookie з session ID у відповіді на успішний логін:
response.cookie('sessionId', sessionId, {
httpOnly: true, // Захист від XSS — cookie недоступне через JavaScript
secure: true, // Передача лише через HTTPS
sameSite: 'lax', // Захист від CSRF атак
maxAge: 3600000, // 1 година у мілісекундах
path: '/', // Cookie діє для всього сайту
});
Крок 4. Автоматична передача cookie у наступних запитах
Браузер автоматично включає cookie у заголовки всіх наступних запитів до того самого домену:
GET /api/v1/users/profile HTTP/1.1
Host: example.com
Cookie: sessionId=a3f5c8e2d9b1f4e7a6c3d8e9f2b5a7c4...
Сервер витягує session ID з cookie, шукає сесію у Redis, завантажує дані користувача і обробляє запит.
Крок 5. Оновлення часу останньої активності
Для реалізації механізму sliding expiration (ковзного закінчення терміну) сервер оновлює поле lastActivityAt при кожному запиті:
await redis.expire(`session:${sessionId}`, 3600); // Продовжити TTL на 1 годину
Це дозволяє автоматично закривати неактивні сесії, але зберігати активні навіть після закінчення початкового терміну.
Крок 6. Вихід користувача (Logout)
При явному виході користувач надсилає запит на /auth/logout. Сервер видаляє сесію зі сховища та скидає cookie:
await redis.del(`session:${sessionId}`);
response.clearCookie('sessionId');
Переваги session-based автентифікації
✅ Серверний контроль
✅ Легке відкликання
✅ Менший розмір даних у запитах
✅ Природна інтеграція з браузерами
Authorization до кожного fetch/axios запиту.Недоліки та архітектурні обмеження
❌ Складність масштабування
❌ Навантаження на сховище
❌ Обмеження у мобільних та SPA застосунках
❌ Проблеми з CORS та крос-доменними запитами
app.example.com, а API на api.example.com, браузер не передає cookies у крос-доменних запитах за замовчуванням. Потрібна точна конфігурація CORS з credentials: 'include' та атрибутом SameSite=None; Secure.Token-Based автентифікація (Stateless)
Token-based автентифікація набула популярності з розвитком Single Page Applications (SPA) та мобільних застосунків, де традиційний механізм cookies виявився обтяжливим. Найпоширенішим форматом токенів є JSON Web Token (JWT) — стандарт RFC 7519, що визначає компактний та самодостатній формат для безпечної передачі claims між сторонами.
Архітектура JWT токенів
JWT токен складається з трьох частин, розділених крапками:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NTBlODQwMCIsImVtYWlsIjoiaXZhbkBleGFtcGxlLmNvbSIsInJvbGVzIjpbInVzZXIiXSwiaWF0IjoxNjkzNTY0ODAwLCJleHAiOjE2OTM1Njg0MDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Структура токена:
- Header (Заголовок): містить тип токена (
JWT) та алгоритм підпису (HS256,RS256,ES256). - Payload (Корисне навантаження): містить claims — дані про користувача, ролі, час видачі та закінчення.
- Signature (Підпис): криптографічний підпис перших двох частин, що гарантує цілісність та автентичність токена.
Кожна частина кодується у Base64URL (модифікація Base64 для безпечного використання у URL).
Життєвий цикл JWT автентифікації
Крок 1. Вхід користувача та генерація токена
Після успішної перевірки паролю сервер генерує JWT токен з даними користувача:
import { sign } from 'jsonwebtoken';
const accessToken = sign(
{
sub: user.id, // Subject — ідентифікатор користувача
email: user.email,
roles: user.roles,
iat: Math.floor(Date.now() / 1000), // Issued At — час видачі
},
process.env.JWT_SECRET, // Секретний ключ для підпису
{
expiresIn: '15m', // Токен діє 15 хвилин
algorithm: 'HS256', // Алгоритм підпису HMAC SHA-256
}
);
Крок 2. Передача токена клієнту
Сервер повертає токен у JSON-відповіді:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 900
}
Крок 3. Зберігання токена на клієнті
Frontend зберігає токен локально — зазвичай у localStorage або sessionStorage:
// Збереження токена після логіну
localStorage.setItem('accessToken', response.accessToken);
// Витягування токена для наступних запитів
const token = localStorage.getItem('accessToken');
localStorage робить їх вразливими до XSS атак (Cross-Site Scripting). Якщо зловмисник впровадить шкідливий JavaScript на сторінку (через вразливість у коді або сторонню бібліотеку), він зможе зчитати токен та надіслати його на свій сервер. Альтернативою є зберігання у HttpOnly cookies, але це повертає проблеми з CORS. Ми розглянемо безпечні підходи детальніше у лекції про refresh tokens.Крок 4. Передача токена у заголовках запитів
При кожному запиті до API frontend додає токен у заголовок Authorization:
const response = await fetch('https://api.example.com/users/profile', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
Крок 5. Перевірка токена на сервері
Сервер витягує токен з заголовка, перевіряє підпис та декодує payload:
import { verify } from 'jsonwebtoken';
try {
const payload = verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
});
// Токен дійсний — дані користувача доступні у payload
const userId = payload.sub;
const roles = payload.roles;
} catch (error) {
// Токен недійсний, застарів або підроблений
throw new UnauthorizedException('Invalid token');
}
Критично важливо: сервер не звертається до бази даних для перевірки токена. Він лише перевіряє криптографічний підпис за допомогою свого секретного ключа. Якщо підпис співпадає, сервер довіряє вмісту токена.
Переваги token-based автентифікації
✅ Горизонтальне масштабування
✅ Відсутність навантаження на сховище
✅ Універсальність для різних платформ
✅ Крос-доменна сумісність
app.example.com може вільно викликати API на api.example.com без складної конфігурації cookies.Недоліки та ризики безпеки
❌ Неможливість миттєвого відкликання
exp claim. Вирішення вимагає додаткової інфраструктури — blacklist токенів у Redis або скорочення терміну життя токенів до 5–15 хвилин з механізмом refresh tokens.❌ Більший розмір запитів
❌ Вразливість до XSS атак
localStorage або sessionStorage, він доступний для будь-якого JavaScript коду на сторінці. Успішна XSS атака дозволяє зловмиснику викрасти токен та видавати себе за користувача до закінчення терміну дії токена.❌ Складність управління секретними ключами
JWT_SECRET) має зберігатися у безпеці та регулярно ротуватися. При компрометації ключа всі видані токени стають вразливими. У розподілених системах доводиться синхронізувати ключі між усіма сервісами або переходити до асиметричного шифрування (RS256) з публічними/приватними ключами.OAuth 2.0: Делегована авторизація
OAuth 2.0 — це не стратегія автентифікації у традиційному розумінні, а протокол делегованої авторизації (delegated authorization). Він дозволяє користувачеві надати стороннім застосункам обмежений доступ до своїх ресурсів на іншому сервісі без розкриття паролю. Найпоширеніший приклад — кнопка «Увійти через Google» або «Увійти через GitHub».
Концептуальна модель OAuth 2.0
Уявіть ситуацію: ви хочете дозволити сервісу друку фотографій (третя сторона) отримати доступ до ваших фотографій у хмарному сховищі (власник ресурсу — Google Drive). Традиційний підхід вимагав би, щоб ви повідомили сервісу друку свій пароль від Google. Це створює величезні ризики:
- Сервіс отримує повний доступ до вашого облікового запису, а не лише до фотографій.
- Ви не можете відкликати доступ без зміни паролю (що вплине на всі інші застосунки).
- Сервіс може зберігати ваш пароль у незахищеному вигляді.
OAuth 2.0 вирішує цю проблему через токени доступу з обмеженою областю видимості (scoped access tokens). Замість паролю сервіс друку отримує токен, що дозволяє лише читати фотографії (scope: read:photos), але не змінювати налаштування облікового запису або читати електронну пошту.
Учасники протоколу
OAuth 2.0 визначає чотири ключові ролі:
👤 Resource Owner (Власник ресурсу)
🖥️ Client (Клієнт)
🔐 Authorization Server (Сервер авторизації)
accounts.google.com або github.com/login/oauth.📦 Resource Server (Сервер ресурсів)
Потік авторизації Authorization Code (найбезпечніший)
Розглянемо покроковий процес входу користувача через Google у ваш веб-застосунок:
Крок 1. Перенаправлення на сервер авторизації
Користувач натискає кнопку «Увійти через Google» у вашому застосунку. Frontend перенаправляє браузер на URL авторизації Google:
https://accounts.google.com/o/oauth2/v2/auth?
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/auth/callback&
response_type=code&
scope=openid%20email%20profile&
state=random_string_for_csrf_protection
Параметри:
client_id— публічний ідентифікатор вашого застосунку, зареєстрований у Google Cloud Console.redirect_uri— URL вашого застосунку, куди Google поверне користувача після авторизації.response_type=code— запит на отримання authorization code (перший етап потоку).scope— запитувані дозволи (email,profile,read:photosтощо).state— випадковий рядок для захисту від CSRF атак (Cross-Site Request Forgery).
Крок 2. Автентифікація та згода користувача
Google відображає екран входу (якщо користувач ще не автентифікований) та екран згоди (consent screen), де перелічені запитувані дозволи:
Застосунок "YourApp" запитує дозвіл на:
✓ Перегляд вашої електронної пошти
✓ Перегляд основної інформації профілю
Користувач натискає «Дозволити» або «Відхилити».
Крок 3. Повернення authorization code
Якщо користувач погодився, Google перенаправляє браузер назад до вашого застосунку з тимчасовим authorization code:
https://yourapp.com/auth/callback?
code=4/0AX4XfWh...authorization_code...&
state=random_string_for_csrf_protection
Authorization code є короткоживучим (зазвичай 10–60 секунд) та одноразовим — його можна обміняти на токени лише один раз.
Крок 4. Обмін authorization code на токени
Ваш backend отримує authorization code та надсилає POST-запит до Google Token Endpoint з client secret (секретний ключ, що зберігається на сервері):
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
code: authorizationCode,
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET, // Секрет!
redirect_uri: 'https://yourapp.com/auth/callback',
grant_type: 'authorization_code',
}),
});
const tokens = await response.json();
/*
{
"access_token": "ya29.a0AfH6...",
"expires_in": 3600,
"refresh_token": "1//0gFz...",
"scope": "openid email profile",
"token_type": "Bearer",
"id_token": "eyJhbGciOiJSUzI1NiIs..." // JWT з даними користувача
}
*/
Крок 5. Використання access token для доступу до API
Ваш backend може тепер викликати Google API від імені користувача:
const profileResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
headers: { 'Authorization': `Bearer ${tokens.access_token}` },
});
const profile = await profileResponse.json();
/*
{
"id": "1234567890",
"email": "user@example.com",
"verified_email": true,
"name": "Іван Петренко",
"picture": "https://lh3.googleusercontent.com/..."
}
*/
client_secret ніколи не потрапляє у браузер (де його можна викрасти через DevTools або шкідливі розширення). Альтернативний потік Implicit Flow (передача токена через URL у браузері) вважається застарілим та небезпечним для сучасних застосунків.Коли використовувати OAuth 2.0
OAuth 2.0 є оптимальним вибором у наступних сценаріях:
- Інтеграція з соціальними мережами: «Увійти через Google/Facebook/GitHub» для спрощення реєстрації.
- Доступ до сторонніх API: ваш застосунок потребує читати дані користувача з іншого сервісу (Google Drive, Dropbox, Spotify).
- Мікросервісна архітектура: делегування автентифікації центральному Identity Provider замість дублювання логіки у кожному сервісі.
- Mobile та Desktop застосунки: OAuth 2.0 має спеціальні потоки для нативних застосунків (PKCE — Proof Key for Code Exchange).
id_token (JWT з claims про користувача) та /userinfo ендпоінт. Більшість сучасних провайдерів (Google, Microsoft, Auth0) реалізують саме OIDC, а не чистий OAuth 2.0.Single Sign-On (SSO): Єдиний вхід для корпоративних систем
Single Sign-On (SSO) — це архітектурний патерн, що дозволяє користувачеві автентифікуватися один раз у центральній системі і отримати доступ до множини пов'язаних застосунків без повторного введення облікових даних. SSO є стандартом де-facto у корпоративних середовищах, де співробітники працюють з десятками внутрішніх сервісів.
Переваги SSO для організацій
👥 Покращений user experience
🔒 Централізована політика безпеки
📊 Аудит та моніторинг
⚙️ Спрощення інтеграції нових застосунків
Протоколи SSO: SAML 2.0 vs OpenID Connect
Існують два основні протоколи для реалізації SSO:
SAML 2.0 (Security Assertion Markup Language) — застарілий, але широко використовуваний у корпоративному середовищі XML-базований протокол. Підтримується великими enterprise системами (Microsoft Active Directory, Okta, Ping Identity). Складний у налаштуванні, але перевірений часом.
OpenID Connect (OIDC) — сучасне розширення OAuth 2.0, що використовує JSON та REST API замість XML. Простіший у реалізації, краще підходить для веб-застосунків та мобільних клієнтів. Підтримується Google Workspace, Microsoft Azure AD, Auth0.
Архітектура SSO на основі OIDC
Ключовий момент: Identity Provider зберігає власну SSO сесію (зазвичай у cookie sso_session з терміном дії 8–12 годин). Коли користувач намагається увійти у другий застосунок, IdP виявляє активну сесію та автоматично видає authorization code без повторного запиту паролю.
Порівняльна таблиця стратегій
Для ефективного вибору стратегії автентифікації необхідно розуміти компроміси між безпекою, продуктивністю та складністю реалізації. Розглянемо систематичне порівняння підходів:
| Критерій | Session-Based | Token-Based (JWT) | OAuth 2.0 + OIDC | SSO (SAML/OIDC) |
|---|---|---|---|---|
| Збереження стану | Stateful (Redis/DB) | Stateless | Stateless | Stateful (у IdP) |
| Масштабування | ❌ Складне (потрібен Redis Cluster) | ✅ Горизонтальне без обмежень | ✅ Горизонтальне без обмежень | ⚠️ Залежить від IdP |
| Миттєве відкликання | ✅ Видалення з Redis | ❌ Потрібен blacklist | ⚠️ Залежить від реалізації | ✅ Централізоване у IdP |
| Продуктивність | ⚠️ Запит до Redis на кожен запит | ✅ Лише перевірка підпису | ⚠️ Додаткові запити до IdP | ⚠️ Додаткові запити до IdP |
| Безпека токенів | ✅ HttpOnly cookies | ❌ Вразливість до XSS | ⚠️ Залежить від зберігання | ✅ Централізоване управління |
| Мобільні застосунки | ❌ Немає нативних cookies | ✅ Природна інтеграція | ✅ Природна інтеграція | ✅ OIDC потоки для mobile |
| CORS складність | ❌ Потрібна точна конфігурація | ✅ Без проблем | ✅ Без проблем | ✅ Без проблем |
| Розмір запиту | ✅ 32–64 байти (session ID) | ❌ 500–1500 байтів (JWT) | ❌ 500–2000 байтів | ❌ 1000–3000 байтів |
| Складність реалізації | ✅ Проста (вбудована у фреймворки) | ⚠️ Середня (потрібна бібліотека) | ❌ Складна (протокол OAuth) | ❌ Дуже складна (enterprise) |
| Типові Use Cases | Традиційні веб-застосунки | SPA, мобільні, мікросервіси | Інтеграція з соціальними мережами | Корпоративні системи |
Критерії вибору стратегії для вашого проєкту
Вибір стратегії автентифікації залежить від архітектурних вимог, масштабу системи та команди розробників. Розглянемо практичні сценарії:
Сценарій 1: Малий монолітний застосунок (стартап MVP)
Вимоги:
- Команда 2–3 розробники, обмежений бюджет
- Очікувана аудиторія: 1000–10000 користувачів
- Традиційний веб-застосунок (server-side rendering)
- Немає планів на мобільні застосунки у найближчі 6 місяців
Рекомендація: Session-Based автентифікація
Обґрунтування:
- Простота реалізації —
express-sessionабо@nestjs/passport-localналаштовуються за 30 хвилин. - Немає необхідності у складних бібліотеках JWT або конфігурації ключів.
- Для малої аудиторії Redis Cluster не потрібен — достатньо одного екземпляра Redis на виділеному сервері або керованого сервісу (AWS ElastiCache, Azure Redis).
- Природна інтеграція з браузерами через cookies.
// Мінімальна конфігурація у NestJS
import * as session from 'express-session';
import * as RedisStore from 'connect-redis';
app.use(
session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: true,
maxAge: 3600000, // 1 година
},
})
);
Сценарій 2: Single Page Application з планами на мобільні застосунки
Вимоги:
- React/Vue/Angular frontend, REST API backend
- Плани на iOS/Android застосунки через 3–6 місяців
- Очікувана аудиторія: 50000–500000 користувачів
- Необхідність горизонтального масштабування
Рекомендація: Token-Based (JWT) з Refresh Tokens
Обґрунтування:
- JWT токени працюють однаково у веб-браузерах та мобільних застосунках.
- Stateless архітектура дозволяє запускати десятки серверів без синхронізації стану.
- Короткоживучі access tokens (15 хвилин) + довгоживучі refresh tokens (7 днів) забезпечують баланс між безпекою та зручністю.
- Refresh tokens зберігаються у HttpOnly cookies або у захищеному сховищі мобільного застосунку (Keychain на iOS, Keystore на Android).
// Генерація пари токенів
const accessToken = sign({ sub: user.id, roles: user.roles }, JWT_SECRET, {
expiresIn: '15m',
});
const refreshToken = sign({ sub: user.id }, REFRESH_SECRET, {
expiresIn: '7d',
});
// Збереження refresh token у БД для можливості відкликання
await db.refreshTokens.create({
token: hashSync(refreshToken, 10), // Зберігаємо хеш, не сам токен!
userId: user.id,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
});
Сценарій 3: Інтеграція з Google/Facebook для швидкої реєстрації
Вимоги:
- Зменшення тертя при реєстрації (conversion rate)
- Користувачі не хочуть запам'ятовувати ще один пароль
- Потрібен доступ до профілю Google/Facebook для персоналізації
Рекомендація: OAuth 2.0 + OIDC через Passport.js
Обґрунтування:
- Бібліотека Passport.js надає готові стратегії для десятків провайдерів (Google, Facebook, GitHub, Twitter).
- Користувач реєструється за 2 кліки без введення email/пароля.
- Ваш застосунок не зберігає паролі — це зменшує відповідальність за витоки даних.
// Налаштування Google OAuth у NestJS
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: 'https://yourapp.com/auth/google/callback',
scope: ['email', 'profile'],
});
}
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: VerifyCallback
): Promise<any> {
const { id, emails, displayName, photos } = profile;
// Знайти або створити користувача у вашій БД
const user = await this.usersService.findOrCreateGoogleUser({
googleId: id,
email: emails[0].value,
name: displayName,
picture: photos[0].value,
});
done(null, user);
}
}
Сценарій 4: Корпоративна система з десятками внутрішніх сервісів
Вимоги:
- 5000+ співробітників
- 20+ внутрішніх застосунків (CRM, ERP, HR, документообіг)
- Централізоване управління доступом
- Інтеграція з Active Directory або LDAP
Рекомендація: Single Sign-On через Okta, Auth0 або Azure AD
Обґрунтування:
- Співробітник входить один раз вранці через корпоративний логін.
- Адміністратори керують доступом централізовано — звільнення співробітника миттєво блокує доступ до всіх систем.
- Готові інтеграції з популярними enterprise системами (Salesforce, Jira, Confluence, Slack).
- Підтримка багатофакторної автентифікації (SMS, TOTP, push-нотифікації).
Інтерактивні запитання для самоперевірки
Authorization, а якщо токена немає — шукає session ID у cookies. Проте така архітектура ускладнює кодову базу та збільшує поверхню атаки, тому для нових проєктів рекомендується обрати один підхід./me, /userinfo, /user). Лише з появою розширення OpenID Connect з'явився стандартизований id_token (JWT) і /userinfo ендпоінт, що перетворили OAuth 2.0 на повноцінний протокол автентифікації.Зловмисник зможе видавати себе за вас до моменту закінчення терміну дії токена (exp claim). Оскільки JWT токени є stateless, сервер не може відкликати скомпрометований токен — він залишається дійсним до exp. Це одна з найбільших вразливостей token-based автентифікації. Захисні механізми:
- Короткий термін життя access token (5–15 хвилин) — обмежує вікно атаки.
- Refresh tokens у HttpOnly cookies — зменшує ризик викрадення через XSS.
- Прив'язка токена до IP-адреси або User-Agent — токен стає недійсним при зміні контексту.
- Token blacklist у Redis — дозволяє відкликати токен вручну, але нівелює переваги stateless архітектури.
- Моніторинг аномалій — виявлення одночасних сесій з різних країн або незвичної активності.
Категорично ні для веб-застосунків. Refresh token має значно довший термін життя (дні або тижні) і дозволяє отримувати нові access tokens. Якщо зловмисник викраде refresh token через XSS атаку (читання localStorage) або перехоплення HTTPS трафіку (компрометація сертифіката), він матиме довгостроковий доступ до облікового запису. Безпечний підхід:
- Для веб-застосунків: зберігати refresh token у HttpOnly cookie, що недоступне для JavaScript.
- Для мобільних застосунків: зберігати у захищеному сховищі ОС (iOS Keychain, Android Keystore) з шифруванням.
- Для SPA без backend: використовувати BFF pattern (Backend-for-Frontend) — проксі-сервер між frontend та API, що керує токенами.
Ключові висновки
⚖️ Stateful vs Stateless
🔐 OAuth 2.0 для делегованого доступу
id_token.🏢 SSO для корпоративних систем
🎯 Немає універсального рішення
Рекомендовані ресурси для поглибленого вивчення
- RFC 6749 — OAuth 2.0 Authorization Framework: офіційна специфікація протоколу OAuth 2.0, що визначає потоки авторизації, ролі учасників та формати токенів.
- OpenID Connect Core 1.0: специфікація OIDC, що розширює OAuth 2.0 стандартизованою автентифікацією та
id_token. - OWASP Session Management Cheat Sheet: найкращі практики безпечної реалізації серверних сесій, налаштування cookies та захист від атак.
- JWT.io: інтерактивний інструмент для декодування, перевірки та створення JWT токенів з підтримкою різних алгоритмів підпису.
- Passport.js Documentation: бібліотека для Node.js з готовими стратегіями автентифікації (Local, JWT, Google, Facebook, GitHub тощо).
express-session, налаштування Redis як сховища сесій, конфігурацію безпечних cookies та створення Guard для захисту маршрутів. Ви створите повноцінну систему входу, виходу та управління сесіями.