1001Ferramentas
🔐Geradores

OAuth PKCE Verifier + Challenge

Gere um par OAuth PKCE (code_verifier e code_challenge S256) para fluxos de autenticação seguros em apps móveis e SPAs. Mitiga ataques de interceptação de código.

verifier + challenge

O que é PKCE e por que existe

PKCE (Proof Key for Code Exchange, pronuncia-se "pixy") é a extensão do OAuth 2.0 descrita na RFC 7636 (2015) que protege o fluxo de Authorization Code para clientes que não conseguem manter um segredo — ou seja, aplicativos mobile nativos, apps de desktop e SPAs. Em um cliente confidencial clássico o servidor guarda um client_secret embutido em um backend que o usuário nunca vê, e é esse segredo que prova ao endpoint de token que a troca do authorization code é legítima. Clientes públicos não têm onde esconder nada: o que vai dentro do binário pode ser extraído, o que vai em JavaScript fica visível no DevTools. PKCE substitui o client secret estático por uma prova por requisição.

Sem PKCE, um cliente público fica exposto ao ataque de interceptação de código: um aplicativo malicioso registrado no mesmo esquema de URL custom, ou uma extensão de browser comprometida, captura o authorization code do redirect e troca por tokens antes do app legítimo. Com PKCE, conhecer o código não basta — o atacante também precisaria do verifier, que nunca saiu do cliente original.

O protocolo verifier / challenge

  • O cliente gera um code_verifier: string aleatória de alta entropia com 43 a 128 caracteres do conjunto unreserved [A-Z a-z 0-9 - . _ ~]. Aproximadamente 32 bytes de CSPRNG codificados em base64url dão 43 caracteres e ~256 bits de entropia — o mínimo recomendado.
  • O cliente deriva o code_challenge = BASE64URL(SHA256(verifier)) com code_challenge_method=S256. O método plain (challenge = verifier) é permitido pela RFC mas desencorajado, e proibido no OAuth 2.1.
  • O cliente envia code_challenge em /authorize; o authorization server vincula o challenge ao código emitido.
  • Em seguida o cliente chama /token com o código e o verifier original; o servidor confere SHA256(verifier) == challenge_armazenado antes de emitir tokens.

Notas de implementação

O verifier precisa ser único por requisição de autorização — reusar entre fluxos destrói a prova e permitiria casar um challenge vazado com um código novo. Mantenha em memória ou em sessionStorage com TTL curto; nunca persista por longo prazo e nunca envie pelo front channel. Bibliotecas maduras (oidc-client-ts, AppAuth iOS/Android, pkce-challenge no npm, authlib em Python) cuidam do pareamento para você. PKCE não substitui HTTPS — os dois lados do fluxo continuam tendo que rodar sobre TLS, caso contrário um atacante na rede lê tudo de qualquer jeito.

PKCE vs parâmetro state

PKCE às vezes é confundido com o parâmetro state do OAuth, mas eles resolvem problemas diferentes. state é um token CSRF: vincula a requisição /authorize ao callback subsequente para que um atacante não engane a vítima a completar o fluxo de outra pessoa. PKCE vincula a troca por token ao iniciador original: mesmo com um código roubado, não há como trocar por token sem o verifier. Você quer os dois em todo fluxo de cliente público — são complementares, não alternativas.

Perguntas frequentes

Preciso de PKCE em um app web com backend que já tem client_secret? A RFC 7636 original marca como MUST para clientes públicos e RECOMMENDED para clientes confidenciais. O draft do OAuth 2.1 promove para MUST em todos os clientes independentemente do tipo, porque o custo marginal é desprezível e a defesa é genuinamente útil.

S256 ou plain? Sempre S256. O método plain só existe para dispositivos embarcados legados que não conseguem calcular SHA-256, o que em 2026 é essencialmente nada. A maioria dos authorization servers recusa plain diretamente.

Posso reusar o mesmo verifier? Não. Toda a garantia colapsa se o verifier for previsível ou repetido. Gere um novo valor aleatório de 32 bytes para cada chamada a /authorize.

E se o usuário abrir duas abas do mesmo app ao mesmo tempo? Cada aba precisa de seu próprio verifier; vincule por state ou por aba no storage, senão a segunda troca falha.

O verifier gerado aqui é enviado para algum lugar? Não. Tanto o verifier quanto o challenge são calculados no seu navegador via crypto.getRandomValues() e SubtleCrypto.digest — nada é enviado para um servidor.

Ferramentas Relacionadas