Tokens JWT Explicados: Autenticação para Aplicações Web Modernas

· 12 min de leitura

Índice

JSON Web Tokens (JWT) tornaram-se o padrão de facto para autenticação em aplicações web modernas. Eles fornecem uma maneira compacta e segura para URL de representar declarações entre duas partes, permitindo autenticação sem estado que escala horizontalmente sem armazenamento de sessão compartilhado.

Seja você construindo uma API REST, uma arquitetura de microsserviços ou uma aplicação de página única, entender JWTs é essencial para implementar autenticação segura e escalável. Este guia abrangente explica como JWTs funcionam, sua estrutura, considerações de segurança e melhores práticas prontas para produção.

O Que É JWT?

JWT (pronunciado "jot") é um padrão aberto (RFC 7519) que define um formato compacto e autocontido para transmitir informações com segurança entre partes como um objeto JSON. A informação é assinada digitalmente usando uma chave secreta (HMAC) ou um par de chaves pública/privada (RSA ou ECDSA), garantindo a integridade e autenticidade do token.

Diferentemente da autenticação tradicional baseada em sessão onde o servidor mantém o estado da sessão em memória ou banco de dados, JWTs são sem estado. Todas as informações necessárias para verificar a identidade do usuário estão contidas no próprio token.

Casos de Uso Principais para JWT

Dica profissional: Use nosso Decodificador JWT para inspecionar e validar tokens durante o desenvolvimento. Ele ajuda você a entender a estrutura e detectar problemas comuns antes de chegarem à produção.

Estrutura do JWT Detalhada

Um JWT consiste em três partes distintas separadas por pontos (.): cabeçalho.payload.assinatura

Veja como um JWT completo se parece:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRGV2ZWxvcGVyIiwiZW1haWwiOiJqYW5lQGV4YW1wbGUuY29tIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzEwNTkwNDAwLCJleHAiOjE3MTA2NzY4MDB9.4Adcj0mYZ8s5vxjKvV8pF7jKX9s8vZ5xJ3kL9mN2pQ4

Parte 1: Cabeçalho

O cabeçalho normalmente contém dois campos que descrevem as operações criptográficas do token:

{
  "alg": "HS256",
  "typ": "JWT"
}

Este objeto JSON é codificado em Base64Url para formar a primeira parte do JWT. A codificação o torna seguro para URL e compacto para transmissão em cabeçalhos HTTP.

Parte 2: Payload

O payload contém declarações—afirmações sobre o usuário e metadados adicionais. As declarações são categorizadas em três tipos:

{
  "sub": "1234567890",
  "name": "Jane Developer",
  "email": "[email protected]",
  "role": "admin",
  "iat": 1710590400,
  "exp": 1710676800
}

Declarações registradas são predefinidas pela especificação JWT e fornecem informações padrão:

Declarações públicas são declarações personalizadas que devem ser definidas no Registro IANA de JSON Web Token ou usar nomes resistentes a colisão (como URLs).

Declarações privadas são declarações personalizadas acordadas entre as partes, como role, permissions ou organizationId.

Importante: O payload é apenas codificado em Base64Url, não criptografado. Nunca armazene informações sensíveis como senhas, números de cartão de crédito ou números de segurança social em um payload JWT.

Parte 3: Assinatura

A assinatura garante que o token não foi alterado. Ela é criada pegando o cabeçalho codificado, payload codificado, uma chave secreta e aplicando o algoritmo especificado no cabeçalho:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

A assinatura permite que o destinatário verifique que o remetente é quem afirma ser e que a mensagem não foi alterada no caminho.

Comparação de Algoritmos de Assinatura

Algoritmo Tipo Nível de Segurança Caso de Uso Gerenciamento de Chaves
HS256 Simétrico (HMAC) Bom Serviço único, APIs internas Segredo compartilhado
RS256 Assimétrico (RSA) Muito Bom Múltiplos serviços, APIs públicas Par de chaves pública/privada
ES256 Assimétrico (ECDSA) Excelente Requisitos de alta segurança Par de chaves pública/privada
PS256 Assimétrico (RSA-PSS) Excelente Aplicações modernas Par de chaves pública/privada

Como Funciona a Autenticação JWT

Entender o fluxo completo de autenticação ajuda você a implementar JWTs corretamente e solucionar problemas de forma eficaz.

