insert([ 'selector' => $selector, 'validator_hash' => hash('sha256', $validator), 'expires_at' => date('Y-m-d H:i:s', time() + self::LIFETIME_SECONDS), ]); return $selector . ':' . $validator; } public function verifyToken(string $token): bool { [$selector, $validator] = $this->splitToken($token); if ($selector === null) { return false; } $row = $this->where('selector', $selector)->first(); if (!$row || strtotime($row['expires_at']) <= time()) { if ($row) { $this->delete($row['id']); } return false; } return hash_equals($row['validator_hash'], hash('sha256', $validator)); } public function deleteToken(string $token): void { [$selector] = $this->splitToken($token); if ($selector !== null) { $this->where('selector', $selector)->delete(); } } public function deleteExpired(): void { $this->where('expires_at <=', date('Y-m-d H:i:s'))->delete(); } private function splitToken(string $token): array { $parts = explode(':', $token, 2); if (count($parts) !== 2 || !ctype_xdigit($parts[0]) || strlen($parts[0]) !== 32 || !ctype_xdigit($parts[1]) || strlen($parts[1]) !== 64) { return [null, null]; } return $parts; } }