sessionId = $this->sessionId !== '' ? $this->sessionId : 'no_session'; } /** * @return array{ts:int,sid:string,token:string} */ public function issue(string $intent): array { $ts = time(); return ['ts' => $ts, 'sid' => $this->sessionId, 'token' => $this->buildToken($intent, $ts, $this->sessionId)]; } public function validate(string $intent, string $timestamp, string $token, int $ttl = 3600, string $sessionHint = ''): bool { if (!preg_match('/^[0-9]{1,20}$/', $timestamp)) { return false; } $ts = (int)$timestamp; $now = time(); if ($ts <= 0 || $ts > $now + 30 || $now - $ts > $ttl) { return false; } $sids = array_values(array_unique(array_filter([$sessionHint, $this->sessionId, 'no_session'], static fn ($v): bool => $v !== ''))); foreach ($sids as $sid) { if (hash_equals($this->buildToken($intent, $ts, $sid), trim($token))) { return true; } } return false; } private function buildToken(string $intent, int $timestamp, string $sid): string { return hash_hmac('sha256', implode('|', [$this->username, $sid, $intent, (string)$timestamp]), $this->secret); } }