
• Полная миграция 64 компонентов с useAuth на AuthContext • Исправлена race condition в SMS регистрации • Улучшена SSR совместимость с таймаутами • Удалена дублирующая система регистрации • Обновлена документация архитектуры аутентификации Технические изменения: - AuthContext.tsx: централизованная система состояния - auth-flow.tsx: убрана агрессивная логика logout - confirmation-step.tsx: исправлена передача телефона - page.tsx: добавлена синхронизация состояния - 64 файла: миграция useAuth → useAuthContext 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
250 lines
9.5 KiB
Markdown
250 lines
9.5 KiB
Markdown
# 🔍 ПЛАН ИСПРАВЛЕНИЯ SMS РЕГИСТРАЦИИ - ДЕТАЛЬНЫЙ АНАЛИЗ ПРОБЛЕМЫ
|
||
|
||
> **Дата:** 2025-09-18
|
||
> **Проблема:** После SMS верификации пользователь перебрасывается на `/` вместо продолжения регистрации
|
||
|
||
---
|
||
|
||
## 🚨 ОПИСАНИЕ ПРОБЛЕМЫ
|
||
|
||
### 📱 Что должно происходить (правильная логика SFERA):
|
||
|
||
**ЕДИНЫЙ FLOW авторизации/регистрации:**
|
||
1. Пользователь вводит номер телефона → SMS отправляется
|
||
2. Пользователь вводит SMS код → код верифицируется
|
||
3. **Определяется сценарий:**
|
||
- **СЦЕНАРИЙ A:** Пользователь существует + есть организация → **Dashboard**
|
||
- **СЦЕНАРИЙ B:** Пользователь существует + НЕТ организации → **Cabinet-select** (выбор типа кабинета)
|
||
- **СЦЕНАРИЙ C:** Новый пользователь → **Cabinet-select** (выбор типа кабинета)
|
||
|
||
### ❌ Что происходит сейчас (баг):
|
||
|
||
```
|
||
1. Пользователь вводит телефон → ✅ SMS отправляется
|
||
2. Пользователь вводит SMS код → ✅ код верифицируется успешно
|
||
3. ❌ Пользователь перебрасывается на главную страницу `/`
|
||
4. ❌ AuthFlow сбрасывается на step: "phone"
|
||
5. ❌ Пользователь вынужден начинать регистрацию заново
|
||
```
|
||
|
||
### 🔍 Логи ошибки:
|
||
|
||
```javascript
|
||
// SMS verification прошла успешно:
|
||
🌐 GraphQL REQUEST: { operationName: 'VerifySmsCode', variables: { phone: '76657584949', code: '1234' } }
|
||
POST /api/graphql 200 in 1295ms
|
||
|
||
// Но после этого:
|
||
AppShell state: { pathname: '/', isAuthenticated: false, hasUser: false }
|
||
🎢 AuthFlow - useEffect triggered with: { isAuthenticated: false, hasUser: false, currentStep: "phone" }
|
||
🎢 AuthFlow - User not authenticated, setting step to phone
|
||
```
|
||
|
||
**КЛЮЧЕВАЯ ПРОБЛЕМА:** `isAuthenticated: false` после успешной SMS верификации!
|
||
|
||
---
|
||
|
||
## 🔍 КОРНЕВАЯ ПРИЧИНА
|
||
|
||
### 🎯 Диагноз:
|
||
|
||
**AuthContext не обновляется после SMS верификации**, поэтому:
|
||
1. SMS верификация успешна → токен сохраняется в localStorage
|
||
2. НО AuthContext остается `isAuthenticated: false, user: null`
|
||
3. app/page.tsx видит неавторизованного пользователя → redirect на `/login`
|
||
4. AuthFlow инициализируется заново → step: "phone"
|
||
|
||
### 🔧 Предполагаемые причины:
|
||
|
||
**1. Timing issue в AuthContext:**
|
||
- SMS-step сохраняет токен через `verifySmsCode()`
|
||
- AuthContext не успевает обновить состояние
|
||
- useEffect в AuthFlow срабатывает с устаревшими данными
|
||
|
||
**2. Конфликт между экземплярами AuthFlow:**
|
||
- AuthGuard на `/dashboard` может создавать второй AuthFlow
|
||
- Два AuthFlow конфликтуют между собой
|
||
|
||
**3. Проблема в app/page.tsx:**
|
||
- Слишком быстро перенаправляет до обновления AuthContext
|
||
- Не учитывает `isLoading` состояние
|
||
|
||
---
|
||
|
||
## 📋 ПЛАН ИСПРАВЛЕНИЯ
|
||
|
||
### **PHASE 1: ДИАГНОСТИКА И ЛОГИРОВАНИЕ**
|
||
|
||
#### 1.1 Добавить детальное логирование в AuthContext
|
||
```typescript
|
||
// В verifySmsCode после успешной верификации:
|
||
console.warn('🔑 AuthContext - BEFORE setState:', {
|
||
isAuthenticated: this.isAuthenticated,
|
||
hasUser: !!this.user
|
||
})
|
||
|
||
setUser(result.user)
|
||
setIsAuthenticated(true)
|
||
|
||
console.warn('🔑 AuthContext - AFTER setState:', {
|
||
isAuthenticated: true,
|
||
hasUser: !!result.user,
|
||
userOrganization: result.user.organization
|
||
})
|
||
```
|
||
|
||
#### 1.2 Добавить логирование в AuthFlow useEffect
|
||
```typescript
|
||
useEffect(() => {
|
||
console.warn('🎢 AuthFlow - useEffect DETAILED:', {
|
||
isAuthenticated,
|
||
hasUser: !!user,
|
||
hasOrganization: !!user?.organization,
|
||
userFromContext: user,
|
||
currentStep: step,
|
||
timestamp: new Date().toISOString()
|
||
})
|
||
|
||
// Существующая логика...
|
||
}, [isAuthenticated, user])
|
||
```
|
||
|
||
#### 1.3 Добавить логирование в app/page.tsx
|
||
```typescript
|
||
useEffect(() => {
|
||
console.warn('📍 app/page.tsx - routing decision:', {
|
||
isLoading,
|
||
hasUser: !!user,
|
||
hasOrganization: !!user?.organization,
|
||
currentPath: window.location.pathname
|
||
})
|
||
|
||
// Существующая логика...
|
||
}, [router, user, isLoading])
|
||
```
|
||
|
||
### **PHASE 2: ИСПРАВЛЕНИЕ TIMING ISSUES**
|
||
|
||
#### 2.1 Исправить AuthContext - добавить промисы
|
||
```typescript
|
||
const verifySmsCode = async (phone: string, code: string) => {
|
||
// ... существующий код ...
|
||
|
||
if (result.success && result.token && result.user) {
|
||
setAuthToken(result.token)
|
||
setUserData(result.user)
|
||
|
||
// Синхронно обновляем состояние
|
||
setUser(result.user)
|
||
setIsAuthenticated(true)
|
||
|
||
// ЖДЕМ обновления Apollo Client
|
||
await refreshApolloClient()
|
||
|
||
// Возвращаем промис когда все готово
|
||
return new Promise(resolve => {
|
||
setTimeout(() => {
|
||
resolve({ success: true, message: result.message, user: result.user })
|
||
}, 100) // Небольшая задержка для обновления состояния
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2.2 Добавить isLoading в app/page.tsx
|
||
```typescript
|
||
const { user, isLoading, isAuthenticated } = useAuthContext()
|
||
|
||
useEffect(() => {
|
||
// КРИТИЧНО: ждем завершения всех проверок
|
||
if (isLoading) {
|
||
console.warn('📍 app/page.tsx - still loading, waiting...')
|
||
return
|
||
}
|
||
|
||
// Только после isLoading: false делаем redirect
|
||
if (user?.organization) {
|
||
router.replace('/dashboard')
|
||
} else if (isAuthenticated) {
|
||
router.replace('/login') // Продолжить на странице регистрации
|
||
} else {
|
||
router.replace('/login')
|
||
}
|
||
}, [router, user, isLoading, isAuthenticated])
|
||
```
|
||
|
||
### **PHASE 3: УСТРАНЕНИЕ КОНФЛИКТОВ**
|
||
|
||
#### 3.1 Исправить AuthGuard
|
||
```typescript
|
||
// НЕ создавать новый AuthFlow на dashboard
|
||
if (!isAuthenticated || (isAuthenticated && user && !user.organization)) {
|
||
// Перенаправить вместо показа AuthFlow
|
||
const router = useRouter()
|
||
useEffect(() => {
|
||
router.replace('/login')
|
||
}, [])
|
||
return <div>Перенаправление...</div>
|
||
}
|
||
```
|
||
|
||
#### 3.2 Убрать window.location.href из AuthFlow
|
||
```typescript
|
||
// ЗАМЕНИТЬ:
|
||
window.location.href = '/dashboard'
|
||
|
||
// НА:
|
||
router.push('/dashboard')
|
||
```
|
||
|
||
### **PHASE 4: ТЕСТИРОВАНИЕ**
|
||
|
||
#### 4.1 Тест-сценарии:
|
||
1. **Новый пользователь:** phone → sms → cabinet-select → inn/api → complete → dashboard
|
||
2. **Существующий без организации:** phone → sms → cabinet-select → inn/api → complete → dashboard
|
||
3. **Существующий с организацией:** phone → sms → dashboard
|
||
4. **Партнерские коды:** все сценарии с параметрами
|
||
|
||
#### 4.2 Проверки:
|
||
- ✅ Нет перебросов на `/`
|
||
- ✅ AuthFlow не сбрасывается на phone
|
||
- ✅ Состояние AuthContext корректное
|
||
- ✅ Нет создания второго AuthFlow
|
||
|
||
---
|
||
|
||
## 🎯 ОЖИДАЕМЫЙ РЕЗУЛЬТАТ
|
||
|
||
### ✅ После исправления:
|
||
|
||
```
|
||
1. Пользователь вводит телефон → SMS отправляется
|
||
2. Пользователь вводит SMS код → код верифицируется
|
||
3. AuthContext обновляется → isAuthenticated: true, user: {...}
|
||
4. AuthFlow определяет сценарий:
|
||
- СЦЕНАРИЙ A: user.organization есть → step: 'complete' → dashboard
|
||
- СЦЕНАРИЙ B/C: user.organization нет → step: 'cabinet-select'
|
||
5. Пользователь продолжает регистрацию БЕЗ сброса
|
||
```
|
||
|
||
### 📊 Логи после исправления:
|
||
|
||
```javascript
|
||
🔑 AuthContext - SMS verification successful
|
||
🔑 AuthContext - State updated: isAuthenticated=true, user=...
|
||
🎢 AuthFlow - useEffect: { isAuthenticated: true, hasUser: true, hasOrganization: false }
|
||
🎢 AuthFlow - Setting step to cabinet-select
|
||
✅ Пользователь видит страницу выбора типа кабинета
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 ГОТОВНОСТЬ К РЕАЛИЗАЦИИ
|
||
|
||
**✅ Проблема диагностирована:** Timing issue в AuthContext + конфликт AuthFlow экземпляров
|
||
|
||
**✅ План пошаговый:** 4 фазы с детальным логированием и тестированием
|
||
|
||
**✅ Минимальные изменения:** Фокус на исправлении, а не переписывании архитектуры
|
||
|
||
**🎯 Следующий шаг:** Начать PHASE 1 - добавить детальное логирование для подтверждения диагноза |