[0.9.28] - OAuth/Auth with httpOnly cookie
All checks were successful
Deploy on push / deploy (push) Successful in 4m32s

This commit is contained in:
2025-09-28 12:22:37 +03:00
parent 6451ba7de5
commit fb98a1c6c8
27 changed files with 1449 additions and 2147 deletions

View File

@@ -2,7 +2,6 @@
Единый middleware для обработки авторизации в GraphQL запросах
"""
import json
import time
from collections.abc import Awaitable, MutableMapping
from typing import Any, Callable
@@ -21,8 +20,8 @@ from settings import (
ADMIN_EMAILS as ADMIN_EMAILS_LIST,
)
from settings import (
SESSION_COOKIE_DOMAIN,
SESSION_COOKIE_HTTPONLY,
SESSION_COOKIE_MAX_AGE,
SESSION_COOKIE_NAME,
SESSION_COOKIE_SAMESITE,
SESSION_COOKIE_SECURE,
@@ -294,34 +293,12 @@ class AuthMiddleware:
logger.debug(f"[middleware] Проверяем cookies: {cookies[:100]}...")
logger.debug(f"[middleware] Ищем cookie с именем: '{SESSION_COOKIE_NAME}'")
# 🔍 Дополнительная диагностика для отладки
# 🔍 Диагностика cookies (только для debug уровня)
if not cookies:
logger.warning("[middleware] 🚨 ПРОБЛЕМА: Cookie заголовок полностью отсутствует!")
logger.warning(f"[middleware] 🔍 Все заголовки: {list(headers.keys())}")
# Проверяем, есть ли активные сессии для этого пользователя
try:
session_keys = await redis_adapter.keys("session:*")
if session_keys:
logger.warning(
f"[middleware] 🔍 В Redis найдено {len(session_keys)} активных сессий, но cookie не передается!"
)
# Показываем первые 3 сессии для диагностики
for session_key in session_keys[:3]:
try:
session_data = await redis_adapter.hgetall(session_key)
if session_data:
user_id = (
session_key.decode("utf-8").split(":")[1]
if isinstance(session_key, bytes)
else session_key.split(":")[1]
)
logger.warning(
f"[middleware] 🔍 Активная сессия для user_id={user_id}: {session_key}"
)
except Exception as e:
logger.debug(f"[middleware] Ошибка чтения сессии {session_key}: {e}")
except Exception as e:
logger.debug(f"[middleware] Ошибка проверки сессий: {e}")
logger.debug("[middleware] Cookie заголовок отсутствует")
logger.debug(f"[middleware] Доступные заголовки: {list(headers.keys())}")
# 💋 OAuth не использует cookies - это нормальное поведение
logger.debug("[middleware] OAuth система работает без cookies - токены передаются через заголовки")
cookie_items = cookies.split(";")
found_cookies = []
@@ -472,23 +449,7 @@ class AuthMiddleware:
"""
# Проверяем, является ли result уже объектом Response
if isinstance(result, Response):
response = result
# Пытаемся получить данные из response для проверки логина/логаута
result_data = {}
if isinstance(result, JSONResponse):
try:
body_content = result.body
if isinstance(body_content, bytes | memoryview):
body_text = bytes(body_content).decode("utf-8")
result_data = json.loads(body_text)
else:
result_data = json.loads(str(body_content))
except Exception as e:
logger.error(f"[process_result] Не удалось извлечь данные из JSONResponse: {e!s}")
else:
response = JSONResponse(result)
result_data = result
response = result if isinstance(result, Response) else JSONResponse(result)
# Проверяем, был ли токен в запросе или ответе
if request.method == "POST":
@@ -496,55 +457,13 @@ class AuthMiddleware:
data = await request.json()
op_name = data.get("operationName", "").lower()
# Если это операция логина или обновления токена, и в ответе есть токен
if op_name in ["login", "refreshtoken"]:
token = None
# Пытаемся извлечь токен из данных ответа
if result_data and isinstance(result_data, dict):
data_obj = result_data.get("data", {})
if isinstance(data_obj, dict) and op_name in data_obj:
op_result = data_obj.get(op_name, {})
if isinstance(op_result, dict) and "token" in op_result:
token = op_result.get("token")
if token:
# Устанавливаем cookie с токеном
response.set_cookie(
key=SESSION_COOKIE_NAME,
value=token,
httponly=SESSION_COOKIE_HTTPONLY,
secure=SESSION_COOKIE_SECURE,
samesite=SESSION_COOKIE_SAMESITE,
max_age=SESSION_COOKIE_MAX_AGE,
)
logger.debug(
f"[graphql_handler] Установлена cookie {SESSION_COOKIE_NAME} для операции {op_name}"
)
# Если это операция getSession и в ответе есть токен, устанавливаем cookie
elif op_name == "getsession":
token = None
# Пытаемся извлечь токен из данных ответа
if result_data and isinstance(result_data, dict):
data_obj = result_data.get("data", {})
if isinstance(data_obj, dict) and "getSession" in data_obj:
op_result = data_obj.get("getSession", {})
if isinstance(op_result, dict) and "token" in op_result and op_result.get("success"):
token = op_result.get("token")
if token:
# Устанавливаем cookie с токеном для поддержания сессии
response.set_cookie(
key=SESSION_COOKIE_NAME,
value=token,
httponly=SESSION_COOKIE_HTTPONLY,
secure=SESSION_COOKIE_SECURE,
samesite=SESSION_COOKIE_SAMESITE,
max_age=SESSION_COOKIE_MAX_AGE,
)
logger.debug(
f"[graphql_handler] Установлена cookie {SESSION_COOKIE_NAME} для операции {op_name}"
)
# 💋 OAuth НЕ использует cookies - токены передаются только через заголовки/localStorage
# Убираем автоматическую установку cookies для login/refreshtoken/getSession
if op_name in ["login", "refreshtoken", "getsession"]:
logger.debug(f"[graphql_handler] Операция {op_name}: токены передаются БЕЗ cookies")
logger.debug(
"[graphql_handler] Фронтенд должен извлечь токен из ответа и управлять им самостоятельно"
)
# Если это операция logout, удаляем cookie
elif op_name == "logout":
@@ -552,7 +471,10 @@ class AuthMiddleware:
key=SESSION_COOKIE_NAME,
secure=SESSION_COOKIE_SECURE,
httponly=SESSION_COOKIE_HTTPONLY,
samesite=SESSION_COOKIE_SAMESITE,
samesite=SESSION_COOKIE_SAMESITE
if SESSION_COOKIE_SAMESITE in ["strict", "lax", "none"]
else "none",
domain=SESSION_COOKIE_DOMAIN, # ✅ КРИТИЧНО: тот же domain что при установке
)
logger.debug(f"[graphql_handler] Удалена cookie {SESSION_COOKIE_NAME} для операции {op_name}")
except Exception as e: