Skip to content

Аутентификация и RBAC

JWT-аутентификация

Planner использует JSON Web Token для stateless-аутентификации:

  • Access token: время жизни 15 минут, отправляется в заголовке Authorization: Bearer <token>
  • Refresh token: время жизни 7 дней, используется для получения новых access токенов

Процесс аутентификации

  1. POST /api/auth/register — создать аккаунт (firstName, lastName, email, password)
  2. POST /api/auth/verify-email — подтвердить email по токену из письма
  3. POST /api/auth/login — получить access + refresh токены (требуется подтверждённый email)
  4. Все последующие запросы включают Authorization: Bearer <access_token>
  5. POST /api/auth/refresh — обменять refresh токен на новый access токен
  6. POST /api/auth/logout — отозвать все refresh токены

Подтверждение email

Новые пользователи должны подтвердить email перед первым входом:

  • POST /api/auth/register — создаёт пользователя, отправляет письмо с подтверждением (НЕ возвращает токены)
  • POST /api/auth/verify-email — проверяет токен, помечает email как подтверждённый
  • POST /api/auth/resend-verification — повторная отправка письма (лимит: 3/мин)
  • POST /api/auth/login и POST /api/auth/refresh — возвращают EMAIL_NOT_VERIFIED, если email не подтверждён
  • Существующие пользователи получают emailVerifiedAt через seed (prisma/seed.ts)

Ограничение запросов (Rate Limiting)

Защита от brute-force атак:

ЭндпоинтЛимит
Регистрация5 запросов/минуту
Вход10 запросов/минуту
Обновление токена20 запросов/минуту
Забыли пароль3 запроса/минуту
Сброс пароля5 запросов/минуту
Подтверждение email5 запросов/минуту
Повторная отправка3 запроса/минуту

Глобальный лимит: 60 запросов/минуту (хранится в Redis в продакшне).

Блокировка аккаунта

После 5 неудачных попыток входа аккаунт блокируется на 15 минут через Redis (ключ lockout:<email>). Общие сообщения об ошибках (INVALID_CREDENTIALS) предотвращают перебор пользователей.

Требования к паролю

  • Минимум 8 символов, максимум 128
  • Требуется: заглавная буква, строчная буква, цифра, спецсимвол
  • Хешируется bcrypt (cost factor 10)
  • Смена пароля инвалидирует все refresh токены
  • Сброс пароля также инвалидирует все refresh токены

Ротация refresh токенов

  • Refresh токены хранятся как SHA-256 хеш (не в открытом виде)
  • При каждом обновлении: старый токен удаляется, выдаётся новый (ротация)
  • Обнаружение повторного использования: если ротированный токен предъявлен снова — все токены пользователя удаляются, логируется предупреждение
  • POST /api/auth/logout удаляет все refresh токены пользователя

Social Auth

Помимо email/password, поддерживается вход через внешних провайдеров:

ПровайдерПутьОписание
Яндекс ID/api/auth/social/yandexВход через Яндекс
VK ID/api/auth/social/vkВход через VK

Процесс: фронтенд открывает popup-окно на URL провайдера → после успешной аутентификации провайдер редиректит на callback с токенами в URL hash → popup отправляет токены родительскому окну через postMessage → родительское окно закрывает popup и сохраняет токены.

Payload токена

json
{
  "sub": "uuid",
  "email": "user@example.com",
  "jti": "random-uuid",
  "iat": 1234567890,
  "exp": 1234568790
}

Управление доступом на основе ролей (RBAC)

Система RBAC предоставляет детальные разрешения на нескольких уровнях.

Области разрешений

ОбластьУровеньПример контроллера
GLOBALСистемныйПанель администратора, управление пользователями
ORGANIZATIONНа организациюНастройки организации, управление участниками
TEAMНа командуНастройки команды, управление участниками
PROJECTНа проектНастройки проекта, управление задачами
TASKНа задачуCRUD задач (контролируется областью проекта)
CHATУровень чатаУправление беседами

Доступные разрешения (30+)

Организация (org:): create, view, edit, delete, members.view, members.invite, members.remove, members.roles, settings.view, settings.edit

Команда (team:): create, view, edit, delete, members.view, members.add, members.remove, members.roles, settings.view, settings.edit

Проект (project:): create, view, edit, delete, members.view, members.add, members.remove, task.create, task.view, task.edit, task.delete, task.assign, settings.view, settings.edit

Встроенные роли

РольУровеньОписание
OWNERОрг/Команда/ПроектПолный контроль, может удалять
ADMINОрг/Команда/ПроектУправление пользователями и настройками
MANAGERОрг/Команда/ПроектОперационное управление
MEMBERОрг/Команда/ПроектБазовый доступ

Пользовательские роли могут быть созданы с любой комбинацией разрешений.

Вычисление разрешений

Разрешения вычисляются из всех ролей пользователя:

effective_permissions = union(
  global_roles.permissions,
  org_roles.permissions,
  team_roles.permissions,
  project_roles.permissions
)

Проверка разрешений в коде

Бэкенд (NestJS):

typescript
@Permissions(['task.create', 'task.edit'])
@Controller('/projects/:projectId/tasks')

Фронтенд (Pinia store):

typescript
const auth = useAuthStore()
// auth.hasPermission — computed, автоматически разворачивается в шаблоне
if (auth.hasPermission('task.create')) { ... }

Группы пользователей

Группы пользователей позволяют массово назначать роли. Создайте группу с набором разрешений, затем добавьте пользователей в группу. Все участники группы наследуют разрешения группы.