87 lines
2.4 KiB
PHP
87 lines
2.4 KiB
PHP
<?php
|
|
|
|
if (!function_exists('base64url_encode')) {
|
|
function base64url_encode(string $data): string
|
|
{
|
|
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
|
}
|
|
}
|
|
|
|
if (!function_exists('jwt_encode')) {
|
|
/**
|
|
* Minimal HS256 JWT encoder.
|
|
*
|
|
* @param array $payload
|
|
* @param string $secret
|
|
* @param string $alg Only HS256 supported here
|
|
* @return string JWT string
|
|
*/
|
|
function jwt_encode(array $payload, string $secret, string $alg = 'HS256'): string
|
|
{
|
|
$header = ['typ' => '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;
|
|
}
|
|
}
|