This commit is contained in:
@@ -624,20 +624,54 @@ 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)
|
||||||
|
|
||||||
# 🔍 Сохраняем redirect_uri из Referer header для testing.discours.io
|
# 🎯 Получаем redirect_uri из query параметра (фронтенд должен передавать явно)
|
||||||
# Приоритет: query параметр → path параметр → Referer header → FRONTEND_URL
|
explicit_redirect_uri = request.query_params.get("redirect_uri")
|
||||||
final_redirect_uri = (
|
|
||||||
request.query_params.get("redirect_uri")
|
if explicit_redirect_uri:
|
||||||
or request.path_params.get("redirect_uri")
|
# Декодируем если URL-encoded
|
||||||
or request.headers.get("referer")
|
from urllib.parse import unquote
|
||||||
or FRONTEND_URL
|
|
||||||
)
|
if "%3A" in explicit_redirect_uri or "%2F" in explicit_redirect_uri:
|
||||||
logger.info(f"🎯 Final redirect URI: '{final_redirect_uri}' (from referer: {request.headers.get('referer')})")
|
explicit_redirect_uri = unquote(explicit_redirect_uri)
|
||||||
|
|
||||||
|
# Если это /oauth, меняем на /settings
|
||||||
|
if "/oauth" in explicit_redirect_uri:
|
||||||
|
from urllib.parse import urlparse, urlunparse
|
||||||
|
|
||||||
|
parsed = urlparse(explicit_redirect_uri)
|
||||||
|
explicit_redirect_uri = urlunparse(
|
||||||
|
(parsed.scheme, parsed.netloc, "/settings", parsed.params, "", parsed.fragment)
|
||||||
|
)
|
||||||
|
logger.info(f"🔧 Changed /oauth redirect to /settings: {explicit_redirect_uri}")
|
||||||
|
|
||||||
|
final_redirect_uri = explicit_redirect_uri
|
||||||
|
else:
|
||||||
|
# Fallback на настройки профиля
|
||||||
|
final_redirect_uri = FRONTEND_URL.rstrip("/") + "/settings"
|
||||||
|
|
||||||
|
logger.info(f"🎯 Final redirect URI: '{final_redirect_uri}'")
|
||||||
|
|
||||||
|
# 🔑 Создаем state с redirect URL и случайным значением для безопасности
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
|
||||||
|
state_data = {
|
||||||
|
"redirect_uri": final_redirect_uri,
|
||||||
|
"random": token_urlsafe(16), # Для CSRF protection
|
||||||
|
"timestamp": int(time.time()),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Кодируем state в base64 для передачи в URL
|
||||||
|
state_json = json.dumps(state_data)
|
||||||
|
state = base64.urlsafe_b64encode(state_json.encode()).decode().rstrip("=")
|
||||||
|
|
||||||
|
logger.info(f"🔑 Created state with redirect_uri: {final_redirect_uri}")
|
||||||
|
|
||||||
oauth_data = {
|
oauth_data = {
|
||||||
"code_verifier": code_verifier,
|
"code_verifier": code_verifier,
|
||||||
"provider": provider,
|
"provider": provider,
|
||||||
"redirect_uri": final_redirect_uri,
|
"redirect_uri": final_redirect_uri,
|
||||||
|
"state_data": state_data, # Сохраняем для callback
|
||||||
"created_at": int(time.time()),
|
"created_at": int(time.time()),
|
||||||
}
|
}
|
||||||
await store_oauth_state(state, oauth_data)
|
await store_oauth_state(state, oauth_data)
|
||||||
@@ -918,10 +952,14 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon
|
|||||||
else f"🔧 Session token: {session_token}"
|
else f"🔧 Session token: {session_token}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Получаем redirect_uri из OAuth данных
|
# 🔑 Получаем redirect_uri из state данных (новый подход)
|
||||||
redirect_uri = oauth_data.get("redirect_uri", FRONTEND_URL)
|
state_data = oauth_data.get("state_data", {})
|
||||||
|
redirect_uri = state_data.get("redirect_uri") or oauth_data.get("redirect_uri", FRONTEND_URL)
|
||||||
|
|
||||||
if not isinstance(redirect_uri, str) or not redirect_uri:
|
if not isinstance(redirect_uri, str) or not redirect_uri:
|
||||||
redirect_uri = FRONTEND_URL
|
redirect_uri = FRONTEND_URL.rstrip("/") + "/settings"
|
||||||
|
|
||||||
|
logger.info(f"🔑 Using redirect_uri from state: {redirect_uri}")
|
||||||
|
|
||||||
# 🎯 Стандартный OAuth flow: токен в URL для фронтенда
|
# 🎯 Стандартный OAuth flow: токен в URL для фронтенда
|
||||||
from urllib.parse import parse_qs, unquote, urlencode, urlparse, urlunparse
|
from urllib.parse import parse_qs, unquote, urlencode, urlparse, urlunparse
|
||||||
@@ -999,11 +1037,11 @@ async def oauth_callback_http(request: Request) -> JSONResponse | RedirectRespon
|
|||||||
if not isinstance(fallback_redirect, str):
|
if not isinstance(fallback_redirect, str):
|
||||||
fallback_redirect = FRONTEND_URL
|
fallback_redirect = FRONTEND_URL
|
||||||
|
|
||||||
# Для testing.discours.io используем главную страницу (так как /oauth редиректит на /)
|
# Для testing.discours.io используем страницу профиля для ошибок
|
||||||
if "testing.discours.io" in fallback_redirect:
|
if "testing.discours.io" in fallback_redirect:
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
error_url = f"https://testing.discours.io/?error=auth_failed&provider={provider}&redirect_url={quote(fallback_redirect)}"
|
error_url = f"https://testing.discours.io/settings?error=auth_failed&provider={provider}&redirect_url={quote(fallback_redirect)}"
|
||||||
else:
|
else:
|
||||||
error_url = f"{fallback_redirect}?error=auth_failed&provider={provider}"
|
error_url = f"{fallback_redirect}?error=auth_failed&provider={provider}"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user