JWT: Fundamentos e Implementação Prática
JWT (JSON Web Token) é um padrão aberto (RFC 7519) para transmissão segura de informações entre partes. Diferente de sessões tradicionais, o JWT é stateless: o servidor não precisa armazenar dados sobre o token. Um JWT consiste em três partes separadas por pontos: header.payload.signature.
O header contém o tipo do token e o algoritmo de criptografia. O payload armazena as claims (dados) que você deseja transmitir. A signature garante que o token não foi alterado. Aqui está uma implementação prática usando a biblioteca firebase/php-jwt:
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secretKey = 'sua_chave_secreta_super_segura';
// Gerar um JWT
$issuedAt = new DateTimeImmutable();
$expire = $issuedAt->modify('+1 hour')->getTimestamp();
$payload = [
'iat' => $issuedAt->getTimestamp(),
'exp' => $expire,
'iss' => 'sua_aplicacao',
'sub' => 'usuario_123',
'email' => 'usuario@example.com'
];
$token = JWT::encode($payload, $secretKey, 'HS256');
echo "Token: " . $token . "\n";
// Validar um JWT
try {
$decoded = JWT::decode($token, new Key($secretKey, 'HS256'));
echo "Usuário: " . $decoded->email . "\n";
} catch (Exception $e) {
echo "Token inválido: " . $e->getMessage();
}
?>
Boas Práticas com JWT
Sempre use algoritmos assimétricos (RS256) em produção em vez de HS256. Armazene a chave secreta em variáveis de ambiente, nunca no código. Implemente rotação de chaves para aumentar a segurança. Configure um tempo de expiração razoável (15 minutos para access tokens, dias para refresh tokens) e nunca armazene informações sensíveis no payload — ele é apenas codificado, não criptografado.
OAuth 2.0: Autenticação Delegada
OAuth 2.0 é um protocolo de autorização que permite aos usuários autenticar-se em sua aplicação usando contas de terceiros (Google, GitHub, Facebook). Diferente de JWT, que é um formato de token, OAuth 2.0 é um fluxo completo de autenticação com papéis bem definidos: Resource Owner (usuário), Client (sua app), Authorization Server (Google, GitHub) e Resource Server (API protegida).
O fluxo mais comum é o "Authorization Code Flow". O usuário é redirecionado para o provedor OAuth, que valida suas credenciais e retorna um código de autorização. Sua aplicação troca esse código por um access token. Aqui está um exemplo usando Google OAuth com a biblioteca google/apiclient:
<?php
require 'vendor/autoload.php';
$clientId = 'seu_client_id.apps.googleusercontent.com';
$clientSecret = 'sua_client_secret';
$redirectUri = 'https://sua-app.com/oauth-callback';
$client = new Google_Client();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
$client->setRedirectUri($redirectUri);
$client->addScope('email');
$client->addScope('profile');
// 1. Gerar URL de autenticação
if (!isset($_GET['code'])) {
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
exit;
}
// 2. Troca do código por token
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
if (!isset($token['error'])) {
$client->setAccessToken($token);
$oauth2 = new Google_Service_Oauth2($client);
$userInfo = $oauth2->userinfo->get();
$_SESSION['user_email'] = $userInfo->email;
$_SESSION['user_name'] = $userInfo->name;
header('Location: /dashboard');
exit;
}
}
?>
Combinando JWT e OAuth 2.0 em APIs
A abordagem ideal é usar OAuth 2.0 para autenticação (permitir login) e JWT para autorização (validar requisições subsequentes). Após o usuário fazer login via OAuth, você emite um JWT que ele usa nas requisições à API. Isso combina a segurança do OAuth com a eficiência do JWT.
Aqui está um exemplo prático de um middleware que valida JWTs em requisições:
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class JWTMiddleware {
private $secretKey = 'sua_chave_secreta';
public function validateToken($request) {
$authHeader = $request->getHeader('Authorization');
if (!$authHeader) {
return ['valid' => false, 'message' => 'Token não fornecido'];
}
// Formato: "Bearer token_aqui"
$parts = explode(' ', $authHeader[0] ?? '');
if (count($parts) !== 2 || $parts[0] !== 'Bearer') {
return ['valid' => false, 'message' => 'Formato inválido'];
}
try {
$decoded = JWT::decode($parts[1], new Key($this->secretKey, 'HS256'));
return ['valid' => true, 'user' => $decoded];
} catch (Exception $e) {
return ['valid' => false, 'message' => 'Token expirado ou inválido'];
}
}
}
// Uso em um endpoint
$middleware = new JWTMiddleware();
$validation = $middleware->validateToken($_SERVER);
if (!$validation['valid']) {
http_response_code(401);
echo json_encode(['error' => $validation['message']]);
exit;
}
// Token válido, prosseguir
$userId = $validation['user']->sub;
?>
Refresh Tokens
Nunca mantenha access tokens com expiração muito longa. Use refresh tokens, armazenados com segurança (httpOnly cookies), para renovar access tokens expirados. O servidor deve validar o refresh token antes de emitir um novo access token, rejeitando tokens revogados.
Conclusão
JWT é excelente para APIs stateless e aplicações modernas, oferecendo eficiência e escalabilidade. OAuth 2.0 é o padrão para autenticação segura com provedores terceirizados, eliminando a necessidade de armazenar senhas. Combinando ambos, você cria um sistema robusto: OAuth 2.0 autentica o usuário inicialmente, JWT autentica requisições subsequentes. Sempre implemente expiração de tokens, use HTTPS, valide rigorosamente no backend e mantenha secrets fora do controle de versão.