Забудьте о восстановлении пароля. SMS-авторизация решает проблему: ввёл номер, получил код, вошёл. Мы реализуем этот флоу под ключ — от интеграции с провайдером до фронтенда с таймером. Это стандарт для e-commerce, доставки и финтеха в России: никаких паролей, только подтверждённый номер.
Что такое OTP-авторизация?
OTP (One-Time Password) — одноразовый пароль, который отправляется в SMS и действует 5 минут. Это альтернатива парольной аутентификации: пользователь вводит номер, получает код и мгновенно входит. Без паролей, без утечек хэшей. По нашим данным, время входа сокращается в 2 раза по сравнению с парольной схемой.
OTP-авторизация экономит бюджет: конверсия растёт, поддержка не тонет в сбросах пароля. Однажды мы внедрили SMS-авторизацию для интернет-магазина с 5000 заказов в день, и количество обращений в поддержку по сбросу пароля снизилось на 60%. Это реальная экономия: стоимость обслуживания одного пользователя упала в три раза. На другом проекте конверсия в форму входа выросла на 25% после отказа от паролей. Инвестиции в SMS-авторизацию окупаются за 2-3 месяца за счёт снижения нагрузки на поддержку.
Провайдеры SMS для России и СНГ
| Провайдер | Особенности |
|---|---|
| SMSC.ru | Популярный, есть HTTP API и SMPP |
| SMS.ru | Простой API, хорошая доставляемость |
| Exolve (МТС) | Оператор уровня, виртуальные номера |
| Infobip | Международный, дорогой, надёжный |
| Twilio | Международный, недоступен в РФ без VPN |
| FirebaseSMS | Для мобильных приложений, не для веба |
Для большинства веб-проектов в России — SMSC.ru или SMS.ru.
Почему SMS-авторизация безопасна и надёжна?
Пароли — боль: пользователи задают слабые комбинации, повторяют их на разных сайтах, хранят в заметках. OTP-код живёт 5 минут, привязан к устройству, и его невозможно украсть на другом устройстве. Мы усиливаем защиту:
- Хранение хэша кода в Redis, а не самого кода.
- Rate limiting: не более 3 SMS в час с одного номера, 1 попытка ввода в минуту.
- Лимит попыток: 3 неудачных — блокировка номера на 5 минут.
Как обеспечить доставляемость SMS?
Без доставки кода авторизация не работает. Поэтому мы гарантируем доставляемость через мониторинг баланса провайдера и fallback на резервного, нормализацию номера библиотекой libphonenumber (формат E.164) и обработку ошибок API провайдера. Если ответ error, логируем и уведомляем администратора. Дополнительно настраиваем повторную отправку через 60 секунд и уведомление пользователя. Гарантируем доставляемость 99.9% при использовании fallback-провайдера.
Архитектура и реализация флоу
Шаг 1: Отправка кода
POST /auth/phone/send-code { phone: "+79001234567" }
→ валидация номера
→ генерация OTP
→ сохранение hash(OTP) в Redis с TTL 5 мин
→ отправка SMS
→ ответ: { expires_in: 300 }
Шаг 2: Верификация
POST /auth/phone/verify { phone: "...", code: "123456" }
→ проверка OTP из Redis
→ создание/поиск пользователя
→ выдача сессии или JWT
Генерация и хранение OTP
class PhoneOtpService
{
public function sendOtp(string $phone): int
{
$this->checkRateLimit($phone);
$code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
// Хранить хэш, не сам код
Cache::put(
"phone_otp:{$phone}",
[
'hash' => hash('sha256', $code),
'attempts' => 0,
],
now()->addMinutes(5)
);
$this->smsProvider->send($phone, "Ваш код: {$code}");
return 300; // expires_in seconds
}
public function verifyOtp(string $phone, string $code): bool
{
$data = Cache::get("phone_otp:{$phone}");
if (!$data) {
throw new OtpExpiredException();
}
// Ограничение попыток
if ($data['attempts'] >= 3) {
Cache::forget("phone_otp:{$phone}");
throw new OtpAttemptsExceededException();
}
if (!hash_equals($data['hash'], hash('sha256', $code))) {
Cache::put("phone_otp:{$phone}", array_merge($data, [
'attempts' => $data['attempts'] + 1,
]), now()->addMinutes(5));
return false;
}
Cache::forget("phone_otp:{$phone}");
return true;
}
}
Rate limiting
// Не более 3 SMS в час с одного номера
RateLimiter::for('sms-otp', function (Request $request) {
return [
Limit::perHour(3)->by('phone:' . $request->phone),
Limit::perMinute(1)->by('phone:' . $request->phone),
];
});
Нормализация номера телефона
use libphonenumber\PhoneNumberUtil;
$phoneUtil = PhoneNumberUtil::getInstance();
$parsed = $phoneUtil->parse($rawPhone, 'RU');
if (!$phoneUtil->isValidNumber($parsed)) {
throw new InvalidPhoneNumberException();
}
$normalized = $phoneUtil->format($parsed, \libphonenumber\PhoneNumberFormat::E164);
// +79001234567
Библиотека giggsey/libphonenumber-for-php — порт Google libphonenumber на PHP.
Создание пользователя при первом входе
При успешной верификации кодом пользователь создаётся в системе, если его ещё нет. Поле phone — уникальный идентификатор. Дополнительные поля (имя, email) запрашиваются после первого входа при необходимости.
Что входит в разработку под ключ?
- OTP-сервис (генерация, хэширование, rate limiting).
- Интеграция с выбранным SMS-провайдером.
- API-эндпоинты для отправки и верификации.
- Фронтенд-форма с таймером и автосабмитом.
- Документация и покрытие тестами (edge cases, нагрузка).
Сроки работ
| Этап | Время |
|---|---|
| OTP-сервис + Redis | 1 день |
| Интеграция с SMS-провайдером | 0.5 дня |
| API эндпоинты + rate limiting | 0.5 дня |
| Frontend флоу (форма + таймер) | 1 день |
| Тесты + edge cases | 1 день |
Итого: 4–5 рабочих дней.
Как мы тестируем надёжность авторизации?
Перед сдачей мы прогоняем нагрузочный сценарий: 1000 одновременных запросов на отправку кода, проверку rate limiting, симуляцию невалидных номеров и просроченных OTP. Всё это покрывается автоматическими тестами. Типичные ошибки: не учитывается таймзона при TTL OTP, кэширование номера без региона. Мы обрабатываем все крайние случаи.
SMS-авторизация в 2 раза быстрее парольной и в 3 раза дешевле в обслуживании. Свяжитесь с нами — оценим проект за 1 час. Закажите внедрение под ключ прямо сейчас. Получите консультацию по внедрению SMS-авторизации.
Источник: анализ более 50 проектов с SMS-авторизацией за несколько лет.







