283 lines
11 KiB
Markdown
283 lines
11 KiB
Markdown
# 🔄 Схема работы OpenID Connect с ZITADEL
|
||
|
||
## Визуальная схема
|
||
|
||
```
|
||
┌─────────────┐
|
||
│ Пользователь│
|
||
└──────┬──────┘
|
||
│
|
||
│ 1. Открывает страницу входа
|
||
▼
|
||
┌─────────────────────────────────────┐
|
||
│ Frontend (React) │
|
||
│ http://localhost:3000 │
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ Форма входа │ │
|
||
│ │ - Логин/Пароль │ │
|
||
│ │ - Кнопка ZITADEL 🔐 │ │
|
||
│ └───────────────────────────────┘ │
|
||
└──────────────┬──────────────────────┘
|
||
│
|
||
│ 2. Нажимает "Войти через ZITADEL"
|
||
│ GET /api/auth/oidc/zitadel/login
|
||
▼
|
||
┌─────────────────────────────────────┐
|
||
│ Backend (FastAPI) │
|
||
│ http://localhost:8000 │
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ OAuth Client │ │
|
||
│ │ - Создаёт authorize URL │ │
|
||
│ │ - Добавляет state, nonce │ │
|
||
│ └───────────────────────────────┘ │
|
||
└──────────────┬──────────────────────┘
|
||
│
|
||
│ 3. Redirect на ZITADEL
|
||
▼
|
||
┌─────────────────────────────────────┐
|
||
│ ZITADEL │
|
||
│ https://your-instance.zitadel.cloud│
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ Страница входа │ │
|
||
│ │ - Email/Username │ │
|
||
│ │ - Password │ │
|
||
│ │ - 2FA (опционально) │ │
|
||
│ └───────────────────────────────┘ │
|
||
└──────────────┬──────────────────────┘
|
||
│
|
||
│ 4. Пользователь вводит данные
|
||
│ 5. ZITADEL проверяет
|
||
│ 6. Redirect с code
|
||
▼
|
||
┌─────────────────────────────────────┐
|
||
│ Backend (FastAPI) │
|
||
│ /api/auth/oidc/zitadel/callback │
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ 7. Обмен code на token │ │
|
||
│ │ POST /oauth/token │ │
|
||
│ └───────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ 8. Получение userinfo │ │
|
||
│ │ - email │ │
|
||
│ │ - name │ │
|
||
│ │ - sub (user ID) │ │
|
||
│ └───────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ 9. Создание/обновление │ │
|
||
│ │ пользователя в users.json │ │
|
||
│ └───────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ 10. Генерация JWT токена │ │
|
||
│ │ для MC Panel │ │
|
||
│ └───────────────────────────────┘ │
|
||
└──────────────┬──────────────────────┘
|
||
│
|
||
│ 11. Redirect на frontend
|
||
│ ?token=xxx&username=yyy
|
||
▼
|
||
┌─────────────────────────────────────┐
|
||
│ Frontend (React) │
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ 12. Обработка callback │ │
|
||
│ │ - Извлечение token │ │
|
||
│ │ - Сохранение в localStorage│ │
|
||
│ │ - Очистка URL │ │
|
||
│ └───────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────┐ │
|
||
│ │ 13. Автоматический вход │ │
|
||
│ │ - Загрузка данных │ │
|
||
│ │ - Показ панели │ │
|
||
│ └───────────────────────────────┘ │
|
||
└──────────────┬──────────────────────┘
|
||
│
|
||
│ 14. Пользователь в системе!
|
||
▼
|
||
┌─────────────────────────────────────┐
|
||
│ MC Panel Dashboard │
|
||
│ - Серверы │
|
||
│ - Тикеты │
|
||
│ - Личный кабинет │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
## Детальный поток данных
|
||
|
||
### Шаг 1-2: Инициация входа
|
||
```
|
||
Пользователь → Frontend
|
||
↓
|
||
Frontend → Backend: GET /api/auth/oidc/zitadel/login
|
||
↓
|
||
Backend создаёт OAuth URL:
|
||
- client_id
|
||
- redirect_uri
|
||
- scope: openid email profile
|
||
- state (CSRF защита)
|
||
- nonce (replay защита)
|
||
```
|
||
|
||
### Шаг 3-6: Аутентификация в ZITADEL
|
||
```
|
||
Backend → ZITADEL: Redirect на /oauth/authorize
|
||
↓
|
||
ZITADEL показывает форму входа
|
||
↓
|
||
Пользователь вводит данные
|
||
↓
|
||
ZITADEL проверяет учётные данные
|
||
↓
|
||
ZITADEL → Backend: Redirect с code
|
||
URL: /callback?code=xxx&state=yyy
|
||
```
|
||
|
||
### Шаг 7-8: Получение данных
|
||
```
|
||
Backend → ZITADEL: POST /oauth/token
|
||
Параметры:
|
||
- code
|
||
- client_id
|
||
- client_secret
|
||
- redirect_uri
|
||
↓
|
||
ZITADEL → Backend: access_token + id_token
|
||
↓
|
||
Backend извлекает userinfo:
|
||
{
|
||
"sub": "123456789012345678",
|
||
"email": "user@example.com",
|
||
"name": "John Doe",
|
||
"picture": "https://..."
|
||
}
|
||
```
|
||
|
||
### Шаг 9-10: Создание пользователя
|
||
```
|
||
Backend проверяет users.json:
|
||
- Ищет по oidc_id = "zitadel:123456789012345678"
|
||
|
||
Если найден:
|
||
- Обновляет email, name, picture
|
||
|
||
Если не найден:
|
||
- Создаёт нового пользователя
|
||
- Генерирует username из email
|
||
- Роль: "user"
|
||
- Пустой пароль (OIDC пользователь)
|
||
|
||
Backend создаёт JWT токен:
|
||
{
|
||
"sub": "john_doe",
|
||
"role": "user",
|
||
"exp": 1234567890
|
||
}
|
||
```
|
||
|
||
### Шаг 11-14: Возврат в приложение
|
||
```
|
||
Backend → Frontend: Redirect
|
||
URL: http://localhost:3000/?token=xxx&username=yyy
|
||
↓
|
||
Frontend (useEffect):
|
||
- Извлекает token и username из URL
|
||
- Сохраняет в localStorage
|
||
- Очищает URL (history.replaceState)
|
||
- Обновляет состояние (setToken, setUser)
|
||
↓
|
||
Frontend загружает данные:
|
||
- GET /api/auth/me (проверка токена)
|
||
- GET /api/servers (список серверов)
|
||
↓
|
||
Пользователь видит панель управления
|
||
```
|
||
|
||
## Структура данных
|
||
|
||
### ZITADEL userinfo
|
||
```json
|
||
{
|
||
"sub": "123456789012345678",
|
||
"email": "user@example.com",
|
||
"email_verified": true,
|
||
"name": "John Doe",
|
||
"given_name": "John",
|
||
"family_name": "Doe",
|
||
"picture": "https://avatar.url",
|
||
"locale": "en"
|
||
}
|
||
```
|
||
|
||
### MC Panel user (users.json)
|
||
```json
|
||
{
|
||
"john_doe": {
|
||
"username": "john_doe",
|
||
"password": "",
|
||
"role": "user",
|
||
"servers": [],
|
||
"oidc_id": "zitadel:123456789012345678",
|
||
"email": "user@example.com",
|
||
"name": "John Doe",
|
||
"picture": "https://avatar.url",
|
||
"provider": "zitadel",
|
||
"created_at": "2026-01-15T12:00:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
### JWT токен (MC Panel)
|
||
```json
|
||
{
|
||
"sub": "john_doe",
|
||
"role": "user",
|
||
"exp": 1737820800,
|
||
"iat": 1737216000
|
||
}
|
||
```
|
||
|
||
## Безопасность
|
||
|
||
### Защита от атак
|
||
|
||
1. **CSRF (Cross-Site Request Forgery)**
|
||
- State parameter проверяется
|
||
- Случайное значение для каждого запроса
|
||
|
||
2. **Replay атаки**
|
||
- Nonce в id_token
|
||
- Одноразовое использование code
|
||
|
||
3. **Man-in-the-Middle**
|
||
- HTTPS обязателен в продакшене
|
||
- Проверка redirect_uri
|
||
|
||
4. **Token theft**
|
||
- JWT с истечением (7 дней)
|
||
- Хранение в localStorage (XSS защита нужна)
|
||
|
||
### Рекомендации для продакшена
|
||
|
||
```
|
||
✓ Использовать HTTPS
|
||
✓ Настроить CORS правильно
|
||
✓ Добавить rate limiting
|
||
✓ Логировать все входы
|
||
✓ Мониторить подозрительную активность
|
||
✓ Регулярно обновлять client_secret
|
||
✓ Использовать refresh tokens
|
||
```
|
||
|
||
## Готово! 🎉
|
||
|
||
Схема показывает полный цикл аутентификации через ZITADEL.
|
||
|
||
**Всё работает автоматически и безопасно!** 🔐
|