Навигация

Аутентификация и авторизация во фронтенде

Аутентификация и авторизация — два разных слоя защиты, которые часто путают. Первый отвечает «кто ты», второй — «что тебе можно». Фронтенд взаимодействует с обоими: показывает форму входа, хранит токены, скрывает или показывает UI-элементы. Статья систематизирует механизмы (сессии, JWT, OAuth 2.0, OIDC, PKCE), варианты хранения токенов, модели контроля доступа (RBAC, ABAC) и практики внедрения в Next.js и Nuxt.

Содержание

  1. Аутентификация vs авторизация
  2. Сессии через cookies
  3. JWT: плюсы, минусы и риски
  4. Access token и Refresh token
  5. OAuth 2.0 и OpenID Connect
  6. PKCE: защита мобильных и SPA-приложений
  7. Где хранить токены: cookies vs localStorage vs memory
  8. RBAC и ABAC
  9. Проверка авторизации на фронтенде
  10. Аутентификация в Next.js и Nuxt
  11. Чек-лист
  12. Терминология
  13. Ключевые тезисы для интервью
  14. Заключение
  15. Полезные ссылки

Аутентификация vs авторизация

Эти два термина путают постоянно, но различие критически важно.

Аутентификация (Authentication) — ответ на вопрос «кто ты?». Проверка личности пользователя: логин/пароль, код из SMS, биометрия, SSO.

Авторизация (Authorization) — ответ на вопрос «что тебе можно?». Проверка прав: может ли пользователь удалять посты, видеть чужие данные, заходить в админку.

Аналогия: аутентификация — это проверка паспорта на входе в здание. Авторизация — это пропуск в конкретный этаж.

Фронтенд-разработчик должен понимать оба слоя. Часто разработчик скрывает кнопку «Удалить» в UI и считает, что защита есть. Но если сервер не проверяет права, любой может отправить запрос напрямую.


Сессии через cookies

Классический подход: сервер создаёт сессию, сохраняет её в базе или Redis, и отправляет клиенту session ID в cookie.

Flow

  1. Пользователь вводит логин/пароль.
  2. Сервер проверяет учётные данные, создаёт запись сессии.
  3. Сервер устанавливает cookie session_id=abc123.
  4. При каждом запросе браузер отправляет cookie.
  5. Сервер по 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

  1. Пользователь логинится.
  2. Сервер выдаёт access token (15 минут) и refresh token (7 дней).
  3. Клиент использует access token для API.
  4. Когда access token истекает, клиент отправляет refresh token и получает новую пару.
  5. При выходе 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

  1. Пользователь нажимает «Войти через Google».
  2. Браузер перенаправляется на Authorization Server.
  3. Пользователь входит и разрешает доступ.
  4. Authorization Server перенаправляет обратно с authorization code.
  5. Ваш backend обменивает code на access token и id token.
  6. Ваше приложение создаёт сессию пользователя.

Важно: 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

  1. Клиент генерирует случайный code_verifier.
  2. Из него вычисляет code_challenge (хэш).
  3. Отправляет code_challenge на Authorization Server.
  4. Получает authorization code.
  5. Обменивает code на токены, отправляя code_verifier.
  6. 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 гидратации без дополнительных ухищрений.
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Состояние взаимодействия пользователя с приложением
JWTJSON Web Token — самодостаточный подписанный токен
Access tokenКороткоживущий токен для доступа к API
Refresh tokenДолгоживущий токен для обновления access token
OAuth 2.0Протокол делегированного доступа
OIDCOpenID Connect — аутентификация поверх OAuth 2.0
PKCEProof Key for Code Exchange — защита OAuth для SPA
RBACRole-Based Access Control — управление доступом по ролям
ABACAttribute-Based Access Control — управление доступом по атрибутам
HttpOnlyCookie, недоступная из 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, многоуровневую авторизацию, механизмы отзыва сессий и логи доступа. Понимание этих механизмов помогает не только писать безопасный код, но и уверенно отвечать на вопросы аудиторов.

Полезные ссылки