Аутентификация и авторизация во фронтенде
Аутентификация и авторизация — два разных слоя защиты, которые часто путают. Первый отвечает «кто ты», второй — «что тебе можно». Фронтенд взаимодействует с обоими: показывает форму входа, хранит токены, скрывает или показывает UI-элементы. Статья систематизирует механизмы (сессии, JWT, OAuth 2.0, OIDC, PKCE), варианты хранения токенов, модели контроля доступа (RBAC, ABAC) и практики внедрения в Next.js и Nuxt.
Содержание
- Аутентификация vs авторизация
- Сессии через cookies
- JWT: плюсы, минусы и риски
- Access token и Refresh token
- OAuth 2.0 и OpenID Connect
- PKCE: защита мобильных и SPA-приложений
- Где хранить токены: cookies vs localStorage vs memory
- RBAC и ABAC
- Проверка авторизации на фронтенде
- Аутентификация в Next.js и Nuxt
- Чек-лист
- Терминология
- Ключевые тезисы для интервью
- Заключение
- Полезные ссылки
Аутентификация vs авторизация
Эти два термина путают постоянно, но различие критически важно.
Аутентификация (Authentication) — ответ на вопрос «кто ты?». Проверка личности пользователя: логин/пароль, код из SMS, биометрия, SSO.
Авторизация (Authorization) — ответ на вопрос «что тебе можно?». Проверка прав: может ли пользователь удалять посты, видеть чужие данные, заходить в админку.
Аналогия: аутентификация — это проверка паспорта на входе в здание. Авторизация — это пропуск в конкретный этаж.
Фронтенд-разработчик должен понимать оба слоя. Часто разработчик скрывает кнопку «Удалить» в UI и считает, что защита есть. Но если сервер не проверяет права, любой может отправить запрос напрямую.
Сессии через cookies
Классический подход: сервер создаёт сессию, сохраняет её в базе или Redis, и отправляет клиенту session ID в cookie.
Flow
- Пользователь вводит логин/пароль.
- Сервер проверяет учётные данные, создаёт запись сессии.
- Сервер устанавливает cookie
session_id=abc123. - При каждом запросе браузер отправляет cookie.
- Сервер по
session_idнаходит сессию и понимает, кто запрашивает.
Почему cookies удобны
- Браузер управляет отправкой автоматически.
- Можно сделать
HttpOnly— токен недоступен из JavaScript. - Можно отозвать сессию на сервере в любой момент.
Почему cookies требуют защиты
- Cookie автоматически отправляются браузером → риск CSRF.
- Если не
Secure, cookie может утекти по HTTP. - Если не
HttpOnly, XSS может украсть cookie.
Правильная cookie:
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
JWT: плюсы, минусы и риски
JWT (JSON Web Token) — это самодостаточный токен, содержащий claims (утверждения) о пользователе. Он состоит из трёх частей:
header.payload.signature
- Header: алгоритм и тип токена.
- Payload: данные (user_id, role, exp и т.д.).
- Signature: подпись, которая гарантирует неизменность токена.
Плюсы
- Stateless: серверу не нужно хранить сессию.
- Масштабируемость: любой сервис может проверить подпись.
- Подходит для микросервисов.
Минусы
- Нельзя отозвать мгновенно (нужен blacklist или короткий срок жизни).
- Если утекает секретный ключ — утекают все токены.
- Разработчики иногда хранят JWT в
localStorage, что делает их уязвимыми для XSS.
Где не стоит использовать JWT
JWT не заменяют сессии во всех случаях. Если вам нужно:
- мгновенно отзывать доступ;
- хранить много данных о сессии;
- иметь единый источник истины;
лучше использовать классические сессии.
Access token и Refresh token
Чтобы сбаланзировать безопасность и удобство, используют пару токенов:
- Access token — короткоживущий (минуты), используется для доступа к API.
- Refresh token — долгоживущий (дни), используется для получения нового access token.
Flow
- Пользователь логинится.
- Сервер выдаёт access token (15 минут) и refresh token (7 дней).
- Клиент использует access token для API.
- Когда access token истекает, клиент отправляет refresh token и получает новую пару.
- При выходе refresh token аннулируется на сервере.
Refresh token rotation
Каждый раз при обновлении access token выдаётся и новый refresh token. Это защищает от повторного использования украденного refresh token: если злоумышленник использовал старый токен, сервер обнаружит несоответствие и отзовёт всю цепочку.
OAuth 2.0 и OpenID Connect
OAuth 2.0 — протокол авторизации, который позволяет пользователю дать приложению доступ к своим данным на другом сервисе без передачи пароля.
OpenID Connect (OIDC) — надстройка над OAuth 2.0, которая добавляет аутентификацию: приложение получает информацию о пользователе (id_token).
Участники
| Участник | Роль |
|---|---|
| Resource Owner | Пользователь, владелец данных |
| Client | Ваше приложение |
| Authorization Server | Сервер, который выдаёт токены (Google, Auth0, Keycloak) |
| Resource Server | Сервер, где хранятся данные пользователя (API Google) |
Authorization Code Flow
- Пользователь нажимает «Войти через Google».
- Браузер перенаправляется на Authorization Server.
- Пользователь входит и разрешает доступ.
- Authorization Server перенаправляет обратно с authorization code.
- Ваш backend обменивает code на access token и id token.
- Ваше приложение создаёт сессию пользователя.
Важно: client_secret никогда не должен храниться на фронтенде. Для SPA и мобильных приложений используется PKCE.
PKCE: защита мобильных и SPA-приложений
PKCE (Proof Key for Code Exchange) — расширение OAuth 2.0, которое защищает authorization code flow от перехвата authorization code.
Зачем нужен
В классическом flow client_secret хранится на сервере приложения. Но у SPA и мобильных приложений нет надёжного способа хранить секрет. PKCE заменяет client_secret динамическим кодом, генерируемым для каждого входа.
Flow
- Клиент генерирует случайный
code_verifier. - Из него вычисляет
code_challenge(хэш). - Отправляет
code_challengeна Authorization Server. - Получает authorization code.
- Обменивает code на токены, отправляя
code_verifier. - Authorization Server проверяет, что verifier соответствует challenge.
Если злоумышленник перехватит authorization code, он не сможет обменять его на токены без code_verifier.
Где хранить токены: cookies vs localStorage vs memory
Это один из самых горячих споров во фронтенд-безопасности.
localStorage / sessionStorage
localStorage.setItem('token', accessToken);
Плюсы:
- Просто реализовать.
- Работает с любыми API, включая cross-origin.
Минусы:
- Доступен из JavaScript → XSS крадёт токен.
- Автоматически не отправляется с запросами → нужен код.
- Не работает при SSR гидратации без дополнительных ухищрений.
HttpOnly cookie
Set-Cookie: access_token=abc; HttpOnly; Secure; SameSite=Lax
Плюсы:
- Недоступен из JavaScript → защита от XSS.
- Автоматически отправляется браузером.
Минусы:
- Требует CSRF-защиты.
- Сложнее масштабировать cross-origin.
- Менее гибок для SPA с отдельным API-доменом.
Memory (in-memory)
Токен хранится в переменной приложения, например, в React Context или Vue composable.
Плюсы:
- Максимальная защита от XSS в рамках одной сессии.
- Не доступен из
localStorage.
Минусы:
- Токен теряется при обновлении страницы.
- Требует механизма восстановления (обычно refresh token в HttpOnly cookie).
Рекомендация
Для большинства веб-приложений:
- Refresh token — в HttpOnly cookie с
SameSite=Strict. - Access token — в памяти или в HttpOnly cookie с коротким сроком жизни.
- localStorage — избегать для sensitive данных.
RBAC и ABAC
RBAC (Role-Based Access Control)
Доступ определяется ролями: admin, editor, user, guest.
if (user.role === 'admin') {
// разрешить удаление пользователя
}
Плюсы: простота, понятность, легко аудировать.
Минусы: грубость. Если ролей много, начинается «role explosion».
ABAC (Attribute-Based Access Control)
Доступ определяется атрибутами пользователя, ресурса и контекста.
const canEdit =
user.department === document.department &&
user.isActive &&
document.status !== 'archived';
Плюсы: гибкость, точность.
Минусы: сложность, сложнее тестировать и аудировать.
Что использовать
Для большинства приложений начните с RBAC. Переходите на ABAC, когда правила становятся слишком сложными для ролей.
Проверка авторизации на фронтенде
Фронтенд не может быть единственным местом проверки прав. Но он играет важную роль в UX:
- Показывать или скрывать элементы управления.
- Блокировать навигацию к защищённым страницам.
- Показывать fallback вместо полного доступа.
Паттерн: авторизация на сервере + UI-адаптация на клиенте
// Серверный компонент (Next.js)
export default async function AdminPage() {
const session = await auth();
if (session?.user?.role !== 'admin') {
redirect('/');
}
return <AdminDashboard />;
}
// Клиентский компонент
function ActionButton({ user }) {
if (user.role !== 'admin') return null;
return <button onClick={deleteUser}>Delete user</button>;
}
Ключевой принцип: сервер всегда проверяет права, клиент только адаптирует интерфейс.
Аутентификация в Next.js и Nuxt
Next.js
Наиболее распространённые решения:
- Auth.js / NextAuth.js — поддержка OAuth, credentials, magic links.
- Clerk — managed-решение с готовыми компонентами.
- Lucia — лёгкая библиотека для кастомной аутентификации.
Auth.js:
// lib/auth.ts
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [GitHub],
cookies: {
sessionToken: {
name: 'session-token',
options: {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
},
},
},
});
Nuxt
- Nuxt Auth — модуль для аутентификации.
- Auth.js — можно использовать через @auth/nuxt.
- Nuxt Supabase — для проектов на Supabase.
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@sidebase/nuxt-auth'],
auth: {
baseURL: process.env.AUTH_ORIGIN,
provider: {
type: 'authjs',
},
},
});
Чек-лист
Аутентификация
- Использовать HTTPS везде.
- Хранить пароли только в виде хэша (bcrypt, Argon2, scrypt).
- Использовать HttpOnly, Secure, SameSite cookies для сессий.
- Ограничивать срок жизни сессий и токенов.
- Реализовать logout с удалением/аннулированием токена на сервере.
Авторизация
- Проверять права на сервере в каждом API endpoint / Server Action.
- Проверять владение ресурсом (пользователь может удалять только свои посты).
- Не полагаться только на UI для скрытия действий.
- Логировать попытки несанкционированного доступа.
OAuth / SSO
- Использовать PKCE для SPA и мобильных приложений.
- Хранить client_secret только на сервере.
- Проверять state-параметр для защиты от CSRF в OAuth.
- Валидировать id_token (подпись, issuer, audience, expiration).
Терминология
| Термин | Значение |
|---|---|
| Authentication | Проверка личности пользователя |
| Authorization | Проверка прав доступа |
| Session | Состояние взаимодействия пользователя с приложением |
| JWT | JSON Web Token — самодостаточный подписанный токен |
| Access token | Короткоживущий токен для доступа к API |
| Refresh token | Долгоживущий токен для обновления access token |
| OAuth 2.0 | Протокол делегированного доступа |
| OIDC | OpenID Connect — аутентификация поверх OAuth 2.0 |
| PKCE | Proof Key for Code Exchange — защита OAuth для SPA |
| RBAC | Role-Based Access Control — управление доступом по ролям |
| ABAC | Attribute-Based Access Control — управление доступом по атрибутам |
| HttpOnly | Cookie, недоступная из JavaScript |
| Token rotation | Выдача нового refresh token при каждом обновлении |
Ключевые тезисы для интервью
- Аутентификация («кто ты») и авторизация («что тебе можно») — два слоя; сервер проверяет оба, клиент лишь адаптирует UI.
- Сессии через cookies дают мгновенный revoke и серверный state, но требуют
HttpOnly,Secure,SameSiteи CSRF-защиты; JWT — stateless, удобен для микросервисов, но плохо отзывается. - Пара access (короткоживущий, минуты) + refresh (дни) token снижает риск утечки; rotation защищает от повторного использования украденного refresh.
- OAuth 2.0 — делегированный доступ, OIDC — аутентификация поверх него; PKCE обязателен для SPA и мобильных приложений, заменяя
client_secretдинамическимcode_verifier;state-параметр защищает от CSRF. - Refresh token — в HttpOnly cookie с
SameSite=Strict, access — в памяти;localStorage— только для не-sensitive данных, так как уязвим к XSS. - RBAC (роли) прост и масштабируется для большинства задач; ABAC (атрибуты) гибче, но сложнее аудировать — переход оправдан, когда ролей слишком много или нужна контекстная логика.
Заключение
Аутентификация и авторизация — это два разных слоя, и оба должны проверяться на сервере. Фронтенд может улучшить UX, но не может быть единственной защитой. Выбор архитектуры — сессии vs JWT, cookies vs localStorage, RBAC vs ABAC — это компромиссы между простотой, безопасностью и масштабируемостью. Для SOC2 и CASA аудиторы смотрят на хранение credentials, многоуровневую авторизацию, механизмы отзыва сессий и логи доступа. Понимание этих механизмов помогает не только писать безопасный код, но и уверенно отвечать на вопросы аудиторов.