12]); } /** * Verify password */ public static function verifyPassword(string $password, string $hash): bool { return password_verify($password, $hash); } /** * Generate JWT Token with HMAC-SHA256 signature */ public static function generateJWT(array $payload, int $expirySeconds = 86400): string { $appUrl = getenv('APP_URL') ?: 'https://saqel.intaleqapp.com'; $header = self::base64UrlEncode(json_encode(['alg' => 'HS256', 'typ' => 'JWT'])); // Standard OWASP Claims $payload['iat'] = time(); $payload['exp'] = time() + $expirySeconds; $payload['iss'] = $appUrl; $payload['aud'] = 'saqel_app'; $payload['jti'] = bin2hex(random_bytes(16)); $payloadEncoded = self::base64UrlEncode(json_encode($payload)); $secret = self::getJwtSecret(); $signature = hash_hmac('sha256', "$header.$payloadEncoded", $secret, true); $signatureEncoded = self::base64UrlEncode($signature); return "$header.$payloadEncoded.$signatureEncoded"; } /** * Verify JWT Token and return payload if valid, false otherwise */ public static function verifyJWT(string $token) { $parts = explode('.', $token); if (count($parts) !== 3) { return false; } list($headerEncoded, $payloadEncoded, $signatureEncoded) = $parts; $header = json_decode(self::base64UrlDecode($headerEncoded), true); if (!is_array($header) || ($header['alg'] ?? '') !== 'HS256' || ($header['typ'] ?? '') !== 'JWT') { return false; } $secret = self::getJwtSecret(); if (!$secret) { return false; } $signature = self::base64UrlDecode($signatureEncoded); $expectedSignature = hash_hmac('sha256', "$headerEncoded.$payloadEncoded", $secret, true); if (!hash_equals($signature, $expectedSignature)) { return false; } $payload = json_decode(self::base64UrlDecode($payloadEncoded), true); $expectedIssuer = getenv('APP_URL') ?: 'https://saqel.intaleqapp.com'; if (!$payload || !isset($payload['exp'], $payload['iat'], $payload['jti']) || time() >= $payload['exp'] || ($payload['iss'] ?? '') !== $expectedIssuer || ($payload['aud'] ?? '') !== 'saqel_app') { return false; } return $payload; } /** * Alias for verifyJWT for backward compatibility */ public static function decodeJwt(string $token) { return self::verifyJWT($token); } 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, '-_', '+/') . str_repeat('=', (4 - strlen($data) % 4) % 4)); } }