From 9d4e24732ef35add877f4ae2477840972b32d441 Mon Sep 17 00:00:00 2001 From: Untone Date: Tue, 23 Sep 2025 21:34:48 +0300 Subject: [PATCH] oauth-instruct --- CHANGELOG.md | 5 ++ auth/oauth.py | 9 +- docs/auth/oauth.md | 6 +- docs/oauth-setup.md | 122 ++++++++++++++++++++++++++++ tests/auth/test_oauth.py | 2 +- tests/auth/test_oauth_functional.py | 2 + 6 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 docs/oauth-setup.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 9405ac65..c65f7af8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [0.9.23] - 2025-09-23 + +### Changed +- 🔄 **OAuth Routes**: Возвращены к стандартному формату `/oauth/{provider}` - провайдеры не передают параметр provider в callback + ## [0.9.22] - 2025-09-22 ### Fixed diff --git a/auth/oauth.py b/auth/oauth.py index cacbd242..e5723207 100644 --- a/auth/oauth.py +++ b/auth/oauth.py @@ -407,7 +407,7 @@ async def oauth_login(_: None, _info: GraphQLResolveInfo, provider: str, callbac } await store_oauth_state(state, oauth_data) - # Используем URL из фронтенда для callback + # Используем URL из фронтенда для callback - для фронтенда oauth_callback_uri = f"{callback_data['base_url']}oauth/{provider}/callback" try: @@ -545,7 +545,7 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse: } await store_oauth_state(state, oauth_data) - # URL для callback + # URL для callback - для фронтенда callback_uri = f"{FRONTEND_URL}oauth/{provider}/callback" # 🔍 Создаем redirect URL вручную (обходим использование request.session в authlib) @@ -588,6 +588,11 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon if not provider: 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) if not client: diff --git a/docs/auth/oauth.md b/docs/auth/oauth.md index 8275bbaa..ba592ff6 100644 --- a/docs/auth/oauth.md +++ b/docs/auth/oauth.md @@ -60,7 +60,7 @@ const oauth = (provider: string) => { ### 2. Backend Endpoints -#### GET `/auth/oauth/{provider}` +#### GET `/oauth/{provider}` ```python @router.get("/auth/oauth/{provider}") async def oauth_redirect( @@ -82,9 +82,9 @@ async def oauth_redirect( return RedirectResponse(url=oauth_url) ``` -#### GET `/auth/oauth/{provider}/callback` +#### GET `/oauth/{provider}/callback` ```python -@router.get("/auth/oauth/{provider}/callback") +@router.get("/oauth/{provider}/callback") async def oauth_callback( provider: str, code: str, diff --git a/docs/oauth-setup.md b/docs/oauth-setup.md new file mode 100644 index 00000000..1d3b1bea --- /dev/null +++ b/docs/oauth-setup.md @@ -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. Корректность переменных окружения diff --git a/tests/auth/test_oauth.py b/tests/auth/test_oauth.py index 7697ab7a..544f56d6 100644 --- a/tests/auth/test_oauth.py +++ b/tests/auth/test_oauth.py @@ -144,7 +144,7 @@ with ( @pytest.mark.asyncio 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) diff --git a/tests/auth/test_oauth_functional.py b/tests/auth/test_oauth_functional.py index 20772a45..20179356 100644 --- a/tests/auth/test_oauth_functional.py +++ b/tests/auth/test_oauth_functional.py @@ -109,6 +109,7 @@ class TestOAuthFunctional: "state": "valid_state", "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.headers = {"user-agent": "test-browser"} request.client = MagicMock() @@ -238,6 +239,7 @@ class TestOAuthFunctional: # Тест 3: Callback без code (но с правильным OAuth клиентом) request.query_params = {"state": "valid_state"} + request.path_params = {"provider": "google"} # Для callback используем path_params oauth_data = {"provider": "google", "code_verifier": "test"} mock_client = AsyncMock()