Реализация авторизации через email с подтверждением
Мы часто сталкиваемся с задачами, где стандартная парольная аутентификация создаёт больше проблем, чем решает. Спам-регистрации (до 70% от всех попыток) забивают базу, забытые пароли генерируют поток тикетов в поддержку, а утечки баз данных дискредитируют сервис. Авторизация через email с подтверждением устраняет эти риски. Экономия на поддержке составляет до 500 000 рублей в год — за счёт снижения нагрузки на службу поддержки на 80% и уменьшения количества фейковых аккаунтов на 95%.
Почему email-верификация критична для безопасности?
Без верификации злоумышленник может зарегистрироваться с любым адресом и рассылать спам или получать доступ к функциям, требующим подтверждения. Верификация гарантирует, что пользователь владеет ящиком. Кроме того, это первый шаг к восстановлению доступа и защите от перехвата аккаунта. По данным наших проектов, внедрение верификации снижает количество фейковых регистраций на 95%.
Два сценария использования
Первый сценарий — верификация при регистрации. Пользователь регистрируется с паролем, получает письмо, подтверждает почту — только после этого получает полный доступ. Второй — passwordless вход. Пользователь вводит email, получает письмо со ссылкой, кликает — авторизован без пароля. Оба сценария могут сосуществовать.
Как мы реализуем email-верификацию под ключ?
Верификация email при регистрации (Laravel)
Модель User реализует контракт MustVerifyEmail. Это обязывает определить метод sendEmailVerificationNotification(), который отправляет кастомное уведомление. Мы используем встроенную подпись URL.
class User extends Authenticatable implements MustVerifyEmail
{
public function sendEmailVerificationNotification(): void
{
$this->notify(new CustomVerifyEmailNotification());
}
}
Route::get('/email/verify/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
->middleware(['auth', 'signed', 'throttle:6,1'])
->name('verification.verify');
Подписанный URL генерируется с истечением через 60 минут. Подпись — HMAC-SHA256 с APP_KEY. Изменение параметров возвращает 403.
$url = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1($user->email)]
);
OTP-код вместо ссылки
Некоторые проекты предпочитают 6-значный код — удобнее, если письмо открывают на другом устройстве. Мы кэшируем хеш кода с истечением 10 минут и проверяем через hash_equals для защиты от timing attack.
$code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
Cache::put("email_verification:{$user->id}", hash('sha256', $code), now()->addMinutes(10));
public function verify(Request $request): JsonResponse
{
$stored = Cache::get("email_verification:{$user->id}");
if (!$stored || !hash_equals($stored, hash('sha256', $request->code))) {
return response()->json(['message' => 'Неверный или устаревший код'], 422);
}
$user->markEmailAsVerified();
Cache::forget("email_verification:{$user->id}");
return response()->json(['message' => 'Email подтверждён']);
}
Защита от спама и повторная отправка
RateLimiter настраивается так, чтобы одно письмо можно было отправить не чаще раза в 5 минут. Фронтенд показывает обратный отсчёт до следующей отправки. Это предотвращает брутфорс и спам-атаки.
RateLimiter::for('email-verification', function (Request $request) {
return Limit::perMinutes(5, 1)->by($request->user()->id);
});
Изменение email с подтверждением
Безопасно менять email можно только после подтверждения нового адреса. Мы вводим поле pending_email в таблицу users или отдельную таблицу. На новый адрес отправляется письмо с верификацией, и только после успешного подтверждения email обновляется.
$user->update(['pending_email' => $newEmail]);
// Отправляем верификацию на $newEmail
// При подтверждении:
$user->update(['email' => $newEmail, 'pending_email' => null]);
Настройка очередей и мониторинг
Отправка писем должна быть асинхронной, чтобы не блокировать ответ. Мы используем очередь Laravel (драйверы Redis или SQS) и настраиваем мониторинг через Horizon или Laravel Pulse. Если письмо не доставлено, логируем ошибку и уведомляем администратора.
Как мы внедряем email-верификацию: пошаговый план
- Анализируем требования и выбираем сценарий (регистрация, passwordless или оба).
- Проектируем модель данных: добавляем поля
email_verified_at,pending_email, настраиваем связь с очередью. - Реализуем маршруты и контроллеры с подписанными URL или OTP.
- Создаём кастомные шаблоны писем (HTML + plain text).
- Настраиваем rate limiting и обработку ошибок (истекшие ссылки, повторные клики).
- Тестируем edge cases (смена email, повторная отправка, параллельные запросы).
- Деплоим с использованием очередей и мониторинга.
Сравнение OTP и Magic Link
| Критерий | OTP-код | Magic Link |
|---|---|---|
| Удобство на одном устройстве | Нужно открыть письмо и ввести код | Сразу авторизация по клику |
| Безопасность | Код ограничен по времени, 6 цифр | Подписанный URL, может быть перехвачен |
| Реализация | Требуется кэш и хеширование | Использует signed route |
| Пользовательский опыт | Требует ручного ввода | Бесшовно |
Этапы реализации
| Этап | Длительность | Результат |
|---|---|---|
| Анализ требований | 0.5–1 день | Выбран сценарий (регистрация/passwordless) |
| Проектирование | 0.5–1 день | Модель, маршруты, контроллеры, шаблоны |
| Реализация базы | 0.5–1 день | Поля email_verified_at, pending_email |
| Настройка уведомлений | 0.5–1 день | Кастомное письмо с ссылкой или OTP |
| Защита и тестирование | 1–2 дня | Rate limiting, signed URL, edge cases |
| Деплой | 0.5 дня | Очередь, мониторинг, документация |
Обработка истекших ссылок
Если пользователь перешёл по истекшей ссылке, мы возвращаем понятное сообщение и кнопку «Выслать новое письмо». При повторном клике по уже подтверждённой ссылке возвращаем 200 с уведомлением, что email уже верифицирован.
Типичные ошибки и их решение
- Истекшая ссылка: показываем сообщение и предлагаем повторную отправку.
- Повторный клик по ссылке: возвращаем 200 с уведомлением, что email уже подтверждён.
- Отсутствие очереди: отправка писем синхронно замедляет ответ — мы используем очередь Laravel.
Что вы получаете в результате
- Полный исходный код с комментариями (модели, контроллеры, уведомления).
- Кастомные шаблоны писем, адаптированные под ваш бренд.
- Настройка очередей и мониторинга (Laravel Horizon/Pulse).
- Документация для разработчиков по развёртыванию и поддержке.
- Гарантия и поддержка в течение 30 дней после внедрения.
Свяжитесь с нами для консультации — оценим ваш проект и предложим оптимальное решение. Закажите внедрение под ключ и получите готовую систему авторизации в кратчайшие сроки.







