This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
7
main.py
7
main.py
@@ -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, изображения)
|
||||
|
||||
Reference in New Issue
Block a user