JwtAuthMiddleware.php 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. <?php
  2. namespace Middlewares;
  3. use Firebase\JWT\JWT;
  4. use Firebase\JWT\Key;
  5. use Libs\Database;
  6. use Libs\Logger;
  7. use Libs\Payload;
  8. use Psr\Http\Message\ServerRequestInterface;
  9. class JwtAuthMiddleware
  10. {
  11. private string $jwtSecret;
  12. public function __construct()
  13. {
  14. // Sem fallback: a chave precisa estar configurada no ambiente.
  15. $this->jwtSecret = $_ENV['JWT_SECRET'] ?? '';
  16. }
  17. public function __invoke(ServerRequestInterface $request, callable $next)
  18. {
  19. if ($this->jwtSecret === '') {
  20. // Configuração ausente é erro de servidor, não de autenticação.
  21. Logger::error('JWT_SECRET is not configured; rejecting authenticated request');
  22. return Payload::fail('Internal server error', [], 'E_GENERIC', 500);
  23. }
  24. $authHeader = $request->getHeaderLine('Authorization');
  25. if (empty($authHeader) || !preg_match('/Bearer\s+(.*)/', $authHeader, $matches)) {
  26. return Payload::fail('Unauthorized', [], 'E_VALIDATE', 401);
  27. }
  28. $token = $matches[1];
  29. try {
  30. $decoded = JWT::decode($token, new Key($this->jwtSecret, 'HS256'));
  31. $userId = $decoded->sub ?? null;
  32. $userEmail = $decoded->email ?? $decoded->username ?? null;
  33. if (empty($userId) || empty($userEmail)) {
  34. return Payload::fail('Unauthorized', [], 'E_VALIDATE', 401);
  35. }
  36. $pdo = Database::pdo();
  37. // O papel (role) é lido do banco — fonte autoritativa — e não do JWT.
  38. // Assim, alterar/revogar o papel de um usuário tem efeito imediato,
  39. // mesmo que ele ainda possua um token antigo válido.
  40. $stmt = $pdo->prepare("SELECT user_id, user_email, user_role FROM \"user\" WHERE user_id = :user_id AND user_email = :user_email AND user_deleted_at = 'infinity'");
  41. $stmt->execute(['user_id' => $userId, 'user_email' => mb_strtolower(trim($userEmail))]);
  42. $user = $stmt->fetch(\PDO::FETCH_ASSOC);
  43. if (!$user) {
  44. return Payload::fail('Unauthorized', [], 'E_VALIDATE', 401);
  45. }
  46. $request = $request
  47. ->withAttribute('api_user', $user['user_email'])
  48. ->withAttribute('api_user_id', $user['user_id'])
  49. ->withAttribute('user_email', $user['user_email'])
  50. ->withAttribute('user_id', $user['user_id'])
  51. ->withAttribute('user_role', $user['user_role']);
  52. return $next($request);
  53. } catch (\Exception $e) {
  54. // Detalhe do erro vai só para o log; cliente recebe mensagem genérica.
  55. Logger::warning('JWT authentication failed', ['error' => $e->getMessage()]);
  56. return Payload::fail('Unauthorized', [], 'E_VALIDATE', 401);
  57. }
  58. }
  59. }