feat(auth): вход через VK ID и Яндекс ID

Владельцу нужны VK и Яндекс (Google/Discord/GitHub остаются в каталоге и
включаются своими ключами).

VK ID (id.vk.ru, OAuth 2.1):
- обязательный PKCE: code_challenge в запросе авторизации, code_verifier при
  обмене; верификатор выводится из подписанного state (HMAC), хранить нечего;
- device_id из callback уходит в обмен кода;
- для конфиденциальных приложений секрет передаётся как service_token, а не
  client_secret (и обмен идёт параметрами в теле, не Basic);
- профиль: POST /oauth2/user_info с client_id и access_token; права email и
  vkid.personal_info.

Яндекс ID:
- права login:email и login:info; секрет — в теле запроса обмена;
- профиль: GET login.yandex.ru/info?format=json с заголовком
  "Authorization: OAuth <токен>" (не Bearer).

Общее:
- в профиль добавлено DisplayName: VK отдаёт имя и фамилию, Яндекс — real_name,
  раньше они терялись;
- почта считается подтверждённой, если провайдер её отдал (отдельного флага нет,
  адрес приходит только по соответствующему праву) — D-083;
- новые переменные OAUTH_VK_* и OAUTH_YANDEX_* в установщике и .env.example;
- тесты: полные флоу обоих провайдеров на мок-серверах, каталог и ручки.
This commit is contained in:
2026-09-27 17:57:48 +03:00
parent a835903938
commit 00d86bb70c
7 changed files with 524 additions and 26 deletions
+15
View File
@@ -786,6 +786,10 @@ generate_secrets() {
# OAuth (Фаза 7) не генерируется: клиентские id/секреты выдаёт провайдер.
# Значение берём из окружения, иначе сохраняем прежнее из .env — иначе
# `--reconfigure` молча выключил бы вход через провайдеров.
OAUTH_VK_CLIENT_ID="$(oauth_value OAUTH_VK_CLIENT_ID)"
OAUTH_VK_CLIENT_SECRET="$(oauth_value OAUTH_VK_CLIENT_SECRET)"
OAUTH_YANDEX_CLIENT_ID="$(oauth_value OAUTH_YANDEX_CLIENT_ID)"
OAUTH_YANDEX_CLIENT_SECRET="$(oauth_value OAUTH_YANDEX_CLIENT_SECRET)"
OAUTH_GITHUB_CLIENT_ID="$(oauth_value OAUTH_GITHUB_CLIENT_ID)"
OAUTH_GITHUB_CLIENT_SECRET="$(oauth_value OAUTH_GITHUB_CLIENT_SECRET)"
OAUTH_GOOGLE_CLIENT_ID="$(oauth_value OAUTH_GOOGLE_CLIENT_ID)"
@@ -970,6 +974,13 @@ AGE_RECIPIENT=@AGE_RECIPIENT@
# --- OAuth-провайдеры (Фаза 7) ---
# Пусто — провайдер выключен. Адрес возврата: <домен>/api/v1/auth/oauth/<id>/callback.
# VK ID: id.vk.ru → «Мои приложения»; секрет — сервисный ключ доступа
# (для конфиденциального приложения он обязателен при обмене кода на токен).
OAUTH_VK_CLIENT_ID=@OAUTH_VK_CLIENT_ID@
OAUTH_VK_CLIENT_SECRET=@OAUTH_VK_CLIENT_SECRET@
# Яндекс ID: oauth.yandex.ru → «Создать приложение» (права login:email, login:info).
OAUTH_YANDEX_CLIENT_ID=@OAUTH_YANDEX_CLIENT_ID@
OAUTH_YANDEX_CLIENT_SECRET=@OAUTH_YANDEX_CLIENT_SECRET@
OAUTH_GITHUB_CLIENT_ID=@OAUTH_GITHUB_CLIENT_ID@
OAUTH_GITHUB_CLIENT_SECRET=@OAUTH_GITHUB_CLIENT_SECRET@
OAUTH_GOOGLE_CLIENT_ID=@OAUTH_GOOGLE_CLIENT_ID@
@@ -1058,6 +1069,10 @@ LIVEKIT_PUBLIC_URL=${LIVEKIT_PUBLIC_URL}
# OAuth-провайдеры (Фаза 7): пусто — провайдер выключен. Кавычки добавляет
# установщик только для значений со спецсимволами (docker compose разбирает
# .env своим парсером и пустые кавычки ему не нравятся).
OAUTH_VK_CLIENT_ID=$(env_quote "$OAUTH_VK_CLIENT_ID")
OAUTH_VK_CLIENT_SECRET=$(env_quote "$OAUTH_VK_CLIENT_SECRET")
OAUTH_YANDEX_CLIENT_ID=$(env_quote "$OAUTH_YANDEX_CLIENT_ID")
OAUTH_YANDEX_CLIENT_SECRET=$(env_quote "$OAUTH_YANDEX_CLIENT_SECRET")
OAUTH_GITHUB_CLIENT_ID=$(env_quote "$OAUTH_GITHUB_CLIENT_ID")
OAUTH_GITHUB_CLIENT_SECRET=$(env_quote "$OAUTH_GITHUB_CLIENT_SECRET")
OAUTH_GOOGLE_CLIENT_ID=$(env_quote "$OAUTH_GOOGLE_CLIENT_ID")