57 lines
1.6 KiB
PHP
57 lines
1.6 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\Auth;
|
|
|
|
use App\Models\User;
|
|
use App\Services\TwoFactorService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class VerifyTwoFactorAction
|
|
{
|
|
public function __construct(private readonly TwoFactorService $twoFactor) {}
|
|
|
|
public function execute(Request $request, string $challengeToken, string $code): User
|
|
{
|
|
$cacheKey = "2fa_challenge:{$challengeToken}";
|
|
$challenge = Cache::get($cacheKey);
|
|
|
|
if (! $challenge || $challenge['ip'] !== $request->ip()) {
|
|
throw ValidationException::withMessages([
|
|
'challenge_token' => 'This challenge has expired. Please log in again.',
|
|
]);
|
|
}
|
|
|
|
if ($challenge['attempts'] >= 5) {
|
|
Cache::forget($cacheKey);
|
|
|
|
throw ValidationException::withMessages([
|
|
'code' => 'Too many failed attempts. Please log in again.',
|
|
]);
|
|
}
|
|
|
|
$user = User::findOrFail($challenge['user_id']);
|
|
|
|
$valid = $this->twoFactor->verifyCode($user->two_factor_secret, $code)
|
|
|| $this->twoFactor->redeemRecoveryCode($user, $code);
|
|
|
|
if (! $valid) {
|
|
$challenge['attempts']++;
|
|
Cache::put($cacheKey, $challenge, now()->addMinutes(5));
|
|
|
|
throw ValidationException::withMessages([
|
|
'code' => 'That code is invalid.',
|
|
]);
|
|
}
|
|
|
|
Cache::forget($cacheKey);
|
|
|
|
Auth::login($user);
|
|
$request->session()->regenerate();
|
|
|
|
return $user;
|
|
}
|
|
}
|