[ "Too many magic link requests. Please try again in {$seconds} seconds.", ], ]); } RateLimiter::hit($key, 3600); $user = User::findByEmail($email); if (!$user) { $user = User::create([ 'email_hash' => User::hashEmail($email), ]); } return MagicLoginToken::generate($email, $ip, $ua); } /** * Verify a magic link token and log the user in. */ public function verifyMagicLink(string $token): bool { $magicToken = MagicLoginToken::valid() ->where('plain_token', $token) ->first(); if (!$magicToken) { return false; } $user = User::where('email_hash', $magicToken->email_hash)->first(); if (!$user) { return false; } $magicToken->markAsUsed(); Auth::login($user, true); return true; } /** * Verify a magic code and log the user in. */ public function verifyCode(string $email, string $code): bool { $emailHash = User::hashEmail($email); $key = 'magic-code:' . $emailHash; if (RateLimiter::tooManyAttempts($key, 5)) { return false; } $magicToken = MagicLoginToken::valid() ->where('email_hash', $emailHash) ->latest() ->first(); if (!$magicToken || !$magicToken->verifyCode($code)) { RateLimiter::hit($key, 300); return false; } $user = User::where('email_hash', $emailHash)->first(); if (!$user) { return false; } $magicToken->markAsUsed(); RateLimiter::clear($key); Auth::login($user, true); return true; } /** * Clean up expired and used tokens older than 7 days. */ public function cleanupExpiredTokens(): int { return MagicLoginToken::expiredOrUsed() ->where('created_at', '<', now()->subDays(7)) ->delete(); } }