Initial commit: VoIdeaAI - voice-first AI idea assistant

This commit is contained in:
2026-05-13 12:51:42 +03:00
commit 688d043dad
421 changed files with 47915 additions and 0 deletions
+243
View File
@@ -0,0 +1,243 @@
# ADR-003: OAuth схема авторизации
**Статус:** принято
**Дата:** 2026-05-10
---
## Контекст
Проект VoIdea поддерживает несколько способов авторизации:
- Email + пароль
- OAuth провайдеры (Яндекс, Google, Apple)
Необходимо определить правила привязки провайдеров к аккаунтам.
---
## Решение
**Один пользователь = один провайдер**
> Нельзя привязать Google к аккаунту, зарегистрированному через Яндекс.
---
## Правила
### Основные
1. **При регистрации через OAuth** — аккаунт привязан к этому провайдеру навсегда
2. **При регистрации через email** — можно использовать только email + пароль
3. **Нельзя добавить второй провайдер** — даже если email совпадает
### Примеры
| Действие | Результат |
|----------|-----------|
| Регистрация через Яндекс → Вход через Google | ❌ Ошибка: создай новый аккаунт |
| Регистрация через Google → Вход через Яндекс | ❌ Ошибка: создай новый аккаунт |
| Регистрация через email → Вход через Яндекс | ❌ Ошибка: это разные аккаунты |
| Регистрация через Яндекс → Повторный вход через Яндекс | ✅ Работает |
---
## Обоснование
### Почему один провайдер
1. **Безопасность**
- Меньше точек входа для атак
- Сложнее украсть аккаунт
- Чёткая атрибуция действий
2. **Простота реализации**
- Не нужно merge аккаунтов
- Не нужно решать конфликты данных
- Понятная модель данных
3. **Privacy**
- Данные не смешиваются между провайдерами
- Пользователь понимает что использует
4. **Яндекс vs Google**
- Разные экосистемы
- Разные данные пользователя
- Разная политика безопасности
---
## Структура данных
### Users table
```sql
CREATE TABLE users (
id UUID PRIMARY KEY,
-- Идентификация
email VARCHAR(255) UNIQUE, -- NULL если OAuth без email
password_hash VARCHAR(255), -- NULL если OAuth-only
-- OAuth (только один провайдер)
oauth_provider VARCHAR(20), -- yandex|google|apple|null
oauth_id VARCHAR(255), -- ID в системе провайдера
-- Метаданные
email_verified BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
-- Constraints
CONSTRAINT users_oauth_xor_email CHECK (
-- OAuth с email
(oauth_provider IS NOT NULL AND email IS NOT NULL) OR
-- Email-only
(oauth_provider IS NULL AND email IS NOT NULL AND password_hash IS NOT NULL)
)
);
CREATE UNIQUE INDEX uq_users_oauth
ON users(oauth_provider, oauth_id)
WHERE oauth_provider IS NOT NULL;
```
---
## OAuth Flow
### Пример: Яндекс OAuth
```python
async def yandex_oauth_callback(code: str, db: AsyncSession):
# 1. Получаем access_token
token_data = await yandex_api.get_token(code)
# 2. Получаем данные пользователя
user_data = await yandex_api.get_user_info(token_data.access_token)
# 3. Проверяем/создаём аккаунт
user = await db.execute(
select(User).where(
User.oauth_provider == 'yandex',
User.oauth_id == user_data.id
)
)
if not user:
# Новый пользователь
user = User(
email=user_data.email,
oauth_provider='yandex',
oauth_id=user_data.id
)
db.add(user)
await db.commit()
# 4. Создаём JWT session
return create_jwt_session(user.id)
```
---
## Google OAuth
Аналогично Яндексу, с заменой endpoint-ов.
### Различия
| Параметр | Яндекс | Google |
|----------|--------|--------|
| OAuth endpoint | oauth.yandex.ru | oauth2.googleapis.com |
| User info | login.yandex.ru | www.googleapis.com/oauth2/v2/userinfo |
| Scope | login:email, profile | email, profile |
---
## Apple OAuth (отложено)
Apple будет реализован ближе к коммерческой версии.
### Требования
- App Store Developer Account ($99/год)
- Private Key для подписи (в Keychain)
- Тот же принцип: один пользователь = один провайдер
---
## Защита от привязки чужого аккаунта
### Проблема
Злоумышленник может попытаться привязать Google к чужому email.
### Решение
1. **Email verification**
- OAuth возвращает verified email
- Привязка только verified email
2. **Separate tables**
- OAuth и email разделены логически
- Разные flows для входа
3. **Audit logging**
- Все попытки OAuth логируются
- Подозрительная активность → SecurityAgent
---
## Последствия
### Положительные
- Простая модель данных
- Безопасность выше
- Понятно для пользователя
- Легко реализовать
### Отрицательные
- Пользователь не может "добавить" Google к существующему аккаунту
- При потере доступа к провайдеру — сложнее восстановить
- Нельзя merge аккаунты
### Workaround для пользователя
При потере доступа к OAuth провайдеру:
1. Обращение в поддержку
2. Подтверждение личности
3. Смена email (если нужно)
4. Сброс пароля на email
---
## Конфигурация
```bash
# .env
OAUTH_YANDEX_ID=your_yandex_client_id
OAUTH_YANDEX_SECRET=your_yandex_secret
OAUTH_YANDEX_REDIRECT_URI=http://localhost:8020/auth/yandex/callback
OAUTH_GOOGLE_ID=your_google_client_id
OAUTH_GOOGLE_SECRET=your_google_secret
OAUTH_GOOGLE_REDIRECT_URI=http://localhost:8020/auth/google/callback
# Apple - зарезервировано для будущего
# OAUTH_APPLE_ID=
# OAUTH_APPLE_TEAM_ID=
```
---
## Ответственный
**Decision maker:** Owner
**Review date:** При добавлении Apple OAuth
---
*Создано: 2026-05-10*
*См. также: docs/backlog/oauth-schema-note.md*