OAuth2, OIDC e JWT sem mistério
As três siglas mais confundidas de autenticação, o que cada uma resolve, e os erros que aparecem em quase todo código.
A confusão começa no nome. OAuth2 tem "auth" no nome e não é sobre login. OIDC é sobre login e quase ninguém sabe o nome completo. E JWT não é nem uma coisa nem outra: é um formato de token que os dois usam.
Vamos por partes.
Autenticação não é autorização
Autenticação (authn) responde: quem é você?
Autorização (authz) responde: o que você pode fazer?
São coisas diferentes, resolvidas por mecanismos diferentes, e o erro clássico é fazer bem a primeira e esquecer a segunda em algum endpoint, que é exatamente o IDOR do artigo anterior.
OAuth2: autorização delegada
O problema que o OAuth2 resolve: dar a um aplicativo acesso limitado a um recurso seu sem entregar a sua senha.
Você quer que o app de agenda leia o seu Google Calendar. Antes do OAuth, a solução era você dar a sua senha do Google ao app, o que dava a ele acesso a tudo, para sempre, sem forma de revogar.
Com OAuth2, você é redirecionado ao Google, autentica lá, autoriza escopos específicos ("ler calendário"), e o app recebe um token de acesso limitado e revogável.
Os atores: o dono do recurso (você), o cliente (o app), o servidor de autorização (o Google) e o servidor de recurso (a API do Calendar).
O fluxo que você deve usar hoje: Authorization Code com PKCE. Para todo tipo de cliente: web, móvel, SPA.
O PKCE existe para resolver um ataque específico: em aplicativo móvel, outro app poderia interceptar o código de autorização no redirecionamento. Com PKCE, o cliente gera um segredo aleatório, envia o hash dele no início e o valor original no final. Quem interceptou o código não tem o segredo.
Os fluxos que você não deve mais usar: implícito (devolve o token direto na URL, que vaza em histórico e log) e password grant (o app recebe a senha, o que anula o propósito).
OIDC: login em cima do OAuth2
OpenID Connect é uma camada fina sobre o OAuth2 que acrescenta uma coisa: o token de identidade.
Enquanto o token de acesso diz "o portador deste token pode chamar essas APIs", o token de identidade diz "esta pessoa é fulano, autenticada em tal momento, por tal método".
É por isso que "Entrar com o Google" é OIDC, não OAuth2 puro. E é por isso que usar OAuth2 puro para login é um erro conhecido: o token de acesso não foi projetado para provar identidade, e usá-lo assim abre a porta para confusão de deputado.
O OIDC também padroniza o endpoint de descoberta (/.well-known/openid-configuration), o que faz a integração ser quase automática em bibliotecas modernas.
JWT: o formato
Três partes separadas por ponto: cabeçalho, dados (claims) e assinatura, cada uma em base64url.
O mal-entendido fundamental: JWT é assinado, não criptografado. Qualquer um lê o conteúdo. Cole um num decodificador e veja.
Nunca coloque dado sensível ali dentro.
O que a assinatura garante é integridade: ninguém alterou o conteúdo. E autenticidade: foi emitido por quem tem a chave.
- OAuth2autorização delegadaDar a um app acesso limitado ao seu recurso sem entregar a senha. Não é login.
- OIDCloginCamada fina sobre o OAuth2 que acrescenta o token de identidade. É o que está por trás do Entrar com o Google.
- JWTformatoAssinado, não criptografado: qualquer um lê. Garante integridade e autenticidade, não sigilo.
Os quatro erros de JWT
1. Aceitar o algoritmo que vem no token.
O cabeçalho diz qual algoritmo foi usado. Se a sua biblioteca confia nisso, um atacante manda "alg": "none" e forja o token que quiser. Ou, pior, troca RS256 por HS256 e usa a chave pública (que é pública) como segredo de HMAC.
Correção: fixe o algoritmo esperado no servidor. Nunca leia do token.
2. Não validar aud e iss.
Um token perfeitamente válido, emitido por um provedor legítimo, para outro serviço, não pode valer no seu. Se você só verifica a assinatura, ele vale.
3. Expiração longa demais, ou nenhuma.
Token de acesso deve durar minutos. Renovar é papel do refresh token, que tem armazenamento e rotação diferentes.
Um token de acesso com validade de trinta dias é uma credencial permanente circulando em log, em cache de proxy e em histórico de navegador.
4. Achar que dá para revogar.
Não dá. JWT é válido até expirar: essa é a natureza dele, e é a razão pela qual ele escala sem consultar o emissor.
Se você precisa de revogação imediata (usuário demitido, conta comprometida), precisa de uma lista de bloqueio consultada a cada requisição. E aí você tem estado no servidor de novo, que era exatamente o que o JWT prometia evitar.
A reflexão que decorre: em muitos sistemas, uma sessão tradicional no Redis é mais simples, mais segura e igualmente rápida. JWT resolve um problema específico: validar sem consultar o emissor, em arquitetura distribuída ou entre organizações. Se você não tem esse problema, talvez não precise dele.
Onde guardar o token
Duas opções, com trocas reais:
Cookie HttpOnly + Secure + SameSite: JavaScript não consegue ler, então um XSS não rouba o token. Vulnerável a CSRF, mitigado pelo SameSite. É a recomendação padrão para aplicação web.
localStorage: qualquer XSS lê. Não sofre CSRF. É comum em SPA e é uma escolha pior do que a maioria assume.
Se você guarda token em localStorage, o seu XSS de baixa severidade virou comprometimento de conta.
Modelos de autorização
Terminando com o "o que você pode fazer".
RBAC, baseado em papel. Admin, editor, leitor. Simples, e vira bagunça quando os casos particulares aparecem: "editor, mas só do departamento X", "leitor, exceto dados financeiros". A resposta costuma ser criar mais papéis, e em dois anos você tem duzentos.
ABAC, baseado em atributo. A regra avalia atributos: departamento, valor da transação, horário, localização. Mais expressivo, mais difícil de auditar: responder "quem pode ver isto?" exige avaliar regras.
ReBAC, baseado em relação. "Você pode editar este documento porque é dono da pasta que o contém." É o modelo do Zanzibar, do Google, e é o que resolve compartilhamento hierárquico sem explodir em papéis. Implementações abertas existem (SpiceDB, OpenFGA, Ory Keto).
Para a maioria dos sistemas, RBAC com alguns atributos resolve. ReBAC vale quando o seu produto tem compartilhamento e hierarquia como funcionalidade central.
O checklist de cinco minutos
No seu código, hoje:
→ O algoritmo do JWT está fixo no servidor?
→ aud e iss são validados?
→ Qual é o TTL do token de acesso?
→ Existe caminho de revogação, e ele foi testado?
→ O token está em cookie HttpOnly ou em localStorage?
→ O fluxo de OAuth é Authorization Code com PKCE?
→ A autorização é verificada no servidor em todos os endpoints, ou só nos que passam pela tela?
Leia depois disto
- EngenhariaPasso 20Legado: caracterizar, costurar e estrangularTrês técnicas que permitem mexer com segurança num sistema que você não escreveu, não entende, e não pode parar.Ler artigo
- EngenhariaPasso 19Testes que valem o que custamTeste não existe para provar que o código está certo. Existe para você poder mudá-lo amanhã sem medo. Essa mudança de objetivo reorganiza tudo.Ler artigo
- EngenhariaPasso 17As vulnerabilidades que aparecem de verdadeA maior parte dos vazamentos que viram notícia não usa técnica sofisticada. Usa uma destas seis falhas, e as seis têm correção conhecida e barata.Ler artigo