feat(auth): expire stale password reset codes
This commit is contained in:
@@ -12,6 +12,12 @@ use Throwable;
|
||||
|
||||
class ResetPasswordAttemptService
|
||||
{
|
||||
public const CODE_VALID = 'valid';
|
||||
|
||||
public const CODE_INVALID = 'invalid';
|
||||
|
||||
public const CODE_EXPIRED = 'expired';
|
||||
|
||||
public function createForEmail(
|
||||
string $email,
|
||||
string $tenantCode,
|
||||
@@ -146,12 +152,12 @@ class ResetPasswordAttemptService
|
||||
);
|
||||
}
|
||||
|
||||
public function validateCode(string $email, string $code): bool
|
||||
public function validateCode(string $email, string $code): string
|
||||
{
|
||||
$emailFingerprint = $this->emailFingerprint($email);
|
||||
|
||||
try {
|
||||
return DB::transaction(function () use ($email, $code, $emailFingerprint): bool {
|
||||
return DB::transaction(function () use ($email, $code, $emailFingerprint): string {
|
||||
$user = User::query()
|
||||
->where('email', $email)
|
||||
->lockForUpdate()
|
||||
@@ -169,14 +175,27 @@ class ResetPasswordAttemptService
|
||||
'email_fingerprint' => $emailFingerprint,
|
||||
]);
|
||||
|
||||
return false;
|
||||
return self::CODE_INVALID;
|
||||
}
|
||||
|
||||
if ($attempt->expires_at?->isPast()) {
|
||||
$attempt->update([
|
||||
'status' => ResetPasswordAttempt::STATUS_EXPIRED,
|
||||
]);
|
||||
|
||||
Log::info('Password reset code validation failed: attempt expired.', [
|
||||
'email_fingerprint' => $emailFingerprint,
|
||||
'attempt_id' => $attempt->getKey(),
|
||||
]);
|
||||
|
||||
return self::CODE_EXPIRED;
|
||||
}
|
||||
|
||||
$attempt->update([
|
||||
'status' => ResetPasswordAttempt::STATUS_VALIDATED,
|
||||
]);
|
||||
|
||||
return true;
|
||||
return self::CODE_VALID;
|
||||
});
|
||||
} catch (Throwable $exception) {
|
||||
Log::error('Failed to validate password reset code.', [
|
||||
@@ -214,6 +233,19 @@ class ResetPasswordAttemptService
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($attempt->expires_at?->isPast()) {
|
||||
$attempt->update([
|
||||
'status' => ResetPasswordAttempt::STATUS_EXPIRED,
|
||||
]);
|
||||
|
||||
Log::info('Password reset failed: attempt expired.', [
|
||||
'email_fingerprint' => $emailFingerprint,
|
||||
'attempt_id' => $attempt->getKey(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$user->password = $password;
|
||||
$user->failed_login_attempts = 0;
|
||||
$user->last_failed_login_at = null;
|
||||
@@ -270,6 +302,7 @@ class ResetPasswordAttemptService
|
||||
'codigo' => $this->generateCode(),
|
||||
'reason' => $reason,
|
||||
'status' => ResetPasswordAttempt::STATUS_PENDING,
|
||||
'expires_at' => now()->addMinutes((int) config('auth.passwords.users.expire')),
|
||||
]);
|
||||
|
||||
return $attempt->getKey();
|
||||
|
||||
Reference in New Issue
Block a user