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

This commit is contained in:
2025-09-24 13:35:49 +03:00
parent 26f28aa35e
commit 12023d9eda
3 changed files with 34 additions and 11 deletions

View File

@@ -5,6 +5,8 @@
### Fixed
- 🔧 **OAuth Callback URL**: Исправлено формирование callback URL - добавлен отсутствующий слеш между доменом и путем
- 🔒 **OAuth HTTPS**: Принудительное использование HTTPS для callback URL в продакшне (исправляет ошибку "redirect_uri is not associated")
- 🔧 **OAuth URL Parsing**: Исправлено извлечение базового URL - теперь используется только схема и хост без пути
- 🔄 **OAuth Path Support**: Добавлена поддержка redirect_uri в path параметрах для совместимости с фронтендом
### Changed
- 🔄 **OAuth Routes**: Возвращены к стандартному формату `/oauth/{provider}` - провайдеры не передают параметр provider в callback

View File

@@ -408,10 +408,15 @@ async def oauth_login(_: None, _info: GraphQLResolveInfo, provider: str, callbac
await store_oauth_state(state, oauth_data)
# Callback должен идти на backend с принудительным HTTPS для продакшна
base_url = callback_data["base_url"].rstrip("/")
# Принудительно HTTPS
base_url = base_url.replace("http://", "https://")
oauth_callback_uri = f"{base_url}/oauth/{provider}/callback"
# Извлекаем только схему и хост из base_url (убираем путь!)
from urllib.parse import urlparse
parsed_url = urlparse(callback_data["base_url"])
scheme = "https" if parsed_url.netloc != "localhost:8000" else parsed_url.scheme
backend_base_url = f"{scheme}://{parsed_url.netloc}"
oauth_callback_uri = f"{backend_base_url}/oauth/{provider}/callback"
logger.info(f"🔗 GraphQL callback URI: '{oauth_callback_uri}'")
try:
return await client.authorize_redirect(
@@ -526,7 +531,12 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
"""HTTP handler для OAuth login"""
try:
provider = request.path_params.get("provider")
logger.info(
f"🔍 OAuth login request: provider='{provider}', url='{request.url}', path_params={request.path_params}, query_params={dict(request.query_params)}"
)
if not provider or provider not in PROVIDER_CONFIGS:
logger.error(f"❌ Invalid provider: '{provider}', available: {list(PROVIDER_CONFIGS.keys())}")
return JSONResponse({"error": "Invalid provider"}, status_code=400)
client = oauth.create_client(provider)
@@ -540,8 +550,12 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
state = token_urlsafe(32)
# 🔍 Сохраняем состояние OAuth только в Redis (убираем зависимость от request.session)
# Получаем redirect_uri из query параметров или используем FRONTEND_URL по умолчанию
final_redirect_uri = request.query_params.get("redirect_uri", FRONTEND_URL)
# Получаем redirect_uri из query параметров, path параметров или используем FRONTEND_URL по умолчанию
final_redirect_uri = (
request.query_params.get("redirect_uri") or request.path_params.get("redirect_uri") or FRONTEND_URL
)
logger.info(f"🎯 Final redirect URI: '{final_redirect_uri}'")
oauth_data = {
"code_verifier": code_verifier,
"provider": provider,
@@ -550,10 +564,13 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
}
await store_oauth_state(state, oauth_data)
# Получаем backend URL из request (принудительно HTTPS для продакшна)
# Получаем БАЗОВЫЙ backend URL (только схема + хост, без пути!)
scheme = "https" if request.url.netloc != "localhost:8000" else request.url.scheme
backend_url = f"{scheme}://{request.url.netloc}"
callback_uri = f"{backend_url}/oauth/{provider}/callback"
backend_base_url = f"{scheme}://{request.url.netloc}"
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}'")
# 🔍 Создаем redirect URL вручную (обходим использование request.session в authlib)
# VK не поддерживает PKCE, используем code_challenge только для поддерживающих провайдеров
@@ -572,6 +589,7 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
state=state,
)
logger.info(f"🚀 GitHub authorization URL: '{authorization_url['url']}'")
return RedirectResponse(url=authorization_url["url"], status_code=302)
except Exception as e:

View File

@@ -302,9 +302,12 @@ async def lifespan(app: Starlette):
app = Starlette(
routes=[
Route("/graphql", graphql_handler, methods=["GET", "POST", "OPTIONS"]),
# OAuth маршруты
Route("/oauth/{provider}", oauth_login_http, methods=["GET"]),
# OAuth маршруты - порядок важен! Более специфичные маршруты должны быть первыми
Route("/oauth/{provider}/callback", oauth_callback_http, methods=["GET"]),
Route(
"/oauth/{provider}/{redirect_uri:path}", oauth_login_http, methods=["GET"]
), # Поддержка старого формата фронтенда
Route("/oauth/{provider}", oauth_login_http, methods=["GET"]),
# Health check endpoint
Route("/health", health_handler, methods=["GET"]),
# Статические файлы (CSS, JS, изображения)