249 lines
7.3 KiB
Markdown
249 lines
7.3 KiB
Markdown
## Документация по аутентификации (Frontend)
|
||
|
||
### Обзор
|
||
|
||
JWT-аутентификация. Публичные и админские эндпоинты:
|
||
|
||
- POST `/api/auth/signin` — вход и выдача JWT + refreshToken
|
||
- POST `/api/auth/refresh` — обновление пары токенов (ротация)
|
||
- POST `/api/auth/logout` — выход (инвалидация refreshToken)
|
||
- POST `/api/admin/users` — создать пользователя (только `ROLE_ADMIN`)
|
||
|
||
Все запросы и ответы — JSON (`Content-Type: application/json`).
|
||
|
||
### Базовый URL
|
||
|
||
- Прод: укажите боевой домен/порт
|
||
- Dev локально: `https://api.konturai.kz`
|
||
|
||
### Переменные окружения (frontend)
|
||
|
||
- REACT_APP_API_URL или NEXT_PUBLIC_API_URL: базовый URL API
|
||
- Храните токен безопасно (HttpOnly cookie предпочтительно; если localStorage — учитывайте XSS риски)
|
||
|
||
## Создание пользователя (Admin)
|
||
|
||
### POST /api/admin/users
|
||
|
||
Требует: `Authorization: Bearer <admin_access_token>` и роль `ROLE_ADMIN`.
|
||
|
||
Запрос:
|
||
|
||
```json
|
||
{
|
||
"email": "user@example.com",
|
||
"password": "StrongPass123!"
|
||
}
|
||
```
|
||
|
||
Успех:
|
||
|
||
- 200 OK, пустой ответ
|
||
|
||
Ошибки:
|
||
|
||
- 400 Bad Request — невалидный email/пароль
|
||
- 401/403 — нет прав администратора
|
||
- 409 Conflict — email уже зарегистрирован
|
||
|
||
Пример (fetch):
|
||
|
||
```js
|
||
await fetch(`${API_URL}/api/admin/users`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
Authorization: `Bearer ${adminAccessToken}`
|
||
},
|
||
body: JSON.stringify({ email, password })
|
||
});
|
||
```
|
||
|
||
## Вход
|
||
|
||
### POST /api/auth/signin
|
||
|
||
Запрос:
|
||
|
||
```json
|
||
{
|
||
"email": "user@example.com",
|
||
"password": "StrongPass123!"
|
||
}
|
||
```
|
||
|
||
Успех:
|
||
|
||
- 200 OK
|
||
|
||
```json
|
||
{
|
||
"accessToken": "<JWT>",
|
||
"tokenType": "Bearer",
|
||
"refreshToken": "<refresh-token>"
|
||
}
|
||
```
|
||
|
||
Ошибки:
|
||
|
||
- 400/401 — неверные учетные данные
|
||
|
||
Пример (fetch):
|
||
|
||
```js
|
||
const res = await fetch(`${API_URL}/api/auth/signin`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ email, password })
|
||
});
|
||
const { accessToken, refreshToken } = await res.json();
|
||
// Сохраните токен и используйте в Authorization заголовке
|
||
```
|
||
|
||
## Обновление токена (Refresh Token)
|
||
|
||
### Общая схема
|
||
|
||
- При успешном входе фронт получает пару токенов: `accessToken` (короткоживущий) и `refreshToken` (длинноживущий).
|
||
- `accessToken` используется в `Authorization: Bearer <JWT>`.
|
||
- Когда `accessToken` истекает (HTTP 401), фронт вызывает `/api/auth/refresh` с `refreshToken`, получает новую пару токенов (ротация) и повторяет запрос.
|
||
|
||
### Рекомендации по хранению
|
||
|
||
- `refreshToken` предпочтительно хранить в HttpOnly Secure SameSite cookie (сервер ставит Set-Cookie).
|
||
- Альтернатива (менее безопасная): хранить в памяти/secure storage и передавать в теле запроса.
|
||
|
||
### POST /api/auth/refresh
|
||
|
||
Запрос (вариант с телом):
|
||
|
||
```json
|
||
{
|
||
"refreshToken": "<refresh-token>"
|
||
}
|
||
```
|
||
|
||
Успех:
|
||
|
||
- 200 OK
|
||
|
||
```json
|
||
{
|
||
"accessToken": "<new-jwt>",
|
||
"tokenType": "Bearer",
|
||
"refreshToken": "<new-refresh-token>"
|
||
}
|
||
```
|
||
|
||
Ошибки:
|
||
|
||
- 400 — отсутствует/некорректный refreshToken
|
||
- 401 — просрочен/отозван/невалиден
|
||
|
||
Пример (fetch, с телом):
|
||
|
||
```js
|
||
const res = await fetch(`${API_URL}/api/auth/refresh`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ refreshToken })
|
||
});
|
||
if (!res.ok) throw new Error('Refresh failed');
|
||
const { accessToken: newAccess, refreshToken: newRefresh } = await res.json();
|
||
```
|
||
|
||
Пример (cookie-стратегия):
|
||
|
||
```js
|
||
const res = await fetch(`${API_URL}/api/auth/refresh`, {
|
||
method: 'POST',
|
||
credentials: 'include'
|
||
});
|
||
const { accessToken } = await res.json();
|
||
```
|
||
|
||
### Ротация refreshToken
|
||
|
||
- При каждом refresh возвращайте новый `refreshToken` и инвалидируйте старый.
|
||
- На фронте заменяйте сохранённый refreshToken на новый.
|
||
|
||
### TTL (рекомендации)
|
||
|
||
- accessToken: 5–15 минут
|
||
- refreshToken: 7–30 дней
|
||
|
||
## Logout
|
||
|
||
- Если refreshToken хранится в cookie: `POST /api/auth/logout` — сервер чистит cookie и отмечает refreshToken как отозванный.
|
||
- Если в хранилище фронта — удалите локальные токены и по возможности вызовите `logout` для аннулирования на бэке.
|
||
|
||
## Авторизация последующих запросов
|
||
|
||
Передавайте JWT в заголовке:
|
||
|
||
```
|
||
Authorization: Bearer <JWT>
|
||
```
|
||
|
||
Пример защищенного вызова:
|
||
|
||
```js
|
||
await fetch(`${API_URL}/api/private/profile`, {
|
||
headers: { Authorization: `Bearer ${token}` }
|
||
});
|
||
```
|
||
|
||
## Формат ошибок (пример)
|
||
|
||
```json
|
||
{
|
||
"timestamp": "2025-09-11T12:34:56Z",
|
||
"status": 409,
|
||
"error": "Conflict",
|
||
"message": "Email already registered",
|
||
"path": "/api/auth/signup"
|
||
}
|
||
```
|
||
|
||
## Валидация на фронте
|
||
|
||
- Email: RFC-проверка и нормализация в lowercase
|
||
- Пароль: минимум 8 символов, цифра, буква, спецсимвол
|
||
- Обработайте статусы 400/401/409 и показывайте человекочитаемые сообщения
|
||
|
||
## Хранение токена
|
||
|
||
- Предпочтительно: HttpOnly Secure cookie, получаемое от бэкенда
|
||
- Альтернатива: `localStorage`/`sessionStorage` (учтите XSS; не вставляйте токен в DOM)
|
||
|
||
## Примеры cURL
|
||
|
||
Создание пользователя (admin):
|
||
|
||
```bash
|
||
curl -X POST "$API_URL/api/admin/users" \
|
||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"email":"user@example.com","password":"StrongPass123!"}'
|
||
```
|
||
|
||
Вход:
|
||
|
||
```bash
|
||
curl -X POST "$API_URL/api/auth/signin" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"email":"user@example.com","password":"StrongPass123!"}'
|
||
```
|
||
|
||
## Заметки по безопасности
|
||
|
||
- Не логируйте пароли и JWT
|
||
- Реализуйте logout (инвалидация на клиенте или список отозванных токенов на бэке, если нужно)
|
||
- Рекомендуется троттлинг/капча для /signin и создания пользователя
|
||
|
||
## Изменения в будущем
|
||
|
||
- Ротация refresh-токенов реализована; можно добавить список отозванных токенов
|
||
- Подтверждение email
|
||
- Сброс пароля через почту
|