This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user