Docs Access System
1. Summary
Goal: Многоуровневая система контроля доступа к документации. Позволяет делиться документацией с внешними пользователями без раскрытия конфиденциальной информации.
User Value: Owner может создать временную ссылку для партнёра/инвестора/коллеги с ограниченным доступом — без регистрации и паролей.
2. Business Logic
Access Levels
- Viewer
- Admin
- Owner
Получение доступа: Invite Link
Что видит: Только страницы с visibility: viewer (или без указания)
Срок действия: Определяется при создании ссылки (от 1 часа до 30 дней, дефолт 1 час)
Use case: Партнёры, инвесторы, внешние разработчики
Получение доступа: Логин в админ-панели
Что видит: Viewer контент + API документация
Срок действия: 4 часа (localStorage), конфигурируется через DOCS_ADMIN_TTL_MINUTES
Use case: Разработчики команды
Получение доступа: 2FA код (TOTP)
Что видит: Весь контент, включая visibility: owner
Срок действия: 4 часа (cookie), конфигурируется через DOCS_OWNER_TTL_MINUTES
Use case: Архитектурные решения, секретная документация
Invite Links
Механизм создания временных ссылок для viewer доступа:
1. Генерация (Admin Panel)
- Owner создаёт ссылку с параметрами: TTL, заметка, лимит использований
- Генерируется stateless JWT-like токен с HMAC подписью
2. Активация (User)
- Пользователь переходит по ссылке
- Backend валидирует подпись и срок действия
- Устанавливается
docs_viewercookie
Токен содержит все данные (expiresAt, usageLimit, note). Не требует хранения в БД — только подпись гарантирует целостность.
3. Usage Tracking
| Хранилище | Данные | TTL |
|---|---|---|
| Redis | Счётчик использований | = срок жизни токена |
| PostgreSQL | Ничего | - |
Hidden Activation
Секретные комбинации для активации режимов на docs-сайте:
| Режим | Комбинация | Результат |
|---|---|---|
| Admin | 5 кликов за 3 сек | Модалка подтверждения |
| Owner | 10 кликов за 5 сек | Модалка 2FA |
Нет. Это только UX — скрываем от обычных пользователей. Реальная защита:
- Admin: требует предварительный логин в админку (docs_session cookie)
- Owner: требует 2FA код (TOTP)
Edge Cases
Что происходит при ошибках:
| Ситуация | Поведение |
|---|---|
| Ссылка просрочена | HTML страница "Ссылка недействительна" |
| Лимит исчерпан | HTML страница "Лимит исчерпан" |
| Redis недоступен | Graceful degradation — лимит не проверяется |
| Неверный 2FA код | "Invalid code" в модалке |
Backend Error Responses
| Эндпоинт | Ошибка | HTTP | Формат |
|---|---|---|---|
/docs/invite/* | Invalid token | 400 | HTML |
/docs/invite/* | Usage limit | 400 | HTML |
/docs/verify-owner | No session | 401 | JSON |
/docs/verify-owner | Invalid 2FA | 401 | JSON |
3. ADR (Architectural Decisions)
Почему Stateless токены?
Проблема: Нужны временные ссылки без регистрации пользователей.
Решение: JWT-like токены с HMAC-SHA256 подписью. Вся информация в токене.
Альтернативы (отклонены):
- Хранение в БД — overhead на запись/чтение при каждом переходе
- UUID ссылки — требуют lookup в БД
Последствия:
- Нельзя отозвать конкретную ссылку (только сменой JWT_SECRET)
- Нет истории созданных ссылок
- Минимальная нагрузка на БД
Почему Redis для счётчика?
Проблема: Нужно ограничить количество использований ссылки.
Решение: Атомарный INCR в Redis с TTL.
Альтернативы (отклонены):
- PostgreSQL counter — блокировки при concurrent requests
- In-memory counter — не работает при нескольких инстансах
Последствия: Зависимость от Redis, но graceful degradation при недоступности.
Почему Traefik ForwardAuth?
Проблема: Документация на отдельном домене (docs.goloot.online), нужна проверка доступа.
Решение: Traefik вызывает /admin-auth/verify-docs перед каждым запросом.
Альтернативы (отклонены):
- Client-side проверка — легко обойти
- Nginx auth_request — менее гибко чем Traefik
Последствия: Каждый запрос к docs проходит через backend проверку.
4. Architecture
Authentication Flow
Invite Link Flow
Key Components
| Компонент | Путь | Описание |
|---|---|---|
| DocsVisibilityService | backend/src/domains/admin/services/docs-visibility.service.ts | Генерация/валидация токенов |
| DocsVisibilityRoutes | backend/src/domains/admin/routes/docs-visibility.routes.ts | API эндпоинты |
| AdminAuthRoutes | backend/src/domains/admin/routes/admin-auth.routes.ts | ForwardAuth verify-docs |
| VisibilityContext | docs-site/src/contexts/VisibilityContext.tsx | React контекст уровня доступа |
| TwoFactorModal | docs-site/src/components/TwoFactorModal/ | UI для 2FA |
5. Cookies & Storage
Cookie Configuration
| Cookie | Имя | Domain | Max-Age | HttpOnly | Secure |
|---|---|---|---|---|---|
| Viewer | docs_viewer | .goloot.online | = TTL токена | Yes | Yes |
| Admin Session | docs_session | .goloot.online | 8 часов | Yes | Yes |
| Owner | docs_owner | .goloot.online | 4 часа (ENV) | Yes | Yes |
Client Storage
| Данные | Хранилище | TTL |
|---|---|---|
| Admin mode flag | localStorage | 4 часа (ENV, проверяется при загрузке) |
| Click counters | React state | Сессия |
ENV Configuration
TTL для owner и admin сессий конфигурируется через переменные окружения:
| Переменная | Описание | Дефолт |
|---|---|---|
DOCS_OWNER_TTL_MINUTES | TTL owner cookie | 240 (4 часа) |
DOCS_ADMIN_TTL_MINUTES | TTL admin localStorage | 240 (4 часа) |
Для разработки можно установить большие значения (например, 99999) чтобы не переавторизовываться.
6. API Endpoints
- Invite Links
- Session
- Traefik
| Метод | Эндпоинт | Описание | Auth |
|---|---|---|---|
| POST | /docs/invite | Создать invite ссылку | JWT (Admin) |
| GET | /docs/invite/* | Активировать ссылку | None |
| Метод | Эндпоинт | Описание | Auth |
|---|---|---|---|
| GET | /docs/session | Проверить текущий уровень | Cookie |
| POST | /docs/verify-owner | Активировать owner (2FA) | Cookie |
| Метод | Эндпоинт | Описание | Auth |
|---|---|---|---|
| GET | /admin-auth/verify-docs | ForwardAuth endpoint | Cookie |
Этот эндпоинт вызывается Traefik'ом автоматически перед проксированием на docs.goloot.online. Возвращает 200 (OK) или 302 (Redirect to login).
7. Security Considerations
Token Security
- HMAC-SHA256 подпись предотвращает подделку
- Expiration встроен в payload
- Смена
JWT_SECRETинвалидирует ВСЕ токены
Cookie Security
- HttpOnly — защита от XSS
- Secure — только HTTPS
- SameSite=Lax — защита от CSRF
- Domain=.goloot.online — работает на всех поддоменах
Rate Limiting
Стандартный rate limit сервера применяется ко всем эндпоинтам.
Отзыв доступа
Invite-токены stateless — нет записи в БД, поэтому отозвать конкретный токен невозможно.
| Способ | Что происходит | Последствия |
|---|---|---|
| Ждать TTL | Viewer cookie истекает автоматически | Штатный способ. Дефолт TTL — 1 час |
Сменить JWT_SECRET | Все токены и cookies инвалидируются мгновенно | "Ядерная кнопка": ломает ВСЕ сессии (viewer, admin, owner). После смены — перелогиниться в админке |
Смена JWT_SECRET в Dokploy → Backend → Environment → Redeploy мгновенно инвалидирует:
- Все
docs_viewercookies (invite-сессии) - Все
docs_sessioncookies (admin-сессии) - Все
docs_ownercookies (owner-сессии) - Все JWT-токены админки
Единственный пользователь JWT — админка (ты), поэтому последствия минимальны: просто перелогиниться.
При создании invite-ссылок ставь минимальный TTL (дефолт — 1 час). Так даже при утечке ссылки окно уязвимости минимальное.
8. Related
- Docs Deployment — CI/CD для docs site, visibility plugin
- User Management — управление пользователями в админке
- Analytics — дашборды и метрики