From 0f6cc6128646001926f78d262bc6487f342d7082 Mon Sep 17 00:00:00 2001 From: Untone Date: Sat, 27 Sep 2025 12:31:53 +0300 Subject: [PATCH] mypyfix --- auth/oauth.py | 39 ++++++++++++++++++- docs/oauth-debug-checklist.md | 72 +++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 docs/oauth-debug-checklist.md diff --git a/auth/oauth.py b/auth/oauth.py index 5ba8c712..ad361975 100644 --- a/auth/oauth.py +++ b/auth/oauth.py @@ -680,6 +680,14 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon client = oauth.create_client(provider) if not client: logger.warning(f"🚨 OAuth provider {provider} not configured - returning graceful error") + # Проверяем конфигурацию провайдера + from settings import OAUTH_CLIENTS + + provider_config = OAUTH_CLIENTS.get(provider.upper(), {}) + logger.error( + f"🚨 OAuth config for {provider}: client_id={'***' if provider_config.get('id') else 'MISSING'}, client_secret={'***' if provider_config.get('key') else 'MISSING'}" + ) + # Graceful fallback: редиректим на фронтенд с информативной ошибкой redirect_uri = oauth_data.get("redirect_uri", FRONTEND_URL) error_url = f"{redirect_uri}?error=provider_not_configured&provider={provider}&message=OAuth+provider+credentials+missing" @@ -692,6 +700,9 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon # 🔍 Обмениваем code на токен - с PKCE или без в зависимости от провайдера logger.info("🔄 Step 1: Exchanging authorization code for access token...") + logger.info(f"🔧 Authorization response URL: {request.url}") + logger.info(f"🔧 Code parameter: {code[:20]}..." if code and len(code) > 20 else f"🔧 Code parameter: {code}") + try: if provider in ["vk", "yandex", "telegram", "facebook"]: # Провайдеры без PKCE поддержки (Facebook может иметь проблемы с PKCE) @@ -707,12 +718,15 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon return JSONResponse({"error": "Missing code verifier in OAuth state"}, status_code=400) logger.info(f"🔧 Using OAuth with PKCE for {provider}") + logger.info(f"🔧 Code verifier length: {len(code_verifier) if code_verifier else 0}") token = await client.fetch_access_token( authorization_response=str(request.url), code_verifier=code_verifier, ) except Exception as e: logger.error(f"❌ Failed to fetch access token for {provider}: {e}", exc_info=True) + logger.error(f"❌ Request URL: {request.url}") + logger.error(f"❌ OAuth data: {oauth_data}") raise # Re-raise для обработки в основном except блоке if not token: logger.error(f"❌ Failed to get access token for {provider}") @@ -848,9 +862,30 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon logger.error(f"OAuth callback error for {provider}: {e!s}", exc_info=True) logger.error(f"OAuth callback request URL: {request.url}") logger.error(f"OAuth callback query params: {dict(request.query_params)}") + # В случае ошибки редиректим на фронтенд с ошибкой - fallback_redirect = request.query_params.get("redirect_uri", FRONTEND_URL) - return RedirectResponse(url=f"{fallback_redirect}?error=auth_failed&provider={provider}") + # Используем сохраненный redirect_uri из OAuth state или fallback + try: + state = request.query_params.get("state") + oauth_data = await get_oauth_state(state) if state else None + fallback_redirect = oauth_data.get("redirect_uri") if oauth_data else FRONTEND_URL + except Exception: + fallback_redirect = FRONTEND_URL + + # Обеспечиваем что fallback_redirect это строка + if not isinstance(fallback_redirect, str): + fallback_redirect = FRONTEND_URL + + # Для testing.discours.io используем специальный формат + if "testing.discours.io" in fallback_redirect: + from urllib.parse import quote + + error_url = f"https://testing.discours.io/oauth?error=auth_failed&provider={provider}&redirect_url={quote(fallback_redirect)}" + else: + error_url = f"{fallback_redirect}?error=auth_failed&provider={provider}" + + logger.error(f"🚨 Redirecting to error URL: {error_url}") + return RedirectResponse(url=error_url) async def _create_or_update_user(provider: str, profile: dict) -> Author: diff --git a/docs/oauth-debug-checklist.md b/docs/oauth-debug-checklist.md new file mode 100644 index 00000000..769b557d --- /dev/null +++ b/docs/oauth-debug-checklist.md @@ -0,0 +1,72 @@ +# 🔍 OAuth Debug Checklist + +## 🚨 Проблема: Google callback получает параметры, но фронтенд получает auth_failed + +### ✅ Диагностика выполнена: + +1. **✅ OAuth Endpoint существует**: `/oauth/google/callback` - роут настроен в `main.py` +2. **⚠️ OAuth провайдеры**: Настроены на продакшн сервере (Dokku), локально отсутствуют +3. **✅ Логирование добавлено**: Детальные логи на каждом этапе OAuth flow + +### 🎯 Правильный подход к диагностике продакшн проблем: + +### 🔧 Исправления внесены: + +1. **Улучшена обработка ошибок**: Правильный redirect на testing.discours.io с error параметрами +2. **Добавлена диагностика**: Проверка конфигурации OAuth провайдеров +3. **Создан скрипт проверки**: `scripts/check_oauth_config.py` + +### 🎯 Диагностика продакшн OAuth проблем: + +#### 1. Анализ продакшн логов (приоритет): +```bash +# Смотрим логи OAuth callback на продакшн сервере +dokku logs discours --tail 100 | grep -E "(OAuth|callback|google)" + +# Ищем конкретные ошибки в логах +dokku logs discours --tail 500 | grep -E "(❌|ERROR|Exception)" +``` + +#### 2. Проверка переменных окружения на сервере: +```bash +# Проверяем настройки OAuth на продакшн +dokku config:show discours | grep -E "(GOOGLE|GITHUB|OAUTH)" +``` + +#### 3. Проверка redirect URI в Google Console: +- Должен быть: `https://v3.dscrs.site/oauth/google/callback` +- Проверить точное совпадение URL +- Убедиться что HTTPS включен + +#### 4. Тестирование с детальными логами: +- Логи уже добавлены в код +- Смотреть продакшн логи во время OAuth попытки +- Анализировать каждый этап: token exchange, profile fetch, user creation + +### 🔍 Логи для мониторинга: + +После настройки OAuth провайдеров, логи должны показывать: +``` +✅ Got access token for google: True +✅ Got user profile for google: id=..., email=..., name=... +✅ User created/updated for google: user_id=..., email=... +✅ Session token created for google: token_length=... +🔗 OAuth redirect URL: https://testing.discours.io/oauth?redirect_url=... +OAuth успешно завершен для google, user_id=... +``` + +### 🚨 Критические проверки: + +1. **Redirect URI в Google Console** должен точно совпадать с `https://v3.dscrs.site/oauth/google/callback` +2. **HTTPS обязателен** для продакшена +3. **Переменные окружения** должны быть установлены на сервере +4. **Перезапуск сервера** после установки переменных окружения + +### 📊 Ожидаемый результат: + +После настройки OAuth: +- ✅ Google callback обрабатывается успешно +- ✅ Пользователь создается/обновляется +- ✅ Session token устанавливается в httpOnly cookie +- ✅ Редирект на `https://testing.discours.io/oauth?redirect_url=...` +- ✅ Фронтенд получает успешную аутентификацию