All checks were successful
Deploy on push / deploy (push) Successful in 5m47s
- **🔍 Comprehensive authentication documentation refactoring**: Полная переработка документации аутентификации
- Обновлена таблица содержания в README.md
- Исправлены архитектурные диаграммы - токены хранятся только в Redis
- Добавлены практические примеры кода для микросервисов
- Консолидирована OAuth документация
374 lines
12 KiB
Markdown
374 lines
12 KiB
Markdown
# Система авторизации Discours Core
|
||
|
||
## 🎯 Обзор архитектуры
|
||
|
||
Модульная система авторизации с JWT токенами, Redis-сессиями и OAuth интеграцией. Построена на принципах разделения ответственности и высокой производительности.
|
||
|
||
```
|
||
auth/
|
||
├── tokens/ # 🎯 Система управления токенами
|
||
│ ├── sessions.py # JWT сессии с Redis
|
||
│ ├── verification.py # Одноразовые токены
|
||
│ ├── oauth.py # OAuth токены
|
||
│ ├── batch.py # Массовые операции
|
||
│ ├── monitoring.py # Мониторинг и статистика
|
||
│ ├── storage.py # Фасад для совместимости
|
||
│ ├── base.py # Базовые классы
|
||
│ └── types.py # Типы и константы
|
||
├── middleware.py # HTTP middleware
|
||
├── decorators.py # GraphQL декораторы
|
||
├── oauth.py # OAuth провайдеры
|
||
├── identity.py # Методы идентификации
|
||
├── jwtcodec.py # JWT кодек
|
||
├── validations.py # Валидация данных
|
||
├── credentials.py # Креденшиалы
|
||
├── exceptions.py # Исключения
|
||
└── utils.py # Утилиты
|
||
```
|
||
|
||
## 🎯 Система токенов
|
||
|
||
### SessionTokenManager
|
||
|
||
**Принцип работы:**
|
||
1. JWT токены с payload `{user_id, username, iat, exp}`
|
||
2. Redis хранение для отзыва и управления жизненным циклом
|
||
3. Поддержка множественных сессий на пользователя
|
||
4. Автоматическое обновление `last_activity` при активности
|
||
|
||
**Redis структура:**
|
||
```bash
|
||
session:{user_id}:{token} # hash с данными сессии
|
||
user_sessions:{user_id} # set с активными токенами
|
||
```
|
||
|
||
**Основные методы:**
|
||
```python
|
||
from auth.tokens.sessions import SessionTokenManager
|
||
|
||
sessions = SessionTokenManager()
|
||
|
||
# Создание сессии
|
||
token = await sessions.create_session(user_id, username=username)
|
||
|
||
# Проверка сессии
|
||
payload = await sessions.verify_session(token)
|
||
|
||
# Обновление сессии
|
||
new_token = await sessions.refresh_session(user_id, old_token)
|
||
|
||
# Отзыв сессии
|
||
await sessions.revoke_session_token(token)
|
||
|
||
# Получение всех сессий пользователя
|
||
user_sessions = await sessions.get_user_sessions(user_id)
|
||
```
|
||
|
||
### Типы токенов
|
||
|
||
| Тип | TTL | Назначение | Менеджер |
|
||
|-----|-----|------------|----------|
|
||
| `session` | 30 дней | JWT сессии пользователей | `SessionTokenManager` |
|
||
| `verification` | 1 час | Одноразовые токены подтверждения | `VerificationTokenManager` |
|
||
| `oauth_access` | 1 час | OAuth access токены | `OAuthTokenManager` |
|
||
| `oauth_refresh` | 30 дней | OAuth refresh токены | `OAuthTokenManager` |
|
||
|
||
### Менеджеры токенов
|
||
|
||
#### 1. **SessionTokenManager** - JWT сессии
|
||
```python
|
||
from auth.tokens.sessions import SessionTokenManager
|
||
|
||
sessions = SessionTokenManager()
|
||
|
||
# Создание сессии
|
||
token = await sessions.create_session(
|
||
user_id="123",
|
||
auth_data={"provider": "local"},
|
||
username="john_doe",
|
||
device_info={"ip": "192.168.1.1", "user_agent": "Mozilla/5.0"}
|
||
)
|
||
|
||
# Создание JWT токена сессии
|
||
token = await sessions.create_session_token(
|
||
user_id="123",
|
||
token_data={"username": "john_doe", "device_info": "..."}
|
||
)
|
||
|
||
# Проверка сессии (совместимость с TokenStorage)
|
||
payload = await sessions.verify_session(token)
|
||
# Возвращает: {"user_id": "123", "username": "john_doe", "iat": 1640995200, "exp": 1643587200}
|
||
|
||
# Валидация токена сессии
|
||
valid, data = await sessions.validate_session_token(token)
|
||
|
||
# Получение данных сессии
|
||
session_data = await sessions.get_session_data(token, user_id)
|
||
|
||
# Обновление сессии
|
||
new_token = await sessions.refresh_session(user_id, old_token, device_info)
|
||
|
||
# Отзыв сессии
|
||
await sessions.revoke_session_token(token)
|
||
|
||
# Отзыв всех сессий пользователя
|
||
revoked_count = await sessions.revoke_user_sessions(user_id)
|
||
|
||
# Получение всех сессий пользователя
|
||
user_sessions = await sessions.get_user_sessions(user_id)
|
||
```
|
||
|
||
#### 2. **VerificationTokenManager** - Одноразовые токены
|
||
```python
|
||
from auth.tokens.verification import VerificationTokenManager
|
||
|
||
verification = VerificationTokenManager()
|
||
|
||
# Создание токена подтверждения email
|
||
token = await verification.create_verification_token(
|
||
user_id="123",
|
||
verification_type="email_change",
|
||
data={"new_email": "new@example.com"},
|
||
ttl=3600 # 1 час
|
||
)
|
||
|
||
# Проверка токена
|
||
valid, data = await verification.validate_verification_token(token)
|
||
|
||
# Подтверждение (одноразовое использование)
|
||
confirmed_data = await verification.confirm_verification_token(token)
|
||
```
|
||
|
||
#### 3. **OAuthTokenManager** - OAuth токены
|
||
```python
|
||
from auth.tokens.oauth import OAuthTokenManager
|
||
|
||
oauth = OAuthTokenManager()
|
||
|
||
# Сохранение OAuth токенов
|
||
await oauth.store_oauth_tokens(
|
||
user_id="123",
|
||
provider="google",
|
||
access_token="ya29.a0AfH6SM...",
|
||
refresh_token="1//04...",
|
||
expires_in=3600,
|
||
additional_data={"scope": "read write"}
|
||
)
|
||
|
||
# Создание OAuth токена (внутренний метод)
|
||
token_key = await oauth._create_oauth_token(
|
||
user_id="123",
|
||
token_data={"token": "ya29.a0AfH6SM...", "provider": "google"},
|
||
ttl=3600,
|
||
provider="google",
|
||
token_type="oauth_access"
|
||
)
|
||
|
||
# Получение access токена
|
||
access_data = await oauth.get_token(user_id, "google", "oauth_access")
|
||
|
||
# Оптимизированное получение OAuth данных
|
||
oauth_data = await oauth._get_oauth_data_optimized("oauth_access", "123", "google")
|
||
|
||
# Отзыв OAuth токенов
|
||
await oauth.revoke_oauth_tokens(user_id, "google")
|
||
|
||
# Оптимизированный отзыв токена
|
||
revoked = await oauth._revoke_oauth_token_optimized("oauth_access", "123", "google")
|
||
|
||
# Отзыв всех OAuth токенов пользователя
|
||
revoked_count = await oauth.revoke_user_oauth_tokens(user_id, "oauth_access")
|
||
```
|
||
|
||
#### 4. **BatchTokenOperations** - Массовые операции
|
||
```python
|
||
from auth.tokens.batch import BatchTokenOperations
|
||
|
||
batch = BatchTokenOperations()
|
||
|
||
# Массовая проверка токенов
|
||
tokens = ["token1", "token2", "token3"]
|
||
results = await batch.batch_validate_tokens(tokens)
|
||
# {"token1": True, "token2": False, "token3": True}
|
||
|
||
# Валидация батча токенов (внутренний метод)
|
||
batch_results = await batch._validate_token_batch(tokens)
|
||
|
||
# Безопасное декодирование токена
|
||
payload = await batch._safe_decode_token(token)
|
||
|
||
# Массовый отзыв токенов
|
||
revoked_count = await batch.batch_revoke_tokens(tokens)
|
||
|
||
# Отзыв батча токенов (внутренний метод)
|
||
batch_revoked = await batch._revoke_token_batch(tokens)
|
||
|
||
# Очистка истекших токенов
|
||
cleaned_count = await batch.cleanup_expired_tokens()
|
||
```
|
||
|
||
#### 5. **TokenMonitoring** - Мониторинг
|
||
```python
|
||
from auth.tokens.monitoring import TokenMonitoring
|
||
|
||
monitoring = TokenMonitoring()
|
||
|
||
# Статистика токенов
|
||
stats = await monitoring.get_token_statistics()
|
||
# {
|
||
# "session_tokens": 150,
|
||
# "verification_tokens": 5,
|
||
# "oauth_access_tokens": 25,
|
||
# "oauth_refresh_tokens": 25,
|
||
# "memory_usage": 1048576
|
||
# }
|
||
|
||
# Подсчет ключей по паттерну (внутренний метод)
|
||
count = await monitoring._count_keys_by_pattern("session:*")
|
||
|
||
# Health check
|
||
health = await monitoring.health_check()
|
||
# {"status": "healthy", "redis_connected": True, "token_count": 205}
|
||
|
||
# Оптимизация памяти
|
||
optimization = await monitoring.optimize_memory_usage()
|
||
# {"cleaned_expired": 10, "memory_freed": 102400}
|
||
|
||
# Оптимизация структур данных (внутренний метод)
|
||
optimized = await monitoring._optimize_data_structures()
|
||
```
|
||
|
||
### TokenStorage (Фасад для совместимости)
|
||
```python
|
||
from auth.tokens.storage import TokenStorage
|
||
|
||
# Упрощенный API для основных операций
|
||
await TokenStorage.create_session(user_id, username=username)
|
||
await TokenStorage.verify_session(token)
|
||
await TokenStorage.refresh_session(user_id, old_token, device_info)
|
||
await TokenStorage.revoke_session(token)
|
||
```
|
||
|
||
## 🔧 Middleware и декораторы
|
||
|
||
### AuthMiddleware
|
||
```python
|
||
from auth.middleware import AuthMiddleware
|
||
|
||
# Автоматическая обработка токенов
|
||
middleware = AuthMiddleware()
|
||
|
||
# Извлечение токена из запроса
|
||
token = await extract_token_from_request(request)
|
||
|
||
# Проверка сессии
|
||
payload = await sessions.verify_session(token)
|
||
```
|
||
|
||
### GraphQL декораторы
|
||
```python
|
||
from auth.decorators import auth_required, permission_required
|
||
|
||
@auth_required
|
||
async def protected_resolver(info, **kwargs):
|
||
"""Требует авторизации"""
|
||
user = info.context.get('user')
|
||
return f"Hello, {user.username}!"
|
||
|
||
@permission_required("shout:create")
|
||
async def create_shout(info, input_data):
|
||
"""Требует права на создание публикаций"""
|
||
pass
|
||
```
|
||
|
||
## ORM модели
|
||
|
||
### Author (Пользователь)
|
||
```python
|
||
class Author:
|
||
id: int
|
||
email: str
|
||
name: str
|
||
slug: str
|
||
password: Optional[str] # bcrypt hash
|
||
pic: Optional[str] # URL аватара
|
||
bio: Optional[str]
|
||
email_verified: bool
|
||
phone_verified: bool
|
||
created_at: int
|
||
updated_at: int
|
||
last_seen: int
|
||
|
||
# OAuth данные в JSON формате
|
||
oauth: Optional[dict] # {"google": {"id": "123", "email": "user@gmail.com"}}
|
||
|
||
# Поля аутентификации
|
||
failed_login_attempts: int
|
||
account_locked_until: Optional[int]
|
||
```
|
||
|
||
### OAuth данные
|
||
OAuth данные хранятся в JSON поле `oauth` модели `Author`:
|
||
```python
|
||
# Формат oauth поля
|
||
{
|
||
"google": {
|
||
"id": "123456789",
|
||
"email": "user@gmail.com",
|
||
"name": "John Doe"
|
||
},
|
||
"github": {
|
||
"id": "456789",
|
||
"login": "johndoe",
|
||
"email": "user@github.com"
|
||
}
|
||
}
|
||
```
|
||
|
||
## ⚙️ Конфигурация
|
||
|
||
### Переменные окружения
|
||
```bash
|
||
# JWT настройки
|
||
JWT_SECRET=your_super_secret_key
|
||
JWT_EXPIRATION_HOURS=720 # 30 дней
|
||
|
||
# Redis подключение
|
||
REDIS_URL=redis://localhost:6379/0
|
||
|
||
# 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
|
||
FACEBOOK_APP_ID=your_facebook_app_id
|
||
FACEBOOK_APP_SECRET=your_facebook_app_secret
|
||
VK_APP_ID=your_vk_app_id
|
||
VK_APP_SECRET=your_vk_app_secret
|
||
YANDEX_CLIENT_ID=your_yandex_client_id
|
||
YANDEX_CLIENT_SECRET=your_yandex_client_secret
|
||
|
||
# Session cookies
|
||
SESSION_COOKIE_NAME=session_token
|
||
SESSION_COOKIE_SECURE=true
|
||
SESSION_COOKIE_HTTPONLY=true
|
||
SESSION_COOKIE_SAMESITE=lax
|
||
SESSION_COOKIE_MAX_AGE=2592000 # 30 дней
|
||
|
||
# Frontend
|
||
FRONTEND_URL=https://yourdomain.com
|
||
```
|
||
|
||
## Производительность
|
||
|
||
### Оптимизации Redis
|
||
- **Pipeline операции** для атомарности
|
||
- **Batch обработка** токенов (100-1000 за раз)
|
||
- **SCAN** вместо KEYS для безопасности
|
||
- **TTL** автоматическая очистка
|
||
|
||
### Кэширование
|
||
- **@lru_cache** для часто используемых ключей
|
||
- **Connection pooling** для Redis
|
||
- **JWT decode caching** в middleware
|