Files
bot_palmas/middleware/Auth.php
T

89 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
/**
* Middleware de autenticación JWT HS256.
*
* Valida el header: Authorization: Bearer <token>
* El secreto se toma de la variable de entorno JWT_SECRET.
*/
class Auth
{
/**
* Verifica el Bearer token. Termina con 401 si es inválido.
* Devuelve el payload decodificado si es válido.
*/
public static function requireBearer(): array
{
$header = $_SERVER['HTTP_AUTHORIZATION']
?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
?? '';
if (!str_starts_with($header, 'Bearer ')) {
self::abort(401, 'Token de autorización requerido.');
}
$token = substr($header, 7);
return self::verifyJwt($token);
}
// ─── JWT HS256 ────────────────────────────────────────────────────────────
private static function verifyJwt(string $token): array
{
$parts = explode('.', $token);
if (count($parts) !== 3) {
self::abort(401, 'Token malformado.');
}
[$b64Header, $b64Payload, $b64Sig] = $parts;
// Verificar firma
$secret = env('JWT_SECRET', '');
if ($secret === '') {
self::abort(500, 'JWT_SECRET no configurado.');
}
$data = "$b64Header.$b64Payload";
$expected = self::base64UrlEncode(
hash_hmac('sha256', $data, $secret, true)
);
// Comparación segura contra timing attacks
if (!hash_equals($expected, $b64Sig)) {
self::abort(401, 'Firma JWT inválida.');
}
// Decodificar payload
$payload = json_decode(self::base64UrlDecode($b64Payload), true);
if (!is_array($payload)) {
self::abort(401, 'Payload JWT inválido.');
}
// Validar expiración si existe
if (isset($payload['exp']) && $payload['exp'] < time()) {
self::abort(401, 'Token expirado.');
}
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, '-_', '+/'));
}
private static function abort(int $code, string $message): never
{
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['error' => $message], JSON_UNESCAPED_UNICODE);
exit;
}
}