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

5.2 KiB
Raw Blame History

Yandex ID + VK ID OAuth — Design

Цель

Подключить авторизацию через Яндекс ID и VK ID на клиенте (серверная часть OAuth уже реализована). Добавить поля профиля: имя, фамилия, пол, аватар. Админ продолжает входить только через email/код.

Объём

База данных

  • Переименовать User.nameUser.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), displayNamereal_name или display_name
    • VK: сохранять firstName (first_name), lastName (last_name), gender (sex: 1→female, 2→male), avatar (photo_200), displayNamefirst_name + ' ' + last_name
    • Gender: если провайдер не вернул — оставлять null
  3. server/src/routes/auth.js — обновить mapUserForClient(): добавить новые поля в ответ /api/me; переименовать namedisplayName
  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.nameuser.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