Аутентификация
Вход, регистрация школы, сброс пароля по коду, автоопределение школ по email и профиль текущего пользователя. Эти эндпоинты не требуют авторизации (кроме /me).
- POST
/api/v1/auth/loginВход в систему - POST
/api/v1/auth/refreshПродлить управляемый сеанс - POST
/api/v1/auth/logoutЗавершить текущий сеанс - POST
/api/v1/auth/device-limit/replaceЗаменить устройство при входе - POST
/api/v1/auth/registerРегистрация школы - POST
/api/v1/auth/verify-emailПодтвердить email владельца - POST
/api/v1/auth/resend-verificationПовторить письмо подтверждения - POST
/api/v1/auth/forgot-passwordЗапросить код сброса пароля - POST
/api/v1/auth/reset-passwordСбросить пароль по коду - POST
/api/v1/auth/schoolsПоиск школ по email - GET
/api/v1/auth/meТекущий пользователь
/api/v1/auth/loginВход в систему
Проверяет email и пароль и создаёт управляемый сеанс устройства. Возвращает короткоживущий access JWT; браузеру refresh-токен передаётся только в защищённой HttpOnly cookie, а нативному приложению — в JSON. Поле subdomain указывает школу; пустое значение используется только для суперадмина платформы.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
emailобяз. | string | Email пользователя. |
passwordобяз. | string | Пароль. |
subdomainопц. | string | Субдомен школы (только латиница и цифры). Пусто — вход суперадмина (tenant_id IS NULL).Пример: acme |
device_tokenопц. | string | FCM-токен нативного мобильного устройства. При наличии токена успешный login гарантирует его сохранение. |
installation_idопц. | string | Стабильный локальный ID установки, не зависящий от FCM-токена. Обязателен для новых управляемых сеансов учеников после включения соответствующего rollout-режима. |
device_platformопц. | `web` | `ios` | `android` | Платформа установки. Для запросов из браузера сервер принудительно использует web. |
device_nameопц. | string | Понятное пользователю название устройства, до 120 символов. |
os_nameопц. | string | Название операционной системы, до 64 символов. |
os_versionопц. | string | Версия операционной системы, до 64 символов. |
browser_nameопц. | string | Название браузера, до 64 символов. |
browser_versionопц. | string | Версия браузера, до 64 символов. |
app_versionопц. | string | Версия нативного приложения, до 64 символов. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "teacher@acme.kz",
"password": "super-secret",
"subdomain": "acme",
"installation_id": "web-installation-123",
"device_platform": "web",
"device_name": "Chrome · macOS",
"os_name": "macOS",
"browser_name": "Chrome"
}'Пример ответа
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"access_expires_at": "2026-08-30T12:15:00Z",
"refresh_expires_at": "2026-11-28T12:00:00Z",
"session_id": "745810eb-...",
"auth_device_id": "2b9d59e5-...",
"user": {
"id": "8f3b1c2a-...",
"email": "teacher@acme.kz",
"role": "admin",
"first_name": "Айгерим",
"last_name": "Нурлан"
}
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid credentials | Неверный email или пароль (обобщённое сообщение — без раскрытия деталей). |
| 403 | email_not_verified | Пароль верный, но школа ещё ожидает подтверждения email владельца. |
| 403 | device_blocked | Администратор школы запретил вход с этой установки. |
| 409 | device_limit_reached | Достигнут лимит активных устройств. В data находятся безопасный список активных устройств и краткоживущий replacement_token, если самостоятельная замена разрешена. |
| 409 | installation_id_required | Клиент должен обновиться и передать стабильный installation_id. |
| 423 | too many attempts, try again later | Сработала защита от перебора — слишком много попыток входа. |
| 503 | device_registration_failed | Учетные данные верны, но переданный device_token не удалось сохранить; клиенту нужно повторить login. |
/api/v1/auth/refreshПродлить управляемый сеанс
Проверяет и атомарно ротирует refresh-токен, затем выдаёт новый короткоживущий access JWT. Браузер передаёт сохранённые non-secret session_id и auth_device_id, по которым сервер выбирает точную HttpOnly cookie; JavaScript не читает и не сохраняет сам refresh-секрет. Нативный клиент передаёт refresh_token в JSON и обязан безопасно сохранить новый токен из ответа.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
tenant_subdomainопц. | string | Субдомен школы. Может также передаваться заголовком X-Tenant-Subdomain. |
session_idопц. | uuid | Обязательный non-secret selector для браузера; значение из login/replace/refresh. Нативный клиент может не передавать. |
auth_device_idопц. | uuid | Обязательный non-secret selector browser cookie; значение из login/replace/refresh. Нативный клиент может не передавать. |
refresh_tokenопц. | string | Только для нативного клиента. Браузер использует HttpOnly cookie. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/auth/refresh -H "Content-Type: application/json" -H "X-Tenant-Subdomain: acme" -b '<device-refresh-cookie>' -d '{"session_id":"745810eb-...","auth_device_id":"2b9d59e5-..."}'Пример ответа
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"access_expires_at": "2026-08-30T12:30:00Z",
"refresh_expires_at": "2026-11-28T12:00:00Z",
"session_id": "745810eb-...",
"auth_device_id": "2b9d59e5-..."
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | session_id_required | В browser-запросе нет корректного session_id selector; cookie и серверная сессия не изменяются. |
| 400 | auth_device_id_required | В browser-запросе нет корректного auth_device_id selector; cookie и серверная сессия не изменяются. |
| 400 | refresh_token_required | Нет cookie или нативного refresh_token. |
| 401 | unauthorized | Точная cookie не найдена, selector не принадлежит её семье, либо сеанс недействителен, истёк или отозван. Ответ не раскрывает причину и ничего не изменяет при ошибке selector. |
| 409 | stale_refresh_token | Параллельный запрос уже ротировал токен; клиент должен использовать результат единственного refresh-запроса. |
/api/v1/auth/logoutЗавершить текущий сеанс
Отзывает текущую серверную сессию и очищает только выбранную браузерную refresh-cookie. Браузер обязан передать non-secret session_id и auth_device_id; сервер проверяет их связь с точной cookie до отзыва. Повторный вызов с уже завершённым подтверждённым семейством безопасен.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
tenant_subdomainопц. | string | Субдомен школы или одноимённый заголовок. |
session_idопц. | uuid | Обязательный selector для browser logout; нативный клиент может не передавать. |
auth_device_idопц. | uuid | Обязательный selector для browser logout; нативный клиент может не передавать. |
refresh_tokenопц. | string | Только для нативного клиента. |
Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | session_id_required | Нет корректного browser session selector; ни cookie, ни сессия не изменяются. |
| 400 | auth_device_id_required | Нет корректного browser device selector; ни cookie, ни сессия не изменяются. |
| 401 | unauthorized | Cookie отсутствует, неоднозначна или не связана с selectors; причина намеренно не раскрывается. |
/api/v1/auth/device-limit/replaceЗаменить устройство при входе
Продолжает отклонённый по лимиту вход: отзывает выбранное активное устройство и создаёт сеанс для новой установки. replacement_token короткоживущий, одноразовый и выдаётся только после успешной проверки пароля.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
replacement_tokenобяз. | string | Токен из ответа device_limit_reached. |
revoke_device_idобяз. | uuid | Одно из активных устройств, возвращённых вместе с ошибкой лимита. |
tenant_subdomainопц. | string | Субдомен школы или одноимённый заголовок. |
Ошибки
| Код | error | Когда |
|---|---|---|
| 403 | device_self_replacement_disabled | Школа запретила самостоятельную замену. |
| 409 | invalid_device_replacement_token | Токен замены неверен, истёк или уже использован. |
| 409 | device_limit_reached | Состояние изменилось конкурентно и лимит всё ещё превышен. |
/api/v1/auth/registerРегистрация школы
Создаёт новую школу (тенант) в статусе pending, её первого администратора и пробный биллинг-период. Письмо подтверждения ставится в outbox атомарно с регистрацией. В актуальном режиме управляемых сессий после подтверждения нужен обычный login с метаданными установки.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
tenant_titleобяз. | string | Название школы (2–255 символов). |
tenant_slugобяз. | string | Уникальный slug школы (латиница/цифры, 2–100).Пример: acme |
tenant_subdomainопц. | string | Субдомен. Если не задан — берётся из slug.Пример: acme |
emailобяз. | string | Email администратора. |
passwordобяз. | string | Пароль (минимум 8 символов). |
first_nameобяз. | string | Имя администратора. |
last_nameобяз. | string | Фамилия администратора. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"tenant_title": "Acme School",
"tenant_slug": "acme",
"email": "admin@acme.kz",
"password": "super-secret",
"first_name": "Айгерим",
"last_name": "Нурлан"
}'Пример ответа
{
"success": true,
"data": {
"verification_required": true,
"email": "admin@acme.kz",
"verification_expires_at": "2026-07-23T10:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | duplicate key | Школа с таким slug/субдоменом или пользователь с таким email уже существует. |
/api/v1/auth/verify-emailПодтвердить email владельца
Проверяет одноразовый токен из письма, отмечает email подтверждённым и переводит школу из pending в active. В актуальном режиме возвращает login_required=true: управляемый сеанс создаётся отдельным login. Поле token остаётся только для ограниченной legacy-совместимости.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
tokenобяз. | string | Подписанный одноразовый токен из verification-ссылки. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/auth/verify-email \
-H "Content-Type: application/json" \
-d '{ "token": "<token-from-email>" }'Пример ответа
{
"success": true,
"data": {
"login_required": true,
"tenant_subdomain": "acme",
"user": { "id": "8f3b1c2a-...", "email": "admin@acme.kz", "role": "admin" }
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_or_expired_email_verification | Токен неверный, просрочен или уже использован. |
/api/v1/auth/resend-verificationПовторить письмо подтверждения
Инвалидирует старую ссылку и ставит новую в outbox для pending-школы. Ответ всегда нейтральный и не раскрывает существование школы или email; действует минутный cooldown.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
emailобяз. | string | Email владельца школы. |
subdomainобяз. | string | Идентификатор школы.Пример: acme |
Пример ответа
{
"success": true,
"data": { "message": "if the account is pending, a verification email has been queued" }
}/api/v1/auth/forgot-passwordЗапросить код сброса пароля
Создаёт 6-значный код подтверждения для сброса пароля. Поле subdomain указывает школу (для суперадмина — пусто). Код действует 15 минут; одновременно активен только один код на пользователя — новый запрос инвалидирует предыдущий.
⚠️ В текущей версии код не отправляется на email, а выводится в консоль сервера (интеграция с email-провайдером запланирована). В отличие от обычной анти-перечислительной практики, эндпоинт намеренно отвечает 404, если связки email + школа не существует.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
emailобяз. | string | Email пользователя. |
subdomainопц. | string | Субдомен школы (латиница/цифры). Пусто — суперадмин.Пример: acme |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/auth/forgot-password \
-H "Content-Type: application/json" \
-d '{ "email": "teacher@acme.kz", "subdomain": "acme" }'Пример ответа
{
"success": true,
"data": { "message": "a confirmation code has been sent" }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | validation error | Некорректный email или невалидный субдомен. |
| 404 | no account with this email and school | Нет аккаунта с такой связкой email + школа. |
/api/v1/auth/reset-passwordСбросить пароль по коду
Устанавливает новый пароль по действующему коду подтверждения. Код проверяется по связке email + школа. После 5 неверных попыток код блокируется (нужно запросить новый).
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
emailобяз. | string | Email пользователя. |
subdomainопц. | string | Субдомен школы (латиница/цифры). Пусто — суперадмин.Пример: acme |
codeобяз. | string | 6-значный код подтверждения (только цифры).Пример: 048213 |
new_passwordобяз. | string | Новый пароль (минимум 8 символов). |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/auth/reset-password \
-H "Content-Type: application/json" \
-d '{
"email": "teacher@acme.kz",
"subdomain": "acme",
"code": "048213",
"new_password": "new-super-secret"
}'Пример ответа
{
"success": true,
"data": { "message": "password updated" }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid or expired confirmation code | Код неверный, просрочен (TTL 15 мин) или уже использован. |
| 423 | too many attempts, request a new code | Исчерпан лимит из 5 попыток ввода кода — запросите новый. |
/api/v1/auth/schoolsПоиск школ по email
Возвращает список активных школ, в которые может войти указанный email. Используется экраном входа для автоподстановки школы. Авторизация не требуется.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
emailобяз. | string | Email для поиска. |
scopeопц. | string | Фильтр по роли: admin — только кабинеты (админ/куратор), student — только школы, где email является учеником. Пусто — любая роль.Пример: admin |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/auth/schools \
-H "Content-Type: application/json" \
-d '{ "email": "teacher@acme.kz", "scope": "admin" }'Пример ответа
{
"success": true,
"data": [
{ "subdomain": "acme", "title": "Acme School", "role": "admin" }
]
}/api/v1/auth/meТекущий пользователь
Возвращает профиль авторизованного пользователя. Для тенант-пользователей дополнительно отдаёт состояние биллинга школы (plan_state, expires_at, days_remaining) для баннера пробного периода. Для суперадмина блок школы отсутствует.
Пример запроса
curl https://api.edumentor.kz/api/v1/auth/me \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-...",
"email": "admin@acme.kz",
"role": "admin",
"first_name": "Айгерим",
"last_name": "Нурлан",
"tenant": {
"plan_state": "trial",
"expires_at": "2026-06-12T00:00:00Z",
"days_remaining": 14
}
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |