11 KiB
📋 ПЛАН УЛУЧШЕНИЯ АРХИТЕКТУРЫ АВТОРИЗАЦИИ И БЕЗОПАСНОСТИ SFERA
Дата создания: 2025-09-18
Автор: Claude AI + Вероника Смирнова
Статус: В разработке
📊 Анализ текущего состояния
✅ Что уже реализовано и работает:
-
Безопасные ID (CUID)
- Все модели используют
@default(cuid())
- Невозможно угадать ID других организаций
- Пример:
cmfpe46iv0001y51d87f4vy2n
- Все модели используют
-
Защита типов кабинетов
useRoleGuard
на 49 страницах- Автоматический редирект при попытке доступа к чужому типу кабинета
- Селлер не может зайти в кабинет Поставщика
-
Изоляция данных на уровне API
- Все резолверы фильтруют по
organizationId
- Каждая организация видит только свои данные
- Проверки в каждом GraphQL резолвере
- Все резолверы фильтруют по
-
Структура роутинга
- Четкое разделение:
/seller/*
,/fulfillment/*
,/logistics/*
,/wholesale/*
- DashboardHome автоматически направляет в нужный кабинет
- Четкое разделение:
🚨 Выявленные проблемы:
-
Отсутствие глобального состояния (AuthContext)
- Каждый компонент создает свой экземпляр useAuth
- Состояние не синхронизируется между компонентами
- Sidebar не видит данные пользователя после авторизации
-
Дублирование кода безопасности
- Одинаковые проверки в 50+ резолверах
- Риск забыть добавить проверку в новый резолвер
- Сложность поддержки
-
Отсутствие персистентности
- При обновлении страницы состояние теряется
- Повторные запросы GET_ME
- Плохой UX при F5
-
Нет серверной проверки роутов
- Проверки только на уровне компонентов
- Страница начинает загружаться до проверки прав
🎯 План улучшений
ФАЗА 1: Критические исправления
1. Реализация AuthContext [🔴 КРИТИЧНО]
Срок: 2-3 часа
Приоритет: Максимальный
Влияние: Решает проблему с sidebar и синхронизацией состояния
План реализации:
- Создать
/src/contexts/AuthContext.tsx
:
import { createContext, useContext, useState, useEffect } from 'react'
import { useApolloClient } from '@apollo/client'
interface AuthContextType {
user: User | null
isAuthenticated: boolean
isLoading: boolean
checkAuth: () => Promise<void>
login: (phone: string, code: string) => Promise<void>
logout: () => void
}
const AuthContext = createContext<AuthContextType | null>(null)
export function AuthProvider({ children }) {
// Перенести всю логику из текущего useAuth
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState(true)
// ... вся логика авторизации
return (
<AuthContext.Provider value={{ user, ... }}>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
if (!context) {
throw new Error('useAuth must be used within AuthProvider')
}
return context
}
- Обновить
/src/app/providers.tsx
:
export function Providers({ children }) {
return (
<ApolloProvider client={apolloClient}>
<AuthProvider>
<SidebarProvider>
{children}
</SidebarProvider>
</AuthProvider>
</ApolloProvider>
)
}
- Убрать временные решения:
- Удалить
useQuery(GET_ME)
из AppShell - Убрать передачу user через props
- Вернуть оригинальную логику компонентов
- Удалить
2. GraphQL Middleware для безопасности [🟡 ВАЖНО]
Срок: 1 день
Приоритет: Высокий
Влияние: Централизованная безопасность, чистый код
План реализации:
- Создать
/src/graphql/middleware/organizationAccess.ts
:
export const organizationAccessMiddleware = async (
resolve,
parent,
args,
context,
info
) => {
// Пропускаем публичные операции
const PUBLIC_OPERATIONS = ['login', 'sendSmsCode', 'verifySmsCode']
if (PUBLIC_OPERATIONS.includes(info.fieldName)) {
return resolve(parent, args, context, info)
}
// Проверяем авторизацию
if (!context.user?.organizationId) {
throw new GraphQLError('Unauthorized', {
extensions: { code: 'UNAUTHENTICATED' }
})
}
// Автоматически добавляем organizationId к запросам
if (args.where && typeof args.where === 'object') {
args.where.organizationId = context.user.organizationId
}
// Логирование для безопасности
console.log(`[${context.user.organizationId}] ${info.fieldName}`, {
userId: context.user.id,
operation: info.fieldName,
timestamp: new Date().toISOString()
})
return resolve(parent, args, context, info)
}
- Применить middleware ко всем резолверам
- Удалить дублированные проверки из резолверов
ФАЗА 2: Важные улучшения
3. Персистентность состояния [🟡 ВАЖНО]
Срок: 0.5 дня
Приоритет: Средний
Влияние: Улучшение UX при обновлении страницы
План реализации:
- Добавить в AuthContext сохранение состояния:
// При успешной авторизации
const encryptedUser = encrypt(JSON.stringify(user))
localStorage.setItem('auth:user', encryptedUser)
// При инициализации
const savedUser = localStorage.getItem('auth:user')
if (savedUser) {
const user = JSON.parse(decrypt(savedUser))
// Проверить валидность токена
}
- Реализовать refresh token механизм
- Очистка при logout
4. Next.js Middleware [🟢 ЖЕЛАТЕЛЬНО]
Срок: 1 день
Приоритет: Средний
Влияние: Производительность, серверная защита
План реализации:
- Создать
/src/middleware.ts
:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { jwtVerify } from 'jose'
export async function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')?.value
const pathname = request.nextUrl.pathname
// Публичные маршруты
const publicPaths = ['/', '/login', '/register']
if (publicPaths.includes(pathname)) return
// Проверка токена
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
try {
const { payload } = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET!))
const orgType = payload.organization?.type
// Проверка доступа к кабинету
if (pathname.startsWith('/seller') && orgType !== 'SELLER') {
return NextResponse.redirect(new URL('/dashboard', request.url))
}
// ... остальные проверки
} catch (error) {
return NextResponse.redirect(new URL('/login', request.url))
}
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
}
ФАЗА 3: Оптимизации
5. Улучшение загрузки [🔵 ОПЦИОНАЛЬНО]
Срок: 1 неделя
Приоритет: Низкий
Влияние: Улучшение UX
- Skeleton screens для всех страниц
- Prefetch критических данных
- Оптимистичные обновления UI
- PWA функциональность
6. Расширенная система прав [🔵 ОПЦИОНАЛЬНО]
Срок: 2 недели
Приоритет: Низкий
Влияние: Enterprise функциональность
- Роли: Admin, Manager, Viewer
- Permissions: canEdit, canDelete, canApprove
- Аудит всех действий
- Делегирование доступа
📈 Ожидаемые результаты
После ФАЗЫ 1:
- ✅ Sidebar работает стабильно
- ✅ Состояние синхронизировано между компонентами
- ✅ Единая точка контроля безопасности
- ✅ Чистый, поддерживаемый код
После ФАЗЫ 2:
- ✅ Мгновенная загрузка после F5
- ✅ Защита на уровне сервера
- ✅ Автоматическое управление сессией
После ФАЗЫ 3:
- ✅ Премиум UX
- ✅ Enterprise-ready система
- ✅ Готовность к масштабированию
🚀 Рекомендуемый порядок выполнения
- Неделя 1: AuthContext (решает критическую проблему)
- Неделя 2: GraphQL Middleware + Персистентность
- Месяц 2: Next.js Middleware + Оптимизации по необходимости
📝 Критерии успеха
- Sidebar отображается сразу после авторизации
- Нет дублирования состояния между компонентами
- Единая проверка безопасности для всех API запросов
- Состояние сохраняется при обновлении страницы
- Производительность не ухудшилась
- Код стал чище и проще в поддержке
Документ будет обновляться по мере реализации плана