[0.9.28] - 2025-09-28
All checks were successful
Deploy on push / deploy (push) Successful in 2m46s

### 🍪 CRITICAL Cross-Origin Auth
- **🔧 SESSION_COOKIE_DOMAIN**: Добавлена поддержка поддоменов `.discours.io` для cross-origin cookies
- **🌐 Cross-Origin SSE**: Исправлена работа Server-Sent Events с httpOnly cookies между поддоменами
- **🔐 Unified Auth**: Унифицированы настройки cookies для OAuth, login, refresh, logout операций
- **📝 MyPy Compliance**: Исправлена типизация `SESSION_COOKIE_SAMESITE` с использованием `cast()`

### 🛠️ Technical Changes
- **settings.py**: Добавлен `SESSION_COOKIE_DOMAIN` с типобезопасной настройкой SameSite
- **auth/oauth.py**: Обновлены все `set_cookie` вызовы с `domain` параметром
- **auth/middleware.py**: Добавлена поддержка `SESSION_COOKIE_DOMAIN` в logout операциях
- **resolvers/auth.py**: Унифицированы cookie настройки в login/refresh/logout resolvers
- **auth/__init__.py**: Обновлены cookie операции с domain поддержкой

### 📚 Documentation
- **docs/auth/sse-httponly-integration.md**: Новая документация по SSE + httpOnly cookies интеграции
- **docs/auth/architecture.md**: Обновлены диаграммы для unified httpOnly cookie архитектуры

### 🎯 Impact
-  **GraphQL API** (`v3.discours.io`) теперь работает с httpOnly cookies cross-origin
-  **SSE сервер** (`connect.discours.io`) работает с теми же cookies
-  **Безопасность**: httpOnly cookies защищают от XSS атак
-  **UX**: Автоматическая аутентификация без управления токенами в JavaScript
This commit is contained in:
2025-09-28 13:06:03 +03:00
parent fb98a1c6c8
commit 752e2dcbdc
9 changed files with 255 additions and 223 deletions

View File

@@ -76,17 +76,33 @@ export const AuthProvider: Component<AuthProviderProps> = (props) => {
// Инициализация авторизации при монтировании
onMount(async () => {
console.log('[AuthProvider] Performing auth initialization...')
console.log('[AuthProvider] Checking localStorage token:', !!localStorage.getItem(AUTH_TOKEN_KEY))
console.log('[AuthProvider] Checking cookie token:', !!getAuthTokenFromCookie())
console.log('[AuthProvider] Checking CSRF token:', !!getCsrfTokenFromCookie())
// Небольшая задержка для завершения других инициализаций
await new Promise((resolve) => setTimeout(resolve, 100))
// Проверяем текущее состояние авторизации
const authStatus = checkAuthStatus()
console.log('[AuthProvider] Final auth status after check:', authStatus)
setIsAuthenticated(authStatus)
// 🍪 Для httpOnly cookies проверяем авторизацию через GraphQL запрос
try {
console.log('[AuthProvider] Checking authentication via GraphQL...')
// Делаем тестовый запрос для проверки авторизации
const result = await query<{ me: { id: string } | null }>(`${location.origin}/graphql`, `
query CheckAuth {
me {
id
name
email
}
}
`)
if (result?.me?.id) {
console.log('[AuthProvider] User authenticated via httpOnly cookie:', result.me.id)
setIsAuthenticated(true)
} else {
console.log('[AuthProvider] No authenticated user found')
setIsAuthenticated(false)
}
} catch (error) {
console.log('[AuthProvider] Authentication check failed:', error)
setIsAuthenticated(false)
}
console.log('[AuthProvider] Auth initialization complete, ready for requests')
setIsReady(true)
@@ -104,9 +120,8 @@ export const AuthProvider: Component<AuthProviderProps> = (props) => {
if (result?.login?.success) {
console.log('[AuthProvider] Login successful')
if (result.login.token) {
saveAuthToken(result.login.token)
}
// Backend автоматически установил session_token cookie при успешном login
console.log('[AuthProvider] Token saved in httpOnly cookie by backend')
setIsAuthenticated(true)
// Убираем window.location.href - пусть роутер сам обрабатывает навигацию
} else {

View File

@@ -2,7 +2,6 @@ export const ADMIN_LOGIN_MUTATION = `
mutation AdminLogin($email: String!, $password: String!) {
login(email: $email, password: $password) {
success
token
author {
id
name

View File

@@ -4,7 +4,8 @@
*/
// Экспортируем константы для использования в других модулях
export const AUTH_TOKEN_KEY = 'auth_token'
export const AUTH_TOKEN_KEY = 'auth_token' // localStorage fallback
export const SESSION_COOKIE_NAME = 'session_token' // ✅ httpOnly cookie от backend
export const CSRF_TOKEN_KEY = 'csrf_token'
/**
@@ -76,34 +77,28 @@ export function saveAuthToken(token: string): void {
}
/**
* Проверяет, авторизован ли пользователь
* @returns Статус авторизации
* Проверяет, авторизован ли пользователь через httpOnly cookie
* @returns Статус авторизации (всегда true для httpOnly - проверка на backend)
*/
export function checkAuthStatus(): boolean {
console.log('[Auth] Checking authentication status...')
// 💋 НЕ проверяем httpOnly cookie через JavaScript - он недоступен!
// httpOnly cookie автоматически отправляется браузером, но недоступен для чтения
// 🍪 Админка использует httpOnly cookies - токен недоступен JavaScript!
// Браузер автоматически отправляет session_token cookie с каждым запросом
// Окончательная проверка авторизации происходит на backend через GraphQL
// Проверяем наличие токена в localStorage
// Проверяем localStorage только как fallback для старых сессий
const localToken = localStorage.getItem(AUTH_TOKEN_KEY)
const hasLocalToken = !!localToken && localToken.length > 10
// 💋 Для httpOnly cookie полагаемся на backend проверку
// Если нет токена в localStorage, считаем что пользователь может быть авторизован через httpOnly cookie
// Окончательная проверка произойдет при первом GraphQL запросе
const isAuth = hasLocalToken
console.log(`[Auth] Local token: ${hasLocalToken ? 'present' : 'missing'}`)
console.log(
`[Auth] Authentication status: ${isAuth ? 'authenticated via localStorage' : 'unknown (may be authenticated via httpOnly cookie)'}`
)
// Дополнительное логирование для диагностики
if (localToken) {
console.log(`[Auth] Local token length: ${localToken.length}`)
console.log(`[Auth] Local token preview: ${localToken.substring(0, 20)}...`)
if (hasLocalToken) {
console.log('[Auth] Found legacy token in localStorage - will be migrated to httpOnly cookie')
}
return isAuth
// ✅ Для httpOnly cookie всегда возвращаем true
// Реальная проверка авторизации произойдет при первом GraphQL запросе
// Если cookie недействителен, backend вернет ошибку авторизации
console.log('[Auth] Using httpOnly cookie authentication - status will be verified by backend')
return true // ✅ Полагаемся на httpOnly cookie + backend проверку
}