294 lines
11 KiB
Markdown
294 lines
11 KiB
Markdown
# 🔐 Система аутентификации Discours Core
|
||
|
||
## 📚 Обзор
|
||
|
||
Модульная система аутентификации с JWT токенами, Redis-сессиями, OAuth интеграцией и RBAC авторизацией.
|
||
|
||
### 🎯 **Гибридный подход авторизации:**
|
||
|
||
**Основной сайт (стандартный подход):**
|
||
- ✅ **OAuth** (Google/GitHub/Yandex/VK) → Bearer токен в URL → localStorage
|
||
- ✅ **Email/Password** → Bearer токен в response → localStorage
|
||
- ✅ **GraphQL запросы** → `Authorization: Bearer <token>`
|
||
- ✅ **Cross-origin совместимость** → работает везде
|
||
|
||
**Админка (максимальная безопасность):**
|
||
- ✅ **Email/Password** → httpOnly cookie (только для /panel)
|
||
- ✅ **GraphQL запросы** → `credentials: 'include'`
|
||
- ✅ **Защита от XSS/CSRF** → httpOnly + SameSite cookies
|
||
- ❌ **OAuth отключен** → только email/password для админов
|
||
|
||
## 🚀 Быстрый старт
|
||
|
||
### Для разработчиков
|
||
|
||
```python
|
||
from auth.tokens.sessions import SessionTokenManager
|
||
from auth.utils import extract_token_from_request
|
||
|
||
# Проверка токена (автоматически из cookie или Bearer заголовка)
|
||
sessions = SessionTokenManager()
|
||
token = await extract_token_from_request(request)
|
||
payload = await sessions.verify_session(token)
|
||
|
||
if payload:
|
||
user_id = payload.get("user_id")
|
||
print(f"Пользователь авторизован: {user_id}")
|
||
```
|
||
|
||
### Для фронтенда
|
||
|
||
**Основной сайт (Bearer токены):**
|
||
```typescript
|
||
// Токен из localStorage
|
||
const token = localStorage.getItem('access_token');
|
||
|
||
const response = await fetch('/graphql', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${token}` // ✅ Bearer токен из localStorage
|
||
},
|
||
body: JSON.stringify({ query, variables })
|
||
});
|
||
```
|
||
|
||
**Админка (httpOnly cookies):**
|
||
```typescript
|
||
// Cookies отправляются автоматически
|
||
const response = await fetch('/graphql', {
|
||
method: 'POST',
|
||
credentials: 'include', // ✅ КРИТИЧНО: отправляет httpOnly cookies
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ query, variables })
|
||
});
|
||
```
|
||
|
||
### Redis ключи для поиска
|
||
|
||
```bash
|
||
# Сессии пользователей
|
||
session:{user_id}:{token} # Данные сессии (hash)
|
||
user_sessions:{user_id} # Список активных токенов (set)
|
||
|
||
# OAuth токены (для API интеграций)
|
||
oauth_access:{user_id}:{provider} # Access токен
|
||
oauth_refresh:{user_id}:{provider} # Refresh токен
|
||
```
|
||
|
||
## 📖 Документация
|
||
|
||
### 🏗️ Архитектура
|
||
- **[Обзор системы](system.md)** - Компоненты и менеджеры токенов
|
||
- **[Архитектура](architecture.md)** - Диаграммы и потоки данных
|
||
- **[Миграция](migration.md)** - Обновление с предыдущих версий
|
||
|
||
### 🔑 Аутентификация
|
||
- **[Управление сессиями](sessions.md)** - JWT токены и Redis хранение
|
||
- **[OAuth интеграция](oauth.md)** - Социальные провайдеры с httpOnly cookies
|
||
- **[Микросервисы](microservices.md)** - 🎯 **Интеграция с другими сервисами**
|
||
|
||
### 🛠️ Разработка
|
||
- **[API Reference](api.md)** - Методы и примеры кода
|
||
- **[Безопасность](security.md)** - Лучшие практики
|
||
- **[Тестирование](testing.md)** - Unit и E2E тесты
|
||
|
||
### 🔗 Связанные системы
|
||
- **[RBAC System](../rbac-system.md)** - Система ролей и разрешений
|
||
- **[Security System](../security.md)** - Управление паролями и email
|
||
- **[Redis Schema](../redis-schema.md)** - Схема данных и кеширование
|
||
|
||
## 🔄 OAuth Flow (правильный 2025)
|
||
|
||
### 1. 🚀 Инициация OAuth
|
||
```typescript
|
||
// Пользователь нажимает "Войти через Google"
|
||
const handleOAuthLogin = (provider: string) => {
|
||
// Сохраняем текущую страницу для возврата
|
||
localStorage.setItem('oauth_return_url', window.location.pathname);
|
||
|
||
// Редиректим на OAuth endpoint
|
||
window.location.href = `/oauth/${provider}/login`;
|
||
};
|
||
```
|
||
|
||
### 2. 🔄 OAuth Callback (бэкенд)
|
||
```python
|
||
# Google → /oauth/google/callback
|
||
# 1. Обменивает code на access_token
|
||
# 2. Получает профиль пользователя
|
||
# 3. Создает JWT сессию
|
||
# 4. Проверяет тип приложения:
|
||
# - Основной сайт: редиректит с токеном в URL
|
||
# - Админка: устанавливает httpOnly cookie
|
||
```
|
||
|
||
### 3. 🌐 Фронтенд финализация
|
||
|
||
**Основной сайт:**
|
||
```typescript
|
||
// Читаем токен из URL
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const token = urlParams.get('access_token');
|
||
const error = urlParams.get('error');
|
||
|
||
if (error) {
|
||
console.error('OAuth error:', error);
|
||
navigate('/login');
|
||
} else if (token) {
|
||
// Сохраняем токен в localStorage
|
||
localStorage.setItem('access_token', token);
|
||
|
||
// Очищаем URL от токена
|
||
window.history.replaceState({}, '', window.location.pathname);
|
||
|
||
// Возвращаемся на сохраненную страницу
|
||
const returnUrl = localStorage.getItem('oauth_return_url') || '/';
|
||
localStorage.removeItem('oauth_return_url');
|
||
navigate(returnUrl);
|
||
}
|
||
```
|
||
|
||
**Админка:**
|
||
```typescript
|
||
// httpOnly cookie уже установлен
|
||
const error = urlParams.get('error');
|
||
if (error) {
|
||
console.error('OAuth error:', error);
|
||
navigate('/panel/login');
|
||
} else {
|
||
// Проверяем сессию (cookie отправится автоматически)
|
||
await auth.checkSession();
|
||
navigate('/panel');
|
||
}
|
||
```
|
||
|
||
## 🔍 Для микросервисов
|
||
|
||
### Подключение к Redis
|
||
|
||
```python
|
||
# Используйте тот же Redis connection pool
|
||
from storage.redis import redis
|
||
|
||
# Проверка сессии
|
||
async def check_user_session(token: str) -> dict | None:
|
||
sessions = SessionTokenManager()
|
||
return await sessions.verify_session(token)
|
||
|
||
# Массовая проверка токенов
|
||
from auth.tokens.batch import BatchTokenOperations
|
||
batch = BatchTokenOperations()
|
||
results = await batch.batch_validate_tokens(token_list)
|
||
```
|
||
|
||
### HTTP заголовки
|
||
|
||
```python
|
||
# Извлечение токена из запроса (cookie или Bearer)
|
||
from auth.utils import extract_token_from_request
|
||
|
||
token = await extract_token_from_request(request)
|
||
# Автоматически проверяет:
|
||
# 1. Authorization: Bearer <token>
|
||
# 2. Cookie: session_token=<token>
|
||
```
|
||
|
||
## 🎯 Основные компоненты
|
||
|
||
- **SessionTokenManager** - JWT сессии с Redis хранением + httpOnly cookies
|
||
- **OAuthTokenManager** - OAuth access/refresh токены для API интеграций
|
||
- **BatchTokenOperations** - Массовые операции с токенами
|
||
- **TokenMonitoring** - Мониторинг и статистика
|
||
- **AuthMiddleware** - HTTP middleware с поддержкой cookies
|
||
|
||
## ⚡ Производительность
|
||
|
||
- **Connection pooling** для Redis
|
||
- **Batch операции** для массовых действий (100-1000 токенов)
|
||
- **Pipeline использование** для атомарности
|
||
- **SCAN** вместо KEYS для безопасности
|
||
- **TTL** автоматическая очистка истекших токенов
|
||
- **httpOnly cookies** - автоматическая отправка браузером
|
||
|
||
## 🛡️ Безопасность (2025)
|
||
|
||
### Максимальная защита:
|
||
- **🚫 Защита от XSS**: httpOnly cookies недоступны JavaScript
|
||
- **🔒 Защита от CSRF**: SameSite=lax cookies
|
||
- **🛡️ Единообразие**: Все типы авторизации через cookies
|
||
- **📱 Автоматическая отправка**: Браузер сам включает cookies
|
||
|
||
### Миграция с Bearer токенов:
|
||
- ✅ OAuth теперь использует httpOnly cookies (вместо localStorage)
|
||
- ✅ Email/Password использует httpOnly cookies (вместо Bearer)
|
||
- ✅ Фронтенд: `credentials: 'include'` во всех запросах
|
||
- ✅ Middleware поддерживает оба подхода для совместимости
|
||
|
||
## 🔧 Настройка
|
||
|
||
### Environment Variables
|
||
```bash
|
||
# OAuth провайдеры
|
||
GOOGLE_CLIENT_ID=your_google_client_id
|
||
GOOGLE_CLIENT_SECRET=your_google_client_secret
|
||
GITHUB_CLIENT_ID=your_github_client_id
|
||
GITHUB_CLIENT_SECRET=your_github_client_secret
|
||
|
||
# Cookie настройки
|
||
SESSION_COOKIE_SECURE=true
|
||
SESSION_COOKIE_HTTPONLY=true
|
||
SESSION_COOKIE_SAMESITE=lax
|
||
SESSION_COOKIE_MAX_AGE=2592000 # 30 дней
|
||
|
||
# JWT
|
||
JWT_SECRET_KEY=your_jwt_secret_key
|
||
JWT_EXPIRATION_HOURS=720 # 30 дней
|
||
|
||
# Redis
|
||
REDIS_URL=redis://localhost:6379/0
|
||
```
|
||
|
||
### Быстрая проверка
|
||
```bash
|
||
# Проверка OAuth провайдеров
|
||
curl https://v3.discours.io/oauth/google
|
||
|
||
# Проверка сессии
|
||
curl -b "session_token=your_token" https://v3.discours.io/graphql \
|
||
-d '{"query":"query { getSession { success author { id } } }"}'
|
||
```
|
||
|
||
## 📊 Мониторинг
|
||
|
||
```python
|
||
from auth.tokens.monitoring import TokenMonitoring
|
||
|
||
monitoring = TokenMonitoring()
|
||
|
||
# Статистика токенов
|
||
stats = await monitoring.get_token_statistics()
|
||
print(f"Active sessions: {stats['session_tokens']}")
|
||
print(f"Memory usage: {stats['memory_usage'] / 1024 / 1024:.2f} MB")
|
||
|
||
# Health check
|
||
health = await monitoring.health_check()
|
||
if health["status"] == "healthy":
|
||
print("✅ Auth system is healthy")
|
||
```
|
||
|
||
## 🎯 Результат архитектуры 2025
|
||
|
||
**Гибридный подход - лучшее из двух миров:**
|
||
|
||
**Основной сайт (стандартный подход):**
|
||
- ✅ **OAuth**: Google/GitHub → Bearer токен в URL → localStorage → GraphQL запросы
|
||
- ✅ **Email/Password**: Login form → Bearer токен в response → localStorage → GraphQL запросы
|
||
- ✅ **Cross-origin совместимость**: Работает везде, включая мобильные приложения
|
||
- ✅ **Простота интеграции**: Стандартный Bearer токен подход
|
||
|
||
**Админка (максимальная безопасность):**
|
||
- ❌ **OAuth отключен**: Только email/password для админов
|
||
- ✅ **Email/Password**: Login form → httpOnly cookie → GraphQL запросы
|
||
- ✅ **Максимальная безопасность**: Защита от XSS и CSRF
|
||
- ✅ **Автоматическое управление**: Браузер сам отправляет cookies |