Pular para o conteúdo
Voltar para o acervo
Engenharia6 min de leituraPasso 18 de 22

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.

  1. OAuth2autorização delegadaDar a um app acesso limitado ao seu recurso sem entregar a senha. Não é login.
  2. OIDCloginCamada fina sobre o OAuth2 que acrescenta o token de identidade. É o que está por trás do Entrar com o Google.
  3. JWTformatoAssinado, não criptografado: qualquer um lê. Garante integridade e autenticidade, não sigilo.
Três siglas, três problemas diferentes. Confundi-las é a origem de metade dos erros.

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

Fala comigo

Dúvida sobre o artigo? Me chama no WhatsApp

Sem formulário e sem lista de e-mail. Se você discorda de alguma coisa que eu escrevi, ou quer contar como resolveu aí, a conversa é direta comigo.

Abrir conversa