diff --git a/docs/admin-users-api.md b/docs/admin-users-api.md new file mode 100644 index 0000000..9548119 --- /dev/null +++ b/docs/admin-users-api.md @@ -0,0 +1,104 @@ +## Admin Users API + +Требуется `Authorization: Bearer ` и роль `ROLE_ADMIN`. + +### Создать пользователя + +POST `/api/admin/users` + +Body: + +```json +{ "email": "user@example.com", "password": "StrongPass123!" } +``` + +Ответ: `200 OK` (пусто) + +Ошибки: 400 (валидация), 401/403 (нет прав), 409 (email занят) + +### Назначить роли пользователю + +POST `/api/admin/users/roles` + +Body: + +```json +{ "email": "user@example.com", "roles": ["ROLE_USER", "ROLE_ADMIN"] } +``` + +Ответ: `200 OK` (пусто) + +Ошибки: 400 (невалидная роль/пользователь не найден), 401/403 + +### Список доступных ролей + +GET `/api/admin/roles` + +Ответ: + +```json +["ROLE_ADMIN", "ROLE_USER"] +``` + +Ошибки: 401/403 + +### Список пользователей (пагинация) + +GET `/api/admin/users?page=0&size=20` + +Ответ (`Page`): + +```json +{ + "content": [ + { "id": 1, "email": "root@konturai.local", "roles": "ROLE_ADMIN,ROLE_USER" } + ], + "pageable": { "pageNumber": 0, "pageSize": 20, ... }, + "totalElements": 1, + "totalPages": 1, + "last": true, + "size": 20, + "number": 0, + "sort": { ... }, + "first": true, + "numberOfElements": 1, + "empty": false +} +``` + +Ошибки: 401/403 + +### Обновить пользователя + +PUT `/api/admin/users/update` + +Body (любые поля опциональны кроме `id`): + +```json +{ + "id": 1, + "email": "new@mail.com", + "password": "NewPass123!", + "roles": ["ROLE_USER"] +} +``` + +Ответ: `200 OK` (пусто) + +Ошибки: 400 (невалидные данные/роль), 401/403, 404 (пользователь не найден) + +### Удалить пользователя + +DELETE `/api/admin/users?id=1` + +Ответ: `200 OK` (пусто) + +Ошибки: 401/403 + +### Формат ошибок + +Все ошибки возвращают JSON: + +```json +{ "message": "описание ошибки" } +``` diff --git a/docs/admin-users.md b/docs/admin-users.md new file mode 100644 index 0000000..bf9d020 --- /dev/null +++ b/docs/admin-users.md @@ -0,0 +1,43 @@ +## Admin: Пользователи и роли + +Требуется роль `ROLE_ADMIN` и заголовок `Authorization: Bearer `. + +### Создать пользователя + +POST `/api/admin/users` + +Body: + +```json +{ "email": "user@example.com", "password": "StrongPass123!" } +``` + +Ответ: `200 OK` (пусто) + +Ошибки: 400, 401/403, 409 + +### Назначить роли пользователю + +POST `/api/admin/users/roles` + +Body: + +```json +{ "email": "user@example.com", "roles": ["ROLE_USER", "ROLE_ADMIN"] } +``` + +Ответ: `200 OK` (пусто) + +Ошибки: 400 (invalid role / user not found), 401/403 + +### Список доступных ролей + +GET `/api/admin/roles` + +Ответ: + +```json +["ROLE_ADMIN", "ROLE_USER"] +``` + +Ошибки: 401/403 diff --git a/docs/auth-api.md b/docs/auth-api.md new file mode 100644 index 0000000..bdc4844 --- /dev/null +++ b/docs/auth-api.md @@ -0,0 +1,248 @@ +## Документация по аутентификации (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 локально: `http://localhost:8080` + +### Переменные окружения (frontend) + +- REACT_APP_API_URL или NEXT_PUBLIC_API_URL: базовый URL API +- Храните токен безопасно (HttpOnly cookie предпочтительно; если localStorage — учитывайте XSS риски) + +## Создание пользователя (Admin) + +### POST /api/admin/users + +Требует: `Authorization: Bearer ` и роль `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": "", + "tokenType": "Bearer", + "refreshToken": "" +} +``` + +Ошибки: + +- 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 `. +- Когда `accessToken` истекает (HTTP 401), фронт вызывает `/api/auth/refresh` с `refreshToken`, получает новую пару токенов (ротация) и повторяет запрос. + +### Рекомендации по хранению + +- `refreshToken` предпочтительно хранить в HttpOnly Secure SameSite cookie (сервер ставит Set-Cookie). +- Альтернатива (менее безопасная): хранить в памяти/secure storage и передавать в теле запроса. + +### POST /api/auth/refresh + +Запрос (вариант с телом): + +```json +{ + "refreshToken": "" +} +``` + +Успех: + +- 200 OK + +```json +{ + "accessToken": "", + "tokenType": "Bearer", + "refreshToken": "" +} +``` + +Ошибки: + +- 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 +``` + +Пример защищенного вызова: + +```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 +- Сброс пароля через почту diff --git a/docs/identity.md b/docs/identity.md new file mode 100644 index 0000000..0e6e6fb --- /dev/null +++ b/docs/identity.md @@ -0,0 +1,19 @@ +## Identity + +Требуется авторизация `Authorization: Bearer `. + +### Текущий пользователь + +GET `/api/identity/me` + +Ответ: + +```json +{ + "id": 1, + "email": "root@konturai.local", + "roles": ["ROLE_ADMIN", "ROLE_USER"] +} +``` + +Ошибки: 401 — нет или просрочен токен diff --git a/index.html b/index.html index 2975cdc..5d55694 100644 --- a/index.html +++ b/index.html @@ -1,17 +1,15 @@ - + + + + + + KonturAI + + - - - - - Sakai Vue - - - - -
- - - - \ No newline at end of file + +
+ + + diff --git a/src/components/landing/FooterWidget.vue b/src/components/landing/FooterWidget.vue index 9078383..d73ae6c 100644 --- a/src/components/landing/FooterWidget.vue +++ b/src/components/landing/FooterWidget.vue @@ -20,7 +20,7 @@ /> -

SAKAI

+

KonturAI

diff --git a/src/components/landing/TopbarWidget.vue b/src/components/landing/TopbarWidget.vue index 5c78976..d73d458 100644 --- a/src/components/landing/TopbarWidget.vue +++ b/src/components/landing/TopbarWidget.vue @@ -30,7 +30,7 @@ function smoothScroll(id) { /> - SAKAI + KonturAI