145 lines
4.2 KiB
Markdown
145 lines
4.2 KiB
Markdown
# OAuth Schema - VoIdea
|
||
|
||
**Дата:** 2026-05-10
|
||
**Статус:** Backlog (для фиксации ADR)
|
||
|
||
---
|
||
|
||
## Обзор
|
||
|
||
Схема авторизации: один пользователь = один провайдер. Нельзя привязать Google если уже есть Яндекс.
|
||
|
||
---
|
||
|
||
## Правила
|
||
|
||
### Основное правило
|
||
|
||
> Один пользователь = один провайдер (email или OAuth)
|
||
|
||
Это означает:
|
||
- Если зарегистрировался через Яндекс → только Яндекс
|
||
- Если зарегистрировался через Google → только Google
|
||
- Если зарегистрировался через email → только email + пароль
|
||
|
||
### Нельзя
|
||
|
||
- Привязать Google к аккаунту зарегистрированному через Яндекс
|
||
- Добавить второй OAuth провайдер
|
||
- Изменить email после регистрации через OAuth
|
||
|
||
---
|
||
|
||
## Структура данных
|
||
|
||
### Users table
|
||
|
||
```sql
|
||
CREATE TABLE users (
|
||
id UUID PRIMARY KEY,
|
||
email VARCHAR(255) UNIQUE,
|
||
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_provider IS NOT NULL AND email IS NOT NULL) OR
|
||
(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;
|
||
```
|
||
|
||
### Почему один провайдер
|
||
|
||
1. **Безопасность** — меньше точек входа
|
||
2. **Простота** — не нужно merge аккаунтов
|
||
3. **Ясность** — пользователь знает что использует
|
||
4. **Privacy** — данные не смешиваются между провайдерами
|
||
|
||
---
|
||
|
||
## OAuth Flow
|
||
|
||
### Яндекс
|
||
|
||
```
|
||
1. Пользователь нажимает "Войти через Яндекс"
|
||
2. Редирект на Яндекс OAuth
|
||
3. Callback с code
|
||
4. Получение access_token
|
||
5. Получение данных пользователя
|
||
6. Поиск/создание user по oauth_provider + oauth_id
|
||
7. Создание JWT session
|
||
```
|
||
|
||
### Google
|
||
|
||
Аналогично Яндексу, с заменой endpoint-ов.
|
||
|
||
---
|
||
|
||
## Регистрация через email
|
||
|
||
```sql
|
||
-- При регистрации через email
|
||
INSERT INTO users (email, password_hash, oauth_provider, oauth_id)
|
||
VALUES ('user@example.com', 'hash123', NULL, NULL);
|
||
```
|
||
|
||
### Вход через email
|
||
|
||
```sql
|
||
-- Проверка пароля
|
||
SELECT * FROM users WHERE email = 'user@example.com' AND password_hash = verify('hash123');
|
||
```
|
||
|
||
---
|
||
|
||
## Защита от привязки чужого аккаунта
|
||
|
||
### Проблема
|
||
|
||
Злоумышленник может попытаться привязать Google к чужому email.
|
||
|
||
### Решение
|
||
|
||
1. **Email verification** — требуется подтверждение
|
||
2. **Password check** — для существующих пользователей
|
||
3. **Separate tables** — OAuth и email разделены логически
|
||
|
||
---
|
||
|
||
## Будущее (Apple OAuth)
|
||
|
||
### Apple будет реализован позже
|
||
|
||
Для Apple потребуется:
|
||
- App Store Developer Account
|
||
- Private Key для подписи
|
||
- Тот же принцип: один пользователь = один провайдер
|
||
|
||
---
|
||
|
||
## TODO
|
||
|
||
- [ ] Реализовать OAuth service
|
||
- [ ] Интегрировать Яндекс OAuth
|
||
- [ ] Интегрировать Google OAuth
|
||
- [ ] Зарезервировать место для Apple (не реализовывать)
|
||
- [ ] Тесты
|
||
- [ ] Документация
|
||
|
||
---
|
||
|
||
*Создано: 2026-05-10*
|
||
*См. также: docs/adr/003-oauth-schema.md* |