first commit
This commit is contained in:
332
lastochka-ui/AUTH.md
Normal file
332
lastochka-ui/AUTH.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# Аутентификация в Ласточке
|
||||
|
||||
## Обзор
|
||||
|
||||
Ласточка поддерживает два способа аутентификации:
|
||||
|
||||
1. **Классическая** — логин и пароль
|
||||
2. **По номеру телефона** — SMS-код подтверждения
|
||||
|
||||
## Регистрация по номеру телефона
|
||||
|
||||
### Процесс регистрации
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
| Ввод номера | -> | SMS с кодом | -> | Верификация |
|
||||
| телефона | | отправлено | | завершена |
|
||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
#### 1. Отправка SMS для регистрации
|
||||
|
||||
```typescript
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
|
||||
const { sendRegistrationSms } = useAuthStore()
|
||||
await sendRegistrationSms('+7 (999) 999-99-99')
|
||||
```
|
||||
|
||||
**Что происходит:**
|
||||
- Валидация номера телефона
|
||||
- Подключение к Tinode
|
||||
- Создание учётной записи с credential (номер телефона)
|
||||
- Отправка SMS кода подтверждения
|
||||
|
||||
#### 2. Проверка SMS кода
|
||||
|
||||
```typescript
|
||||
const { verifyRegistrationSms } = useAuthStore()
|
||||
await verifyRegistrationSms('123456', 'Имя пользователя')
|
||||
```
|
||||
|
||||
**Что происходит:**
|
||||
- Проверка кода подтверждения
|
||||
- Создание полноценного аккаунта
|
||||
- Автоматический вход в систему
|
||||
|
||||
### Утилиты для работы с телефоном
|
||||
|
||||
```typescript
|
||||
import {
|
||||
formatPhoneNumber, // Форматирование для отображения
|
||||
cleanPhoneNumber, // Очистка для отправки на сервер
|
||||
isValidPhoneNumber, // Валидация номера
|
||||
} from '@/lib/phone-utils'
|
||||
|
||||
// Примеры
|
||||
formatPhoneNumber('79999999999') // "+7 (999) 999-99-99"
|
||||
cleanPhoneNumber('+7 (999)...') // "79999999999"
|
||||
isValidPhoneNumber('+7 (999)...') // true
|
||||
```
|
||||
|
||||
## Вход по номеру телефона
|
||||
|
||||
### Процесс входа
|
||||
|
||||
```typescript
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
|
||||
const { loginByPhone, loginWithSmsCode } = useAuthStore()
|
||||
|
||||
// 1. Запрос SMS кода
|
||||
await loginByPhone('+7 (999) 999-99-99')
|
||||
|
||||
// 2. Ввод кода из SMS
|
||||
await loginWithSmsCode('123456')
|
||||
```
|
||||
|
||||
## Классическая аутентификация
|
||||
|
||||
### Вход по логину/паролю
|
||||
|
||||
```typescript
|
||||
const { login } = useAuthStore()
|
||||
await login('username', 'password')
|
||||
```
|
||||
|
||||
### Регистрация по логину/паролю
|
||||
|
||||
```typescript
|
||||
// Пока не реализовано через отдельный метод
|
||||
// Используйте login с новым логином
|
||||
```
|
||||
|
||||
## Компоненты
|
||||
|
||||
### LoginScreen.tsx
|
||||
|
||||
Основной компонент экрана входа/регистрации.
|
||||
|
||||
**Режимы работы:**
|
||||
- `phone-login` — вход по номеру телефона
|
||||
- `phone-register` — регистрация по номеру телефона
|
||||
- `login` — вход по логину/паролю
|
||||
- `register` — регистрация по логину/паролю
|
||||
|
||||
**Состояния:**
|
||||
- `verificationStep: 'none'` — ввод номера/логина
|
||||
- `verificationStep: 'sms-sent'` — ожидание SMS кода
|
||||
- `verificationStep: 'verified'` — успешная верификация
|
||||
|
||||
### SmsVerification.tsx
|
||||
|
||||
Компонент ввода SMS кода.
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface SmsVerificationProps {
|
||||
phone: string // Номер телефона для отображения
|
||||
onComplete: (code) => void // Callback при вводе кода
|
||||
onBack: () => void // Callback при возврате назад
|
||||
isLoading: boolean // Индикатор загрузки
|
||||
error: string | null // Сообщение об ошибке
|
||||
title?: string // Заголовок
|
||||
description?: string // Описание
|
||||
}
|
||||
```
|
||||
|
||||
**Функции:**
|
||||
- Автофокус на первом поле
|
||||
- Автопереход между полями
|
||||
- Поддержка вставки кода из буфера обмена
|
||||
- Обработка Backspace для возврата назад
|
||||
|
||||
## Store (auth.ts)
|
||||
|
||||
### Состояние
|
||||
|
||||
```typescript
|
||||
interface AuthState {
|
||||
isAuthenticated: boolean
|
||||
userId: string | null
|
||||
displayName: string | null
|
||||
isLoading: boolean
|
||||
error: string | null
|
||||
|
||||
// Для SMS-верификации
|
||||
phoneForVerification: string | null
|
||||
verificationStep: 'none' | 'sms-sent' | 'verified'
|
||||
|
||||
// Методы
|
||||
login: (login, password) => Promise<void>
|
||||
registerByPhone: (phone, displayName) => Promise<void>
|
||||
sendRegistrationSms: (phone) => Promise<void>
|
||||
verifyRegistrationSms: (code, displayName) => Promise<void>
|
||||
loginByPhone: (phone) => Promise<void>
|
||||
loginWithSmsCode: (code) => Promise<void>
|
||||
logout: () => Promise<void>
|
||||
tryAutoLogin: () => Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
## SMS-провайдеры
|
||||
|
||||
### Настройка
|
||||
|
||||
Для отправки SMS необходимо настроить SMS-провайдера на стороне сервера Tinode.
|
||||
|
||||
**Популярные провайдеры для РФ:**
|
||||
- SMS.ru
|
||||
- SMS Pilot
|
||||
- Prostor SMS
|
||||
- Telesign
|
||||
|
||||
### Конфигурация на сервере
|
||||
|
||||
В конфигурации сервера Tinode укажите:
|
||||
|
||||
```yaml
|
||||
sms:
|
||||
provider: "sms.ru"
|
||||
api_key: "your-api-key"
|
||||
sender: "Lastochka"
|
||||
```
|
||||
|
||||
## Безопасность
|
||||
|
||||
### Валидация номеров
|
||||
|
||||
- Проверка формата российского номера (+7 XXX XXX-XX-XX)
|
||||
- Очистка от спецсимволов перед отправкой
|
||||
- Проверка длины номера (11 цифр)
|
||||
|
||||
### Защита от злоупотреблений
|
||||
|
||||
- Rate limiting на отправку SMS (не чаще 1 раза в минуту)
|
||||
- Максимум 3 попытки ввода кода
|
||||
- Блокировка при множественных неудачных попытках
|
||||
|
||||
### Хранение токенов
|
||||
|
||||
- Токен сохраняется в localStorage
|
||||
- Токен автоматически обновляется при продлении сессии
|
||||
- При logout токен удаляется
|
||||
|
||||
## Пример использования
|
||||
|
||||
### Полная регистрация по номеру телефона
|
||||
|
||||
```typescript
|
||||
import { useState } from 'react'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
import { formatPhoneNumber, isValidPhoneNumber } from '@/lib/phone-utils'
|
||||
|
||||
function RegistrationForm() {
|
||||
const [phone, setPhone] = useState('')
|
||||
const [displayName, setDisplayName] = useState('')
|
||||
const [code, setCode] = useState('')
|
||||
|
||||
const { sendRegistrationSms, verifyRegistrationSms, isLoading, error } = useAuthStore()
|
||||
|
||||
const handleSendSms = async () => {
|
||||
if (!isValidPhoneNumber(phone)) return
|
||||
await sendRegistrationSms(phone)
|
||||
}
|
||||
|
||||
const handleVerify = async () => {
|
||||
await verifyRegistrationSms(code, displayName)
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input
|
||||
type="tel"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(formatPhoneNumber(e.target.value))}
|
||||
placeholder="+7 (999) 999-99-99"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={displayName}
|
||||
onChange={(e) => setDisplayName(e.target.value)}
|
||||
placeholder="Ваше имя"
|
||||
/>
|
||||
<button onClick={handleSendSms} disabled={isLoading}>
|
||||
Получить код
|
||||
</button>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
placeholder="Код из SMS"
|
||||
maxLength={6}
|
||||
/>
|
||||
<button onClick={handleVerify} disabled={isLoading}>
|
||||
Подтвердить
|
||||
</button>
|
||||
|
||||
{error && <p className="error">{error}</p>}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Интеграция с бэкендом
|
||||
|
||||
### Эндпоинты
|
||||
|
||||
Сервер должен поддерживать следующие эндпоинты:
|
||||
|
||||
1. **POST /v1/creds/tel** — запрос кода подтверждения
|
||||
2. **PUT /v1/creds/tel** — проверка кода подтверждения
|
||||
|
||||
### Формат запроса
|
||||
|
||||
```json
|
||||
// Запрос SMS кода
|
||||
{
|
||||
"tel": "79999999999",
|
||||
"op": "add"
|
||||
}
|
||||
|
||||
// Проверка кода
|
||||
{
|
||||
"tel": "79999999999",
|
||||
"val": "123456",
|
||||
"op": "add"
|
||||
}
|
||||
```
|
||||
|
||||
### Формат ответа
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"text": "OK",
|
||||
"params": {
|
||||
"cred": "tel:79999999999"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Тестирование
|
||||
|
||||
### Тестовые номера
|
||||
|
||||
Для тестирования без реальной отправки SMS:
|
||||
|
||||
```
|
||||
+7 (999) 000-00-01 → код: 123456
|
||||
+7 (999) 000-00-02 → код: 654321
|
||||
```
|
||||
|
||||
### Mock SMS-провайдера
|
||||
|
||||
```typescript
|
||||
// В разработке можно использовать mock
|
||||
export async function sendSmsCode(phone: string) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(`SMS код для ${phone}: 123456`)
|
||||
return { success: true }
|
||||
}
|
||||
// Реальная отправка SMS
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Ласточка** — народный мессенджер с открытым кодом 🕊️
|
||||
Reference in New Issue
Block a user