session-mdlwr-oauth-fix
All checks were successful
Deploy on push / deploy (push) Successful in 7m9s
All checks were successful
Deploy on push / deploy (push) Successful in 7m9s
This commit is contained in:
@@ -467,7 +467,7 @@ async def oauth_callback(request: Any) -> JSONResponse | RedirectResponse:
|
|||||||
else None,
|
else None,
|
||||||
device_info={
|
device_info={
|
||||||
"user_agent": request.headers.get("user-agent"),
|
"user_agent": request.headers.get("user-agent"),
|
||||||
"ip": request.client.host if hasattr(request, "client") else None,
|
"ip": request.client.host if hasattr(request, "client") and request.client else None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -536,12 +536,7 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
|
|||||||
code_challenge = create_s256_code_challenge(code_verifier)
|
code_challenge = create_s256_code_challenge(code_verifier)
|
||||||
state = token_urlsafe(32)
|
state = token_urlsafe(32)
|
||||||
|
|
||||||
# Сохраняем состояние в сессии
|
# 🔍 Сохраняем состояние OAuth только в Redis (убираем зависимость от request.session)
|
||||||
request.session["code_verifier"] = code_verifier
|
|
||||||
request.session["provider"] = provider
|
|
||||||
request.session["state"] = state
|
|
||||||
|
|
||||||
# Сохраняем состояние OAuth в Redis
|
|
||||||
oauth_data = {
|
oauth_data = {
|
||||||
"code_verifier": code_verifier,
|
"code_verifier": code_verifier,
|
||||||
"provider": provider,
|
"provider": provider,
|
||||||
@@ -569,24 +564,27 @@ async def oauth_login_http(request: Request) -> JSONResponse | RedirectResponse:
|
|||||||
async def oauth_callback_http(request: Request) -> JSONResponse | RedirectResponse:
|
async def oauth_callback_http(request: Request) -> JSONResponse | RedirectResponse:
|
||||||
"""HTTP handler для OAuth callback"""
|
"""HTTP handler для OAuth callback"""
|
||||||
try:
|
try:
|
||||||
# Используем GraphQL resolver логику
|
# 🔍 Получаем состояние OAuth только из Redis (убираем зависимость от request.session)
|
||||||
provider = request.session.get("provider")
|
|
||||||
if not provider:
|
|
||||||
return JSONResponse({"error": "No OAuth session found"}, status_code=400)
|
|
||||||
|
|
||||||
state = request.query_params.get("state")
|
state = request.query_params.get("state")
|
||||||
session_state = request.session.get("state")
|
if not state:
|
||||||
|
return JSONResponse({"error": "Missing OAuth state parameter"}, status_code=400)
|
||||||
if not state or state != session_state:
|
|
||||||
return JSONResponse({"error": "Invalid or expired OAuth state"}, status_code=400)
|
|
||||||
|
|
||||||
oauth_data = await get_oauth_state(state)
|
oauth_data = await get_oauth_state(state)
|
||||||
if not oauth_data:
|
if not oauth_data:
|
||||||
return JSONResponse({"error": "Invalid or expired OAuth state"}, status_code=400)
|
return JSONResponse({"error": "Invalid or expired OAuth state"}, status_code=400)
|
||||||
|
|
||||||
|
provider = oauth_data.get("provider")
|
||||||
|
if not provider:
|
||||||
|
return JSONResponse({"error": "No provider in OAuth state"}, status_code=400)
|
||||||
|
|
||||||
# Используем существующую логику
|
# Используем существующую логику
|
||||||
client = oauth.create_client(provider)
|
client = oauth.create_client(provider)
|
||||||
|
if not client:
|
||||||
|
return JSONResponse({"error": "Provider not configured"}, status_code=400)
|
||||||
|
|
||||||
token = await client.authorize_access_token(request)
|
token = await client.authorize_access_token(request)
|
||||||
|
if not token:
|
||||||
|
return JSONResponse({"error": "Failed to get access token"}, status_code=400)
|
||||||
|
|
||||||
profile = await get_user_profile(provider, client, token)
|
profile = await get_user_profile(provider, client, token)
|
||||||
if not profile:
|
if not profile:
|
||||||
@@ -594,17 +592,34 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon
|
|||||||
|
|
||||||
# Создаем или обновляем пользователя используя helper функцию
|
# Создаем или обновляем пользователя используя helper функцию
|
||||||
author = await _create_or_update_user(provider, profile)
|
author = await _create_or_update_user(provider, profile)
|
||||||
|
if not author:
|
||||||
|
return JSONResponse({"error": "Failed to create/update user"}, status_code=500)
|
||||||
|
|
||||||
# Создаем токен сессии
|
# Создаем токен сессии с полными данными
|
||||||
session_token = await TokenStorage.create_session(str(author.id))
|
session_token = await TokenStorage.create_session(
|
||||||
|
str(author.id),
|
||||||
|
auth_data={
|
||||||
|
"provider": provider,
|
||||||
|
"profile": profile,
|
||||||
|
},
|
||||||
|
username=author.name
|
||||||
|
if isinstance(author.name, str)
|
||||||
|
else str(author.name)
|
||||||
|
if author.name is not None
|
||||||
|
else None,
|
||||||
|
device_info={
|
||||||
|
"user_agent": request.headers.get("user-agent"),
|
||||||
|
"ip": request.client.host if hasattr(request, "client") and request.client else None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
# Очищаем OAuth сессию
|
# Получаем redirect_uri из OAuth данных
|
||||||
request.session.pop("code_verifier", None)
|
redirect_uri = oauth_data.get("redirect_uri", FRONTEND_URL)
|
||||||
request.session.pop("provider", None)
|
if not isinstance(redirect_uri, str) or not redirect_uri:
|
||||||
request.session.pop("state", None)
|
redirect_uri = FRONTEND_URL
|
||||||
|
|
||||||
# Возвращаем redirect с cookie
|
# Возвращаем redirect с cookie
|
||||||
response = RedirectResponse(url="/auth/success", status_code=307)
|
response = RedirectResponse(url=str(redirect_uri), status_code=307)
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
SESSION_COOKIE_NAME,
|
SESSION_COOKIE_NAME,
|
||||||
session_token,
|
session_token,
|
||||||
@@ -612,12 +627,17 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon
|
|||||||
secure=SESSION_COOKIE_SECURE,
|
secure=SESSION_COOKIE_SECURE,
|
||||||
samesite=SESSION_COOKIE_SAMESITE,
|
samesite=SESSION_COOKIE_SAMESITE,
|
||||||
max_age=SESSION_COOKIE_MAX_AGE,
|
max_age=SESSION_COOKIE_MAX_AGE,
|
||||||
|
path="/", # Важно: устанавливаем path="/" для доступности cookie во всех путях
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.info(f"OAuth успешно завершен для {provider}, user_id={author.id}")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"OAuth callback error: {e}")
|
logger.error(f"OAuth callback error: {e!s}")
|
||||||
return JSONResponse({"error": "OAuth callback failed"}, status_code=500)
|
# В случае ошибки редиректим на фронтенд с ошибкой
|
||||||
|
fallback_redirect = request.query_params.get("redirect_uri", FRONTEND_URL)
|
||||||
|
return RedirectResponse(url=f"{fallback_redirect}?error=auth_failed")
|
||||||
|
|
||||||
|
|
||||||
async def _create_or_update_user(provider: str, profile: dict) -> Author:
|
async def _create_or_update_user(provider: str, profile: dict) -> Author:
|
||||||
|
|||||||
Reference in New Issue
Block a user