first commit
This commit is contained in:
74
src/Auth/PasswordResetRepository.php
Normal file
74
src/Auth/PasswordResetRepository.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Auth;
|
||||
|
||||
use PDO;
|
||||
|
||||
final class PasswordResetRepository implements PasswordResetRepositoryInterface
|
||||
{
|
||||
public function __construct(private readonly PDO $db)
|
||||
{
|
||||
}
|
||||
|
||||
public function create(int $userId, string $tokenHash, string $expiresAt): void
|
||||
{
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO password_resets (user_id, token_hash, expires_at, created_at)
|
||||
VALUES (:user_id, :token_hash, :expires_at, :created_at)
|
||||
');
|
||||
|
||||
$stmt->execute([
|
||||
':user_id' => $userId,
|
||||
':token_hash' => $tokenHash,
|
||||
':expires_at' => $expiresAt,
|
||||
':created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function findActiveByHash(string $tokenHash): ?array
|
||||
{
|
||||
$stmt = $this->db->prepare(
|
||||
'SELECT * FROM password_resets WHERE token_hash = :token_hash AND used_at IS NULL'
|
||||
);
|
||||
$stmt->execute([':token_hash' => $tokenHash]);
|
||||
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function invalidateByUserId(int $userId): void
|
||||
{
|
||||
$stmt = $this->db->prepare(
|
||||
'UPDATE password_resets SET used_at = :used_at WHERE user_id = :user_id AND used_at IS NULL'
|
||||
);
|
||||
$stmt->execute([':used_at' => date('Y-m-d H:i:s'), ':user_id' => $userId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically consume a token and return the affected row.
|
||||
* Uses UPDATE ... RETURNING to avoid SELECT+UPDATE race conditions.
|
||||
*/
|
||||
public function consumeActiveToken(string $tokenHash, string $usedAt): ?array
|
||||
{
|
||||
$stmt = $this->db->prepare(
|
||||
'UPDATE password_resets
|
||||
SET used_at = :used_at
|
||||
WHERE token_hash = :token_hash
|
||||
AND used_at IS NULL
|
||||
AND expires_at >= :now
|
||||
RETURNING *'
|
||||
);
|
||||
|
||||
$stmt->execute([
|
||||
':used_at' => $usedAt,
|
||||
':token_hash' => $tokenHash,
|
||||
':now' => $usedAt,
|
||||
]);
|
||||
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $row ?: null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user