b239ae3e5f
Replaces Firebase with a self-hosted PHP/MySQL API served from winded.prymsolutions.com. Includes full backend (schema, auth, events, teams, brackets, suggestions, stats, media, file upload) and updated Flutter repositories and domain models. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
35 lines
1.3 KiB
PHP
35 lines
1.3 KiB
PHP
<?php
|
|
// Change JWT_SECRET to a long random string before deploying.
|
|
define('JWT_SECRET', 'f0gHGG23#5j9gaGg90asndfa9gtybama89G#');
|
|
define('JWT_TTL', 60 * 60 * 24 * 30); // 30 days
|
|
|
|
class JWT {
|
|
public static function encode(array $payload): string {
|
|
$header = self::b64e(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
|
|
$payload['iat'] = time();
|
|
$payload['exp'] = time() + JWT_TTL;
|
|
$payload = self::b64e(json_encode($payload));
|
|
$sig = self::b64e(hash_hmac('sha256', "$header.$payload", JWT_SECRET, true));
|
|
return "$header.$payload.$sig";
|
|
}
|
|
|
|
public static function decode(string $token): ?array {
|
|
$parts = explode('.', $token);
|
|
if (count($parts) !== 3) return null;
|
|
[$header, $payload, $sig] = $parts;
|
|
$expected = self::b64e(hash_hmac('sha256', "$header.$payload", JWT_SECRET, true));
|
|
if (!hash_equals($expected, $sig)) return null;
|
|
$data = json_decode(self::b64d($payload), true);
|
|
if (!$data || ($data['exp'] ?? 0) < time()) return null;
|
|
return $data;
|
|
}
|
|
|
|
private static function b64e(string $v): string {
|
|
return rtrim(strtr(base64_encode($v), '+/', '-_'), '=');
|
|
}
|
|
|
|
private static function b64d(string $v): string {
|
|
return base64_decode(strtr($v, '-_', '+/') . str_repeat('=', (4 - strlen($v) % 4) % 4));
|
|
}
|
|
}
|