'JWT', 'alg' => 'HS256']); $payload['iat'] = time(); $payload['exp'] = time() + $ttl; $payload['iss'] = 'https://ttmqms.hospital.local'; $base64UrlHeader = self::base64UrlEncode($header); $base64UrlPayload = self::base64UrlEncode(json_encode($payload)); $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $secret, true); $base64UrlSignature = self::base64UrlEncode($signature); return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature; } /** * Verify and Decode JWT Token * * @param string $token * @param string $secret * @return array|null Payload array if valid, null if invalid or expired */ public static function decodeJwt(string $token, string $secret): ?array { $parts = explode('.', $token); if (count($parts) !== 3) return null; [$header64, $payload64, $sig64] = $parts; $signature = self::base64UrlDecode($sig64); $expectedSig = hash_hmac('sha256', $header64 . "." . $payload64, $secret, true); if (!hash_equals($expectedSig, $signature)) { return null; // Signature mismatch (Tampered) } $payload = json_decode(self::base64UrlDecode($payload64), true); if (!$payload || !isset($payload['exp']) || $payload['exp'] < time()) { return null; // Token expired } return $payload; } private static function base64UrlEncode(string $data): string { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } private static function base64UrlDecode(string $data): string { return base64_decode(strtr($data, '-_', '+/')); } /** * Get Client IP Address (Supporting Proxy X-Forwarded-For) */ public static function getClientIp(): string { if (!empty($_SERVER['HTTP_CLIENT_IP'])) { return $_SERVER['HTTP_CLIENT_IP']; } if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); return trim($ips[0]); } return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; } }