oauth-fix3
All checks were successful
Deploy on push / deploy (push) Successful in 7m8s

This commit is contained in:
2025-09-24 23:11:01 +03:00
parent 77513080c7
commit 5d0ad2a2e3
2 changed files with 28 additions and 11 deletions

View File

@@ -6,9 +6,12 @@
- 🔧 **OAuth Token Redirect**: Исправлена передача JWT токена - теперь токен передается через URL параметры (`access_token`) вместо cookie для корректной обработки фронтендом
- 🔒 **OAuth State Security**: Добавлена обязательная передача `state` параметра в редиректе для CSRF защиты
- 🔗 **OAuth URL Parameters**: Реализована поддержка передачи токена через URL query parameters согласно OAuth 2.0 спецификации
- 🔧 **Facebook OAuth PKCE**: Отключена поддержка PKCE для Facebook - провайдер не поддерживает Code Challenge
- 🔍 **OAuth Error Logging**: Добавлено детальное логирование ошибок OAuth для диагностики проблем с провайдерами
### Changed
- 🍪 **OAuth Cookie Compatibility**: Cookie с сессией оставлена для обратной совместимости, но основной способ передачи токена - URL параметры
- 🔧 **OAuth PKCE Support**: Facebook добавлен в список провайдеров без PKCE поддержки
## [0.9.23] - 2025-09-24

View File

@@ -523,10 +523,12 @@ async def oauth_callback(request: Any) -> JSONResponse | RedirectResponse:
return response
except Exception as e:
logger.error(f"OAuth callback error: {e!s}")
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")
return RedirectResponse(url=f"{fallback_redirect}?error=auth_failed&provider={provider}")
async def store_oauth_state(state: str, data: dict) -> None:
@@ -589,18 +591,20 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
callback_uri = f"{backend_base_url}/oauth/{provider}/callback"
logger.info(f"🔗 Backend base URL: '{backend_base_url}'")
logger.info(f"🔗 Callback URI for GitHub: '{callback_uri}'")
logger.info(f"🔗 Callback URI for {provider}: '{callback_uri}'")
# 🔍 Создаем redirect URL вручную (обходим использование request.session в authlib)
# VK не поддерживает PKCE, используем code_challenge только для поддерживающих провайдеров
if provider in ["vk", "yandex", "telegram"]:
# VK, Facebook не поддерживают PKCE, используем code_challenge только для поддерживающих провайдеров
if provider in ["vk", "yandex", "telegram", "facebook"]:
# Провайдеры без PKCE поддержки
logger.info(f"🔧 Creating authorization URL without PKCE for {provider}")
authorization_url = await client.create_authorization_url(
callback_uri,
state=state,
)
else:
# Провайдеры с PKCE поддержкой (Google, GitHub, Facebook, X)
# Провайдеры с PKCE поддержкой (Google, GitHub, X)
logger.info(f"🔧 Creating authorization URL with PKCE for {provider}")
authorization_url = await client.create_authorization_url(
callback_uri,
code_challenge=code_challenge,
@@ -608,7 +612,7 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
state=state,
)
logger.info(f"🚀 GitHub authorization URL: '{authorization_url['url']}'")
logger.info(f"🚀 {provider.title()} authorization URL: '{authorization_url['url']}'")
return RedirectResponse(url=authorization_url["url"], status_code=302)
except Exception as e:
@@ -648,8 +652,9 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon
return JSONResponse({"error": "Missing authorization code"}, status_code=400)
# 🔍 Обмениваем code на токен - с PKCE или без в зависимости от провайдера
if provider in ["vk", "yandex", "telegram"]:
# Провайдеры без PKCE поддержки
if provider in ["vk", "yandex", "telegram", "facebook"]:
# Провайдеры без PKCE поддержки (Facebook может иметь проблемы с PKCE)
logger.info(f"🔧 Using OAuth without PKCE for {provider}")
token = await client.fetch_access_token(
authorization_response=str(request.url),
)
@@ -659,17 +664,24 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon
if not code_verifier:
return JSONResponse({"error": "Missing code verifier in OAuth state"}, status_code=400)
logger.info(f"🔧 Using OAuth with PKCE for {provider}")
token = await client.fetch_access_token(
authorization_response=str(request.url),
code_verifier=code_verifier,
)
if not token:
logger.error(f"❌ Failed to get access token for {provider}")
return JSONResponse({"error": "Failed to get access token"}, status_code=400)
logger.info(f"✅ Got access token for {provider}: {bool(token)}")
profile = await get_user_profile(provider, client, token)
if not profile:
logger.error(f"❌ Failed to get user profile for {provider}")
return JSONResponse({"error": "Failed to get user profile"}, status_code=400)
logger.info(f"✅ Got user profile for {provider}: id={profile.get('id')}, email={profile.get('email')}, name={profile.get('name')}")
# Создаем или обновляем пользователя используя helper функцию
author = await _create_or_update_user(provider, profile)
if not author:
@@ -734,10 +746,12 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon
return response
except Exception as e:
logger.error(f"OAuth callback error: {e!s}")
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")
return RedirectResponse(url=f"{fallback_redirect}?error=auth_failed&provider={provider}")
async def _create_or_update_user(provider: str, profile: dict) -> Author: