This commit is contained in:
parent
7c20415533
commit
cbecf13053
|
@ -315,42 +315,53 @@ async def oauth_callback(request: Any) -> JSONResponse | RedirectResponse:
|
||||||
Создает или обновляет пользователя и устанавливает сессионный токен.
|
Создает или обновляет пользователя и устанавливает сессионный токен.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Получаем state из query параметров
|
provider = request.path_params.get("provider")
|
||||||
state = request.query_params.get("state")
|
|
||||||
if not state:
|
|
||||||
return JSONResponse({"error": "State parameter missing"}, status_code=400)
|
|
||||||
|
|
||||||
# Получаем сохраненные данные OAuth из Redis
|
|
||||||
oauth_data = await get_oauth_state(state)
|
|
||||||
if not oauth_data:
|
|
||||||
return JSONResponse({"error": "Invalid or expired OAuth state"}, status_code=400)
|
|
||||||
|
|
||||||
provider = oauth_data.get("provider")
|
|
||||||
code_verifier = oauth_data.get("code_verifier")
|
|
||||||
stored_redirect_uri = oauth_data.get("redirect_uri", FRONTEND_URL)
|
|
||||||
|
|
||||||
if not provider:
|
if not provider:
|
||||||
return JSONResponse({"error": "No active OAuth session"}, status_code=400)
|
return JSONResponse({"error": "Provider not specified"}, status_code=400)
|
||||||
|
|
||||||
|
# Получаем OAuth клиента
|
||||||
client = oauth.create_client(provider)
|
client = oauth.create_client(provider)
|
||||||
if not client:
|
if not client:
|
||||||
return JSONResponse({"error": "Provider not configured"}, status_code=400)
|
return JSONResponse({"error": "Invalid provider"}, status_code=400)
|
||||||
|
|
||||||
# Получаем токен с PKCE verifier
|
# Получаем токен
|
||||||
token = await client.authorize_access_token(request, code_verifier=code_verifier)
|
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:
|
||||||
|
return JSONResponse({"error": "Failed to get user profile"}, status_code=400)
|
||||||
|
|
||||||
# Создаем или обновляем пользователя используя 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,
|
||||||
|
device_info={
|
||||||
|
"user_agent": request.headers.get("user-agent"),
|
||||||
|
"ip": request.client.host if hasattr(request, "client") else None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
# Формируем URL для редиректа с токеном
|
# Получаем state из Redis для редиректа
|
||||||
redirect_url = f"{stored_redirect_uri}?state={state}&access_token={session_token}"
|
state = request.query_params.get("state")
|
||||||
response = RedirectResponse(url=redirect_url)
|
state_data = await get_oauth_state(state) if state else None
|
||||||
|
redirect_uri = state_data.get("redirect_uri") if state_data else FRONTEND_URL
|
||||||
|
|
||||||
|
# Создаем ответ с редиректом
|
||||||
|
response = RedirectResponse(url=redirect_uri)
|
||||||
|
|
||||||
|
# Устанавливаем cookie с сессией
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
SESSION_COOKIE_NAME,
|
SESSION_COOKIE_NAME,
|
||||||
session_token,
|
session_token,
|
||||||
|
@ -358,14 +369,17 @@ async def oauth_callback(request: Any) -> JSONResponse | RedirectResponse:
|
||||||
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!s}")
|
logger.error(f"OAuth callback error: {e!s}")
|
||||||
# В случае ошибки редиректим на фронтенд с ошибкой
|
# В случае ошибки редиректим на фронтенд с ошибкой
|
||||||
fallback_redirect = request.query_params.get("redirect_uri", FRONTEND_URL)
|
fallback_redirect = request.query_params.get("redirect_uri", FRONTEND_URL)
|
||||||
return RedirectResponse(url=f"{fallback_redirect}?error=oauth_failed&message={e!s}")
|
return RedirectResponse(url=f"{fallback_redirect}?error=auth_failed")
|
||||||
|
|
||||||
|
|
||||||
async def store_oauth_state(state: str, data: dict) -> None:
|
async def store_oauth_state(state: str, data: dict) -> None:
|
||||||
|
|
|
@ -236,8 +236,8 @@ class SessionTokenManager(BaseTokenManager):
|
||||||
|
|
||||||
logger.debug(f"Проверка сессии для токена: {token[:20]}...")
|
logger.debug(f"Проверка сессии для токена: {token[:20]}...")
|
||||||
|
|
||||||
# Декодируем токен для получения payload
|
|
||||||
try:
|
try:
|
||||||
|
# Декодируем токен для получения payload
|
||||||
payload = JWTCodec.decode(token)
|
payload = JWTCodec.decode(token)
|
||||||
if not payload:
|
if not payload:
|
||||||
logger.error("Не удалось декодировать токен")
|
logger.error("Не удалось декодировать токен")
|
||||||
|
@ -248,18 +248,20 @@ class SessionTokenManager(BaseTokenManager):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
logger.debug(f"Успешно декодирован токен, user_id={payload.user_id}")
|
logger.debug(f"Успешно декодирован токен, user_id={payload.user_id}")
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Ошибка при декодировании токена: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Проверяем валидность токена
|
# Проверяем наличие сессии в Redis
|
||||||
try:
|
token_key = self._make_token_key("session", str(payload.user_id), token)
|
||||||
valid, error = await self.validate_session_token(token)
|
session_exists = await redis_adapter.exists(token_key)
|
||||||
if valid:
|
|
||||||
logger.debug(f"Сессия найдена для пользователя {payload.user_id}")
|
if not session_exists:
|
||||||
return payload
|
logger.warning(f"Сессия не найдена в Redis для user_id={payload.user_id}")
|
||||||
logger.warning(f"Сессия не найдена: {payload.user_id}, ошибка: {error}")
|
return None
|
||||||
return None
|
|
||||||
|
# Обновляем last_activity
|
||||||
|
await redis_adapter.hset(token_key, "last_activity", str(int(time.time())))
|
||||||
|
|
||||||
|
return payload
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при валидации сессии: {e}")
|
logger.error(f"Ошибка при проверке сессии: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
Loading…
Reference in New Issue
Block a user