'JWT', 'alg' => $alg]; $segments = []; $segments[] = base64url_encode(json_encode($header, JSON_UNESCAPED_SLASHES)); $segments[] = base64url_encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); $signingInput = implode('.', $segments); switch ($alg) { case 'HS256': $signature = hash_hmac('sha256', $signingInput, $secret, true); break; default: throw new \InvalidArgumentException('Unsupported JWT alg: ' . $alg); } $segments[] = base64url_encode($signature); return implode('.', $segments); } } if (!function_exists('base64url_decode')) { function base64url_decode(string $data): string { $padding = strlen($data) % 4; if ($padding > 0) { $data .= str_repeat('=', 4 - $padding); } return base64_decode(strtr($data, '-_', '+/')); } } if (!function_exists('jwt_decode')) { function jwt_decode(string $token, string $secret, string $alg = 'HS256'): ?array { $parts = explode('.', $token); if (count($parts) !== 3) { return null; } [$header64, $payload64, $signature64] = $parts; $header = json_decode(base64url_decode($header64), true); $payload = json_decode(base64url_decode($payload64), true); $signature = base64url_decode($signature64); if (!is_array($header) || !is_array($payload)) { return null; } $signingInput = $header64 . '.' . $payload64; switch ($header['alg'] ?? $alg) { case 'HS256': $expected = hash_hmac('sha256', $signingInput, $secret, true); break; default: return null; } if (!hash_equals($expected, $signature)) { return null; } return $payload; } }