O Fluxo Completo de Autenticação

  1. Login do Usuário: O cliente envia credenciais (nome de usuário/senha) para o endpoint de autenticação
  2. Verificação de Credenciais: O servidor valida as credenciais contra o banco de dados
  3. Geração de Token: Após autenticação bem-sucedida, o servidor cria um JWT contendo informações do usuário e declarações
  4. Entrega do Token: O servidor envia o JWT de volta ao cliente (normalmente no corpo da resposta)
  5. Armazenamento do Token: O cliente armazena o JWT (em memória, localStorage ou cookies httpOnly)
  6. Requisições Autenticadas: Para requisições subsequentes, o cliente inclui o JWT no cabeçalho Authorization
  7. Verificação do Token: O servidor valida a assinatura do JWT e verifica a expiração
  8. Acesso Concedido: Se válido, o servidor processa a requisição usando as declarações no token

Exemplo de Requisição de Autenticação

POST /api/auth/login HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "securePassword123"
}

Exemplo de Resposta de Autenticação

HTTP/1.1 200 OK
Content-Type: application/json

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 3600,
  "tokenType": "Bearer"
}

Exemplo de Requisição de API Autenticada

GET /api/users/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

JWT vs Autenticação Baseada em Sessão

Escolher entre JWT e autenticação baseada em sessão depende da arquitetura, escala e requisitos da sua aplicação.

Aspecto JWT Baseada em Sessão
Estado Sem estado (servidor não armazena tokens) Com estado (servidor armazena dados de sessão)
Escalabilidade Excelente (não precisa de armazenamento compartilhado) Requer armazenamento de sessão (Redis, banco de dados)
Revogação Difícil (requer lista negra ou expiração curta) Fácil (deletar sessão do armazenamento)
Tamanho Maior (enviado com cada requisição) Menor (apenas ID de sessão)
Cross-domain Fácil (compatível com CORS) Complexo (restrições de domínio de cookie)
Apps móveis Ideal (não precisa de suporte a cookies) Desafiador (manipulação de cookies)
Segurança Requer implementação cuidadosa Padrões bem estabelecidos

Quando Usar JWT

Quando Usar Sessões

Implementando JWT na Sua Aplicação

Vamos percorrer exemplos práticos de implementação em linguagens de programação e frameworks populares.

Node.js com Express

const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();

const SECRET_KEY = process.env.JWT_SECRET;

// Endpoint de login
app.post('/api/auth/login', async (req, res) => {
  const { email, password } = req.body;
  
  // Verificar credenciais (pseudo-código)
  const user = await verifyCredentials(email, password);
  
  if (!user) {
    return res.status(401).json({ error: 'Credenciais inválidas' });
  }
  
  // Gerar JWT
  const token = jwt.sign(
    {
      sub: user.id,
      email: user.email,
      role: user.role
    },
    SECRET_KEY,
    { expiresIn: '1h' }
  );
  
  res.json({ accessToken: token });
});

// Middleware de autenticação
const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'Nenhum token fornecido' });
  }
  
  jwt.verify(token, SECRET_KEY, (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Token inválido' });
    }
    req.user = user;
    next();
  });
};

// Rota protegida
app.get('/api/users/profile', authenticateToken, (req, res) => {
  res.json({ user: req.user });
});

Python com Flask

from flask import Flask, request, jsonify
import jwt
import datetime
import os

app = Flask(__name__)
SECRET_KEY = os.getenv('JWT_SECRET')

@app.route('/api/auth/login', methods=['POST'])
def login():
    data = request.get_json()
    email = data.get('email')
    password = data.get('password')
    
    # Verificar credenciais
    user = verify_credentials(email, password)
    
    if not user:
        return jsonify({'error': 'Credenciais inválidas'}), 401
    
    # Gerar JWT
    token = jwt.encode({
        'sub': user['id'],
        'email': user['email'],
        'role': user['role'],
        'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
    }, SECRET_KEY, algorithm='HS256')
    
    return jsonify({'accessToken': token})

def token_required(f):
    def decorator(*args, **kwargs):
        token = request.headers.get('Authorization')
        
        if not token:
            return jsonify({'error': 'Nenhum token fornecido'}), 401
        
        try:
            token = token.split(' ')[1]
            data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
            request.user = data
        except jwt.ExpiredSignatureError:
            return jsonify({'error': 'Token expirado'}), 403
        except jwt.InvalidTokenError:
            return jsonify({'error': 'Token inválido'}), 403
        
        return f(*args, **kwargs)
    
    decorator.__name__ = f.__name__
    return decorator

@app.route('/api/users/profile')
@token_required
def profile():
    return jsonify({'user': request.user})

Dica rápida: Sempre use variáveis de ambiente para chaves secretas. Nunca as codifique diretamente no seu código-fonte ou as envie para controle de versão. Use ferramentas como Gerador de Senhas para criar segredos fortes.

We use cookies for analytics. By continuing, you agree to our Privacy Policy.