Files
shop-server/docs/superpowers/specs/2026-05-20-yandex-vk-oauth-design.md
T
2026-05-20 10:26:45 +05:00

93 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`