docs: yandex+vk oauth design spec

This commit is contained in:
Kirill
2026-05-20 10:26:45 +05:00
parent 8257a19292
commit 01bd9f8968
@@ -0,0 +1,92 @@
# Yandex ID + VK ID OAuth — Design
## Цель
Подключить авторизацию через Яндекс ID и VK ID на клиенте (серверная часть OAuth уже реализована). Добавить поля профиля: имя, фамилия, пол, аватар. Админ продолжает входить только через email/код.
## Объём
### База данных
- Переименовать `User.name``User.displayName`
- Добавить поля: `firstName String?`, `lastName String?`, `gender String?`, `avatar String?`
- Сбросить БД (`prisma migrate reset --force`), прода нет
### Сервер
1. **`server/prisma/schema.prisma`** — обновить модель User
2. **`server/src/routes/oauth-social.js`** — обновить `findOrCreateUserFromOAuth()`:
- Яндекс: сохранять `firstName`, `lastName`, `gender` (`sex`), `avatar` (`https://avatars.yandex.net/get-yapic/{default_avatar_id}/islands-200`), `displayName``real_name` или `display_name`
- VK: сохранять `firstName` (`first_name`), `lastName` (`last_name`), `gender` (`sex`: 1→female, 2→male), `avatar` (`photo_200`), `displayName``first_name + ' ' + last_name`
- Gender: если провайдер не вернул — оставлять `null`
3. **`server/src/routes/auth.js`** — обновить `mapUserForClient()`: добавить новые поля в ответ `/api/me`; переименовать `name``displayName`
4. **`server/src/**/*.js`** — найти и заменить все использования `user.name` на `user.displayName`
5. **`server/.env.example`** — документировать redirect URI для Яндекс и VK
### Клиент
1. **`client/src/shared/model/auth.ts`** — обновить тип `AuthUser`, добавить `displayName`, `firstName`, `lastName`, `gender`, `avatar`; убрать `name`
2. **`client/src/features/auth-oauth/`** — новая FSD-фича:
- `lib/oauth-providers.ts` — конфигурация: `{ id, label, icon, color }` для yandex и vk
- `ui/OAuthButtons.tsx` — компонент с двумя кнопками (Stack + Button variant="outlined"), каждая редиректит на `/api/auth/oauth/{provider}`
- `index.ts` — barrel экспорт
3. **`client/src/pages/auth/ui/AuthPage.tsx`** — добавить `<OAuthButtons />` после формы email-кода, разделив Divider'ом с текстом «или»
4. **`client/src/**/*.tsx`** — найти и заменить все использования `user.name``user.displayName`
### ENV
Переменные в `server/.env` (из примера):
```
SERVER_PUBLIC_URL=http://127.0.0.1:3333
CLIENT_PUBLIC_URL=http://127.0.0.1:5173
YANDEX_CLIENT_ID=<значение>
YANDEX_CLIENT_SECRET=<значение>
VK_CLIENT_ID=<значение>
VK_CLIENT_SECRET=<значение>
```
Redirect URI для настройки в кабинетах провайдеров:
- Яндекс (локально): `http://127.0.0.1:3333/api/auth/oauth/yandex/callback`
- VK (локально): `http://127.0.0.1:3333/api/auth/oauth/vk/callback`
- Яндекс (прод): `https://любимыйкреатив.рф/api/auth/oauth/yandex/callback`
- VK (прод): `https://любимыйкреатив.рф/api/auth/oauth/vk/callback`
## Структура фичи `features/auth-oauth/`
```
features/auth-oauth/
index.ts — barrel: export { OAuthButtons }
ui/
OAuthButtons.tsx — Stack из 2 кнопок (Яндекс, VK)
lib/
oauth-providers.ts — массив провайдеров: { id, label, icon, color }
```
## Data flow (OAuth)
```
Клиент: кнопка «Войти через Яндекс/VK»
→ редирект на /api/auth/oauth/{yandex|vk}
Сервер: формирует state JWT, редиректит на Яндекс/VK
→ пользователь авторизуется у провайдера
→ провайдер редиректит на /api/auth/oauth/{yandex|vk}/callback
Сервер: обменивает code на токен → получает профиль → findOrCreateUserFromOAuth()
→ генерирует JWT → редиректит на {CLIENT_PUBLIC_URL}/auth/callback?token=<jwt>
Клиент: AuthCallbackPage читает token → сохраняет в localStorage → редирект на /
```
## Не входит в scope
- Отображение аватара в хедере/UserMenu (будет отдельно)
- Страница профиля с новыми полями (будет отдельно)
- OAuth для админа (админ только email/код)
## Примечания
- `gender` — nullable, если провайдер не вернул пол
- VK: `sex: 1` = female, `sex: 2` = male → нормализуем в `female` / `male`
- Яндекс: avatar — конструируем URL из `default_avatar_id`, поле `is_avatar_empty` подскажет, загружен ли аватар
- Яндекс scopes: `login:email login:info`
- VK scopes: `email`
- OAuth state — JWT с `expiresIn: 15m`