# zemlya-web — OAuth + Multi-identity план (спецификация для следующей сессии)

**Статус:** не реализовано. Юзер сформулировал требования 2026-05-19.

## Требования юзера

> Наша задача сделать разграничение telegram и сайт авторизации. Если telegram заблокируют полностью в РФ, тогда мы сможем принимать новых пользователей только по почте или авторизации через сервисы. Так же надо учесть, чтобы все платные отчёты и AI-отчёты у пользователя сохранялись, если он их приобретал в telegram, надо продумать как-то логику.

## Архитектура (multi-identity)

### Schema migration (users)

```sql
-- telegram_id: NOT NULL → NULLABLE (UNIQUE сохраняется)
-- В SQLite это full table rebuild. Использовать ALTER через CREATE TABLE new + INSERT SELECT + DROP + RENAME.
-- ВАЖНО: учесть все FK от других таблиц (reports.user_id, payments.user_id и т.д.).

ALTER TABLE users ADD COLUMN email VARCHAR(255);
ALTER TABLE users ADD COLUMN yandex_id VARCHAR(64);
ALTER TABLE users ADD COLUMN vk_id VARCHAR(64);
ALTER TABLE users ADD COLUMN password_hash VARCHAR(255);
-- Partial unique индексы (SQLite поддерживает):
CREATE UNIQUE INDEX ix_users_email ON users(email) WHERE email IS NOT NULL;
CREATE UNIQUE INDEX ix_users_yandex_id ON users(yandex_id) WHERE yandex_id IS NOT NULL;
CREATE UNIQUE INDEX ix_users_vk_id ON users(vk_id) WHERE vk_id IS NOT NULL;
```

### Identity-стратегия

Один user.id может иметь множественные идентификаторы: TG / Email / Яндекс / VK / Пароль.
При входе через любой провайдер:
1. Ищем user по identity провайдера → если есть, выпускаем JWT.
2. Если нет — создаём новый user **только с этим identity**.

Привязка происходит явно через `/dashboard/account` («Привязать TG», «Привязать Я.» и т.д.). При привязке identity нового провайдера к существующему user — сохраняется баланс.

### Связка для **миграции из TG** (главное требование юзера)

Сценарий: «Я купил отчёты через TG-бот, но Telegram заблокирован — хочу зайти через Яндекс и сохранить баланс».

1. Юзер заходит через Яндекс на сайт впервые → создаётся новый user (без баланса).
2. На `/dashboard/account` видит блок «У меня уже есть аккаунт в Telegram-боте?» → кнопка.
3. Нажимает → backend генерирует one-time-token (TTL 10 минут), сохраняет `link_token` для current_user.id.
4. Юзер инструктируется: «Откройте бот @ZemlyaBezRiskaCheck_bot и отправьте `/link_web ABC123`».
5. Бот по `telegram_id` находит TG-user. Проверяет токен → совпадение → объединение:
   - В TG-user.id переносится `yandex_id` (и `email`) от нового web-user.
   - Web-user (созданный шаг 1) удаляется (или помечается merged).
   - JWT-сессия пользователя на сайте инвалидируется → требуется перелогиниться.
6. После перелогинивания через Яндекс — попадает в TG-user.id со всем балансом и историей.

**Альтернативный сценарий:** если TG ещё работает — юзер может пользоваться обоими режимами параллельно.

## Backend (FastAPI)

### Новые routes

```
GET  /api/auth/yandex/login    → 302 redirect на oauth.yandex.ru
GET  /api/auth/yandex/callback → обмен code→access_token, профиль, find-or-create, JWT-cookie, redirect /dashboard
GET  /api/auth/email/login     (форма) → ...
POST /api/auth/email/register  → {email, password, captcha} → confirm-email-link
POST /api/auth/email/confirm   → ...
POST /api/auth/email/login-pw  → {email, password} → JWT
POST /api/auth/email/reset     → ...

GET  /api/account/identities   → текущие привязки {telegram, email, yandex, vk}
POST /api/account/link-tg/init → создать one-time-token, вернуть его
POST /api/account/link-tg/cancel
```

### Bot (zemlya-bot)

```
/link_web ABC123 → находит web-user по токену, переносит yandex_id/email/vk_id в текущий TG-user
```

### .env

```
YANDEX_OAUTH_CLIENT_ID=...
YANDEX_OAUTH_CLIENT_SECRET=...
VK_OAUTH_CLIENT_ID=...
VK_OAUTH_CLIENT_SECRET=...
EMAIL_SMTP_HOST=...
EMAIL_SMTP_USER=...
EMAIL_SMTP_PASSWORD=...
EMAIL_FROM=noreply@zemlyaweb.duckdns.org
```

## Frontend

### `/login`
Заглушки «Скоро» → активные кнопки (если backend конфиг сообщает что provider настроен).

### `/dashboard/account` (новая страница)
Список привязанных identity + кнопки «Привязать ...» для отсутствующих. UI для генерации `link_token` и инструкции по `/link_web` в боте.

### `/register` (опционально)
Регистрация по email/password (для тех у кого нет ни TG, ни Я.).

## Регистрация Yandex OAuth (юзер делает)

1. https://oauth.yandex.ru/client/new
2. Название: `Земля без риска`, платформа: ✓ Веб-сервисы
3. Callback URL: `https://zemlyaweb.duckdns.org/api/auth/yandex/callback`
4. Scope: `login:email`, `login:info`
5. Передать ClientID + ClientSecret в `.env`

## Регистрация VK OAuth (юзер делает)

1. https://id.vk.com/business (VK ID для бизнеса)
2. Создать приложение
3. Callback URL: `https://zemlyaweb.duckdns.org/api/auth/vk/callback`
4. Передать ClientID + ClientSecret

## Объём работы

- Schema migration: ~1 час (с тестами)
- Backend yandex+vk+email routes: ~3 часа
- Frontend /login + /dashboard/account: ~2 часа
- Бот /link_web: ~1 час
- E2E тест полного flow: ~1 час

**Итого: ~1 рабочий день.** Делаем когда юзер вернётся с:
- ClientID/Secret от Яндекса (и/или VK)
- Подтверждение архитектуры (multi-identity + явная привязка через /link_web)
