Безпечний вхід без пароля: реалізація Magic Link на сайті
Паролі — слабке місце будь-якого веб-додатка. Користувачі їх забувають, використовують прості комбінації, а підтримка витрачає години на скидання. У SaaS-проєктах кожен п'ятий запит у саппорт — «не можу зайти». Уявіть, що користувач може увійти одним кліком за посиланням з листа — без введення пароля, без очікування SMS. Саме так працює Magic Link: після введення email система генерує одноразовий токен, хешує його (SHA-256) і відправляє посилання з обмеженим терміном дії. Після кліку токен позначається як використаний, і увійти за ним вдруге неможливо. Реалізація такого механізму в Laravel займає близько 4 днів і значно знижує навантаження на службу підтримки. Ми впроваджуємо Magic Link протягом багатьох років, за цей час розробили десятки рішень для SaaS-продуктів, від MVP до enterprise.
Вхід за Magic Link швидший за введення пароля в 10 разів: користувачеві не потрібно нічого запам'ятовувати, достатньо одного кліка. Економія на підтримці досягає 30% — менше звернень щодо скидання пароля. При цьому безпека не страждає: токен живе 15 хвилин і не може бути використаний повторно.
Як працює Magic Link авторизація?
Користувач вводить email, система генерує одноразовий токен, зберігає його хеш у БД і відправляє посилання. Клік за посиланням верифікує токен і авторизує користувача. Все займає секунди, без введення пароля.
Чому Magic Link безпечніший за паролі?
Токен створюється з 64 випадкових байт (SHA-256), діє 15 хвилин і може бути використаний лише один раз. Навіть якщо зловмисник перехопить посилання, через 15 хвилин воно стане недійсним. Для додаткового захисту ми застосовуємо rate limiting: не більше 1 запиту за 5 хвилин на email і 5 з однієї IP за годину. Як зазначено в документації Laravel, одноразові токени з обмеженим часом життя — стандартна практика для захисту від перехоплення.
Які проблеми вирішує Magic Link?
Основні проблеми, які вирішує Magic Link: зниження навантаження на підтримку, прискорення входу та покращення UX. Замість того щоб запам'ятовувати паролі та вводити їх щоразу, користувач отримує посилання та авторизується одним кліком. Це особливо актуально для SaaS-сервісів, де важлива швидкість входу. На одному з проєктів SaaS-платформи ми скоротили кількість звернень у підтримку на 30% після впровадження Magic Link. Вартість розробки розраховується індивідуально, але в середньому інвестиція окупається за 2–3 місяці за рахунок зменшення навантаження на саппорт.
Коли Magic Link виправданий?
Magic Link ідеально підходить для проєктів з нечастими входами (сервіси з тривалими сесіями), для мобільних додатків або кіосків, де незручно вводити пароль. Також це чудове рішення для B2B-продуктів, коли потрібно швидко надати доступ без створення повноцінного облікового запису.
Як ми реалізуємо Magic Link на вашому сайті?
Використовуємо Laravel 11, PostgreSQL, Redis. Весь код покриваємо тестами.
Генерація та зберігання токена
class MagicLinkService
{
public function sendLink(string $email): void
{
$this->checkRateLimit($email);
$user = User::firstOrCreate(
['email' => $email],
['name' => explode('@', $email)[0], 'email_verified_at' => now()]
);
$token = Str::random(64);
$hashedToken = hash('sha256', $token);
// Інвалідувати попередні токени для цього користувача
MagicLinkToken::where('user_id', $user->id)->delete();
MagicLinkToken::create([
'user_id' => $user->id,
'token' => $hashedToken,
'expires_at' => now()->addMinutes(15),
]);
Mail::to($email)->send(new MagicLinkMail($user, $token));
}
public function authenticate(string $token, string $email): User
{
$hashedToken = hash('sha256', $token);
$record = MagicLinkToken::where('token', $hashedToken)
->whereHas('user', fn($q) => $q->where('email', $email))
->where('expires_at', '>', now())
->whereNull('used_at')
->firstOrFail();
// Одноразове використання
$record->update(['used_at' => now()]);
return $record->user;
}
}
Міграція для зберігання токенів
Schema::create('magic_link_tokens', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('token', 64)->unique(); // SHA256 hash
$table->timestamp('expires_at');
$table->timestamp('used_at')->nullable();
$table->string('ip_address', 45)->nullable();
$table->string('user_agent')->nullable();
$table->timestamps();
$table->index(['token', 'expires_at']);
});
Верифікація та rate limiting
class MagicLinkController extends Controller
{
public function verify(Request $request): RedirectResponse
{
$request->validate([
'token' => 'required|string|size:64',
'email' => 'required|email',
]);
try {
$user = $this->magicLinkService->authenticate(
$request->token,
$request->email
);
} catch (ModelNotFoundException) {
return redirect('/login?error=invalid_link');
}
Auth::login($user, remember: true);
if (!$user->hasVerifiedEmail()) {
$user->markEmailAsVerified();
}
return redirect()->intended('/dashboard');
}
}
// Rate limiting
RateLimiter::for('magic-link', function (Request $request) {
return [
Limit::perMinutes(5, 1)->by('email:' . $request->email),
Limit::perHour(5)->by('ip:' . $request->ip()),
];
});
Шаблон листа
class MagicLinkMail extends Mailable
{
use Queueable, SerializesModels;
public string $loginUrl;
public function __construct(User $user, string $token)
{
$this->loginUrl = URL::signedRoute(
'auth.magic-link.verify',
['token' => $token, 'email' => $user->email],
now()->addMinutes(15)
);
}
public function build(): self
{
return $this->subject('Ваше посилання для входу')
->markdown('emails.magic-link');
}
}
Процес роботи
- Аналітика — вивчаємо поточну аутентифікацію, вимоги до безпеки.
- Проектування — розробляємо архітектуру сервісу, узгоджуємо з вами.
- Реалізація — пишемо код, покриваємо unit-тестами.
- Тестування — перевіряємо edge cases (невалідний токен, прострочений, повторне використання).
- Деплой — розгортаємо на вашому сервері, налаштовуємо моніторинг.
| Етап | Час |
|---|---|
| Сервіс генерації + зберігання | 1 день |
| Верифікація + rate limiting | 0.5 дня |
| Шаблон листа + черга | 0.5 дня |
| Frontend форма + UI станів | 0.5 дня |
| Тести + edge cases | 0.5 дня |
Разом: 3–4 робочі дні.
Що входить в роботу
- Повна документація по API та схемі БД.
- Доступ до репозиторію з кодом.
- Навчання вашої команди (1 година, онлайн).
- Гарантія на код — 3 місяці безкоштовної підтримки.
Ми завершили більше 50 проєктів з впровадження Magic Link. Впровадження Magic Link дозволяє знизити витрати на підтримку на 20-30% за рахунок скорочення запитів на скидання пароля. Якщо ви хочете реалізувати Magic Link у своєму проєкті, зв'яжіться з нами — ми підготуємо пропозицію протягом дня.
Поширені проблеми при реалізації Magic Link
- Токен в URL — логується в access_log. Обов'язково використовуйте HTTPS.
- При запиті нового токена старі потрібно інвалідувати.
- Після кліку токен позначайте як used, не видаляйте — це потрібно для аудиту.
- Відсутність rate limiting дозволяє зловмиснику заспамити email.
| Характеристика | Magic Link | Пароль + OTP |
|---|---|---|
| Час входу | 1 клік | 2+ хвилини |
| Навантаження на підтримку | Низьке | Високе |
| Захист від перехоплення | Обмежений TTL | Фіксований код |
| Зручність для користувача | Висока | Середня |
Замовте реалізацію Magic Link на вашому сайті та позбавте користувачів зайвих дій. Отримайте консультацію щодо впровадження — оцінка проєкту протягом одного робочого дня.







