test borked
This commit is contained in:
parent
49b528a66b
commit
2418edccfd
29 changed files with 2036 additions and 121 deletions
117
app/Services/MagicLinkAuthService.php
Executable file
117
app/Services/MagicLinkAuthService.php
Executable file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\MagicLoginToken;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class MagicLinkAuthService
|
||||
{
|
||||
/**
|
||||
* Send a magic link to the given email address.
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function sendMagicLink(string $email, ?string $ip, ?string $ua): MagicLoginToken
|
||||
{
|
||||
$key = 'magic-link:' . hash('sha256', strtolower(trim($email)));
|
||||
|
||||
if (RateLimiter::tooManyAttempts($key, 5)) {
|
||||
$seconds = RateLimiter::availableIn($key);
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => [
|
||||
"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();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue