← Назад к вопросам

Спроектируй взаимодействие с API при двухфакторной аутентификации

1.0 Junior🔥 71 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектура двухфакторной аутентификации

Двухфакторная аутентификация (2FA) требует правильного управления состоянием между этапами верификации. Вот как спроектировать это взаимодействие:

Типы 2FA методов

В современных приложениях используются несколько подходов:

  • SMS-коды (отправка кода на номер)
  • TOTP (Time-based One-Time Password, как Google Authenticator)
  • Email подтверждение
  • Биометрия (Face ID, Touch ID)

API endpoints для 2FA

Нужны отдельные endpoints для каждого этапа:

// 1. Инициализация 2FA после первичной аутентификации
POST /api/v1/auth/verify-2fa/start
Body: { userId, method: "sms" | "totp" | "email" }
Response: { sessionId, expiresIn, retriesLeft }

// 2. Отправка/получение кода подтверждения
POST /api/v1/auth/verify-2fa/send
Body: { sessionId }
Response: { sent: true, expiresIn }

// 3. Проверка кода подтверждения
POST /api/v1/auth/verify-2fa/verify
Body: { sessionId, code }
Response: { token, refreshToken, user }

// 4. Запасные коды (backup codes) для восстановления
GET /api/v1/auth/backup-codes
POST /api/v1/auth/backup-codes/use

Управление состоянием на фронтенде

Используй состояние для отслеживания этапа аутентификации:

const [authState, setAuthState] = useState({
  stage: "login", // login -> 2fa -> authenticated
  sessionId: null,
  method: null,
  expiresAt: null,
  attemptsLeft: 3,
  error: null,
});

Безопасность и обработка ошибок

Важные моменты:

  • Session timeout: код верификации должен иметь короткий TTL (60-300 сек)
  • Rate limiting: ограничить попытки (макс 3-5 попыток)
  • Резервные коды: всегда предоставляй способ восстановления
  • Нет хранения кодов: никогда не храни коды подтверждения в localStorage
// Обработка ошибок при верификации
const verify2FA = async (code) => {
  try {
    const response = await api.post("/auth/verify-2fa/verify", {
      sessionId: authState.sessionId,
      code: code.trim(),
    });
    
    setAuthState(prev => ({
      ...prev,
      stage: "authenticated",
    }));
    
    // Сохраняй токен в httpOnly cookie (самый безопасный способ)
    localStorage.setItem("userId", response.user.id);
  } catch (error) {
    if (error.response?.status === 429) {
      setAuthState(prev => ({
        ...prev,
        error: "Слишком много попыток. Попробуй позже.",
      }));
    } else if (error.response?.data?.message === "Invalid code") {
      setAuthState(prev => ({
        ...prev,
        attemptsLeft: error.response.data.attemptsLeft,
        error: `Неверный код. Осталось ${error.response.data.attemptsLeft} попыток`,
      }));
    }
  }
};

UX компонент для ввода кода

Лучшая практика - использовать отдельные поля для каждой цифры:

export function TwoFactorVerification() {
  const [codes, setCodes] = useState(["", "", "", "", "", ""]);
  
  const handleCodeChange = (index, value) => {
    const newCodes = [...codes];
    newCodes[index] = value.slice(0, 1); // только одна цифра
    setCodes(newCodes);
    
    // Автофокус на следующее поле
    if (value && index < 5) {
      document.getElementById(`code-${index + 1}`).focus();
    }
  };
  
  const code = codes.join("");
  
  return (
    <div className="flex gap-2">
      {codes.map((_, i) => (
        <input
          key={i}
          id={`code-${i}`}
          type="text"
          inputMode="numeric"
          maxLength="1"
          value={codes[i]}
          onChange={(e) => handleCodeChange(i, e.target.value)}
          className="w-12 h-12 text-center text-2xl font-bold border rounded"
        />
      ))}
    </div>
  );
}

Обработка истечения сессии

Если сессия истекла, нужно позволить пользователю начать заново:

const checkSessionExpiry = () => {
  if (authState.expiresAt && Date.now() > authState.expiresAt) {
    setAuthState(prev => ({
      ...prev,
      error: "Сессия истекла. Пожалуйста, начните заново.",
      sessionId: null,
    }));
  }
};

Хранение tokens безопасно

Лучший подход для веб-приложения:

  • JWT token -> httpOnly, secure, samesite=strict cookie
  • Refresh token -> httpOnly cookie (более длительный TTL)
  • localStorage -> только некритичные данные (userId, theme)

Это защищает от XSS атак, так как JavaScript не может получить доступ к httpOnly cookies.

Итого: 2FA требует тщательного управления сессией, безопасного хранения токенов, хорошей UX при вводе кодов и механизма восстановления доступа.

Спроектируй взаимодействие с API при двухфакторной аутентификации | PrepBro