This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.9.23] - 2025-09-23
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- 🔄 **OAuth Routes**: Возвращены к стандартному формату `/oauth/{provider}` - провайдеры не передают параметр provider в callback
|
||||||
|
|
||||||
## [0.9.22] - 2025-09-22
|
## [0.9.22] - 2025-09-22
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -407,7 +407,7 @@ async def oauth_login(_: None, _info: GraphQLResolveInfo, provider: str, callbac
|
|||||||
}
|
}
|
||||||
await store_oauth_state(state, oauth_data)
|
await store_oauth_state(state, oauth_data)
|
||||||
|
|
||||||
# Используем URL из фронтенда для callback
|
# Используем URL из фронтенда для callback - для фронтенда
|
||||||
oauth_callback_uri = f"{callback_data['base_url']}oauth/{provider}/callback"
|
oauth_callback_uri = f"{callback_data['base_url']}oauth/{provider}/callback"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -545,7 +545,7 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
|
|||||||
}
|
}
|
||||||
await store_oauth_state(state, oauth_data)
|
await store_oauth_state(state, oauth_data)
|
||||||
|
|
||||||
# URL для callback
|
# URL для callback - для фронтенда
|
||||||
callback_uri = f"{FRONTEND_URL}oauth/{provider}/callback"
|
callback_uri = f"{FRONTEND_URL}oauth/{provider}/callback"
|
||||||
|
|
||||||
# 🔍 Создаем redirect URL вручную (обходим использование request.session в authlib)
|
# 🔍 Создаем redirect URL вручную (обходим использование request.session в authlib)
|
||||||
@@ -588,6 +588,11 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon
|
|||||||
if not provider:
|
if not provider:
|
||||||
return JSONResponse({"error": "No provider in OAuth state"}, status_code=400)
|
return JSONResponse({"error": "No provider in OAuth state"}, status_code=400)
|
||||||
|
|
||||||
|
# Дополнительная проверка провайдера из path параметров (для старого формата)
|
||||||
|
provider_from_path = request.path_params.get("provider")
|
||||||
|
if provider_from_path and provider_from_path != provider:
|
||||||
|
return JSONResponse({"error": "Provider mismatch"}, status_code=400)
|
||||||
|
|
||||||
# Используем существующую логику
|
# Используем существующую логику
|
||||||
client = oauth.create_client(provider)
|
client = oauth.create_client(provider)
|
||||||
if not client:
|
if not client:
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const oauth = (provider: string) => {
|
|||||||
|
|
||||||
### 2. Backend Endpoints
|
### 2. Backend Endpoints
|
||||||
|
|
||||||
#### GET `/auth/oauth/{provider}`
|
#### GET `/oauth/{provider}`
|
||||||
```python
|
```python
|
||||||
@router.get("/auth/oauth/{provider}")
|
@router.get("/auth/oauth/{provider}")
|
||||||
async def oauth_redirect(
|
async def oauth_redirect(
|
||||||
@@ -82,9 +82,9 @@ async def oauth_redirect(
|
|||||||
return RedirectResponse(url=oauth_url)
|
return RedirectResponse(url=oauth_url)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### GET `/auth/oauth/{provider}/callback`
|
#### GET `/oauth/{provider}/callback`
|
||||||
```python
|
```python
|
||||||
@router.get("/auth/oauth/{provider}/callback")
|
@router.get("/oauth/{provider}/callback")
|
||||||
async def oauth_callback(
|
async def oauth_callback(
|
||||||
provider: str,
|
provider: str,
|
||||||
code: str,
|
code: str,
|
||||||
|
|||||||
122
docs/oauth-setup.md
Normal file
122
docs/oauth-setup.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# 🔐 Настройка OAuth Провайдеров
|
||||||
|
|
||||||
|
## 🎯 Быстрая настройка для менеджера
|
||||||
|
|
||||||
|
### 1. Google OAuth
|
||||||
|
|
||||||
|
1. Перейти в [Google Cloud Console](https://console.cloud.google.com/)
|
||||||
|
2. Создать проект или выбрать существующий
|
||||||
|
3. **APIs & Services** → **Credentials** → **Create Credentials** → **OAuth 2.0 Client ID**
|
||||||
|
4. **Application type**: Web application
|
||||||
|
5. **Authorized redirect URIs**:
|
||||||
|
- `https://v3.dscrs.site/oauth/google/callback`
|
||||||
|
- `http://localhost:3000/oauth/google/callback` (для разработки)
|
||||||
|
6. Скопировать **Client ID** и **Client Secret**
|
||||||
|
|
||||||
|
**Переменные окружения:**
|
||||||
|
```bash
|
||||||
|
GOOGLE_CLIENT_ID=your_google_client_id
|
||||||
|
GOOGLE_CLIENT_SECRET=your_google_client_secret
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. GitHub OAuth
|
||||||
|
|
||||||
|
1. Перейти в [GitHub Developer Settings](https://github.com/settings/developers)
|
||||||
|
2. **New OAuth App**
|
||||||
|
3. **Authorization callback URL**: `https://v3.dscrs.site/oauth/github/callback`
|
||||||
|
4. Скопировать **Client ID** и **Client Secret**
|
||||||
|
|
||||||
|
**Переменные окружения:**
|
||||||
|
```bash
|
||||||
|
GITHUB_CLIENT_ID=your_github_client_id
|
||||||
|
GITHUB_CLIENT_SECRET=your_github_client_secret
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. VK OAuth
|
||||||
|
|
||||||
|
1. Перейти в [VK Developers](https://dev.vk.com/apps)
|
||||||
|
2. **Создать приложение** → **Веб-сайт**
|
||||||
|
3. **Настройки** → **Redirect URI**: `https://v3.dscrs.site/oauth/vk/callback`
|
||||||
|
4. Скопировать **ID приложения** и **Защищённый ключ**
|
||||||
|
|
||||||
|
**Переменные окружения:**
|
||||||
|
```bash
|
||||||
|
VK_CLIENT_ID=your_vk_app_id
|
||||||
|
VK_CLIENT_SECRET=your_vk_secure_key
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Facebook OAuth
|
||||||
|
|
||||||
|
1. Перейти в [Facebook Developers](https://developers.facebook.com/)
|
||||||
|
2. **My Apps** → **Create App** → **Consumer**
|
||||||
|
3. **Facebook Login** → **Settings**
|
||||||
|
4. **Valid OAuth Redirect URIs**: `https://v3.dscrs.site/oauth/facebook/callback`
|
||||||
|
5. Скопировать **App ID** и **App Secret**
|
||||||
|
|
||||||
|
**Переменные окружения:**
|
||||||
|
```bash
|
||||||
|
FACEBOOK_CLIENT_ID=your_facebook_app_id
|
||||||
|
FACEBOOK_CLIENT_SECRET=your_facebook_app_secret
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Yandex OAuth
|
||||||
|
|
||||||
|
1. Перейти в [Yandex OAuth](https://oauth.yandex.ru/)
|
||||||
|
2. **Создать новое приложение**
|
||||||
|
3. **Callback URI**: `https://v3.dscrs.site/oauth/yandex/callback`
|
||||||
|
4. **Права**: `login:info`, `login:email`, `login:avatar`
|
||||||
|
5. Скопировать **ID** и **Пароль**
|
||||||
|
|
||||||
|
**Переменные окружения:**
|
||||||
|
```bash
|
||||||
|
YANDEX_CLIENT_ID=your_yandex_client_id
|
||||||
|
YANDEX_CLIENT_SECRET=your_yandex_client_secret
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Развертывание
|
||||||
|
|
||||||
|
### Локальная разработка
|
||||||
|
```bash
|
||||||
|
# .env файл
|
||||||
|
GOOGLE_CLIENT_ID=...
|
||||||
|
GOOGLE_CLIENT_SECRET=...
|
||||||
|
GITHUB_CLIENT_ID=...
|
||||||
|
GITHUB_CLIENT_SECRET=...
|
||||||
|
# и т.д.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Продакшн (Dokku/Heroku)
|
||||||
|
```bash
|
||||||
|
# Установка переменных окружения
|
||||||
|
dokku config:set myapp GOOGLE_CLIENT_ID=xxx GOOGLE_CLIENT_SECRET=yyy
|
||||||
|
# или
|
||||||
|
heroku config:set GOOGLE_CLIENT_ID=xxx GOOGLE_CLIENT_SECRET=yyy
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✅ Проверка настройки
|
||||||
|
|
||||||
|
1. Перезапустить приложение
|
||||||
|
2. Проверить логи: `OAuth provider google: id=SET, key=SET`
|
||||||
|
3. Тестовый вход: `https://v3.dscrs.site/oauth/google`
|
||||||
|
|
||||||
|
## 🔍 Диагностика проблем
|
||||||
|
|
||||||
|
**Ошибка "Provider not configured":**
|
||||||
|
- Проверить переменные окружения
|
||||||
|
- Убедиться что значения не пустые
|
||||||
|
- Перезапустить приложение
|
||||||
|
|
||||||
|
**Ошибка redirect_uri_mismatch:**
|
||||||
|
- Проверить точное соответствие URL в настройках провайдера
|
||||||
|
- Убедиться что протокол (http/https) совпадает
|
||||||
|
|
||||||
|
**VK ошибка "Code challenge method is unsupported":**
|
||||||
|
- Это нормально, VK не поддерживает PKCE
|
||||||
|
- Система автоматически обрабатывает это
|
||||||
|
|
||||||
|
## 📞 Поддержка
|
||||||
|
|
||||||
|
При проблемах проверить:
|
||||||
|
1. Логи приложения при запуске
|
||||||
|
2. Настройки redirect URI у провайдера
|
||||||
|
3. Корректность переменных окружения
|
||||||
@@ -144,7 +144,7 @@ with (
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_oauth_login_invalid_provider(mock_request):
|
async def test_oauth_login_invalid_provider(mock_request):
|
||||||
"""Тест с неправильным провайдером"""
|
"""Тест с неправильным провайдером"""
|
||||||
mock_request.path_params["provider"] = "invalid"
|
mock_request.path_params = {"provider": "invalid"}
|
||||||
|
|
||||||
response = await oauth_login_http(mock_request)
|
response = await oauth_login_http(mock_request)
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ class TestOAuthFunctional:
|
|||||||
"state": "valid_state",
|
"state": "valid_state",
|
||||||
"code": "auth_code_123"
|
"code": "auth_code_123"
|
||||||
}
|
}
|
||||||
|
request.path_params = {"provider": "google"} # Для callback используем path_params
|
||||||
request.url = "https://localhost:3000/oauth/google/callback?state=valid_state&code=auth_code_123"
|
request.url = "https://localhost:3000/oauth/google/callback?state=valid_state&code=auth_code_123"
|
||||||
request.headers = {"user-agent": "test-browser"}
|
request.headers = {"user-agent": "test-browser"}
|
||||||
request.client = MagicMock()
|
request.client = MagicMock()
|
||||||
@@ -238,6 +239,7 @@ class TestOAuthFunctional:
|
|||||||
|
|
||||||
# Тест 3: Callback без code (но с правильным OAuth клиентом)
|
# Тест 3: Callback без code (но с правильным OAuth клиентом)
|
||||||
request.query_params = {"state": "valid_state"}
|
request.query_params = {"state": "valid_state"}
|
||||||
|
request.path_params = {"provider": "google"} # Для callback используем path_params
|
||||||
oauth_data = {"provider": "google", "code_verifier": "test"}
|
oauth_data = {"provider": "google", "code_verifier": "test"}
|
||||||
|
|
||||||
mock_client = AsyncMock()
|
mock_client = AsyncMock()
|
||||||
|
|||||||
Reference in New Issue
Block a user