Anúncios incomodam? Ir Sem anúncios Hoje

Fluxos OAuth 2.0 Código de Autorização, PKCE e Clientes de Credenciais

Atualizado em

Um guia para desenvolvedores sobre escolher a correta fluxo OAuth 2.0. Aborda o fluxo de autorização (aplicativos web), PKCE (aplicativos de página única e móveis) e credenciais do cliente (servidor para servidor), com exemplos de código funcionais e erros que afetam você mais tarde.

Fluxos OAuth 2.0: Código de Autorização, PKCE e Credenciais do Cliente 1
ANUNCIADO Remover?

Três fluxos cobrem 95% de casos reais de uso do OAuth 2.0. A especificação define mais, mas os demais estão desatualizados, são casos extremos ou ambos. Escolha o certo desde o início e você pula uma refatoração dolorosa quando as exigências de autenticação mudam.

FluxoQuando usá-loEnvolve o usuário?Precisa de client_secret?
Código de AutorizaçãoAplicativo web com servidor backendSimSim — permanece no servidor
Código de Autorização + PKCEAplicativo SPA, app móvel, qualquer cliente públicoSimNão
Credenciais do ClienteEntre serviços (sem usuário)NãoSim — permanece no servidor

Fluxo de Código de Autorização

O fluxo padrão para aplicativos web com backend. O token de acesso e o cliente secreto nunca tocam o navegador — a troca do token ocorre no lado do servidor. Isso é o ponto principal.

Etapa 1: Redirecione o usuário

Construa uma URL de autorização e redirecione o navegador para ela:

GET https://accounts.example.com/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/callback
  &scope=openid+profile+email
  &state=RANDOM_CSRF_TOKEN

O state o valor é sua defesa contra CSRF no redirecionamento. Gerar uma string aleatória fresca por fluxo, armazenar na sessão e verificar quando o usuário retornar. Se você pular essa verificação, um atacante poderá conduzir o usuário por um fluxo usando o código de autorização do atacante — vinculando silenciosamente a conta do usuário à identidade do atacante.

Etapa 2: Trate a resposta de callback

O servidor de autorização redireciona de volta para seu redirect_uri com um código de curta duração:

GET https://yourapp.com/callback?code=AUTH_CODE&state=SAME_STATE_YOU_SENT

Verificar state corresponde ao que você armazenou. Em seguida, troque o código no lado do servidor.

Etapa 3: Troque o código por tokens (apenas no lado do servidor)

POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://yourapp.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

A resposta:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200a12b3c...",
  "scope": "openid profile email"
}

Armazene ambos os tokens no lado do servidor. Quando access_token expira (verifique expires_in), use o refresh_token para obter um novo sem precisar fazer o usuário passar novamente pelo login.

PKCE — Para Clientes que Não Podem Manter Segredos

Um SPA ou app móvel não tem um local seguro para um client_secret. Qualquer pessoa pode abrir DevTools e encontrá-lo no seu bundle JS. Qualquer pessoa pode decompilar seu APK. O PKCE (Proof Key for Code Exchange, pronunciado "pixy") resolve isso com um desafio criptográfico único — sem necessidade de segredo compartilhado.

O fluxo é idêntico ao de Código de Autorização com duas adições: um code_verifier (string aleatória que você gera) e um code_challenge (hash SHA-256 do verificador, codificado em base64url). Você envia o desafio antecipadamente e prova que possui o verificador no momento da troca.

Etapa 1: Gerar o verificador e o desafio

function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return base64url(array);
}

async function generateCodeChallenge(verifier) {
  const data = new TextEncoder().encode(verifier);
  const digest = await crypto.subtle.digest('SHA-256', data);
  return base64url(new Uint8Array(digest));
}

function base64url(buffer) {
  return btoa(String.fromCharCode(...buffer))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
}

// Usage
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Store codeVerifier in memory — NOT localStorage

Armazene o code_verifier na memória — uma variável de escopo de módulo, não no localStorage. Você o enviará no momento da troca do token.

Etapa 2: Solicitação de autorização — adicione o desafio

GET https://accounts.example.com/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/callback
  &scope=openid+profile
  &state=RANDOM_CSRF_TOKEN
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256

Etapa 3: Troca do token — verificador em vez de client_secret

POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://yourapp.com/callback
&client_id=YOUR_CLIENT_ID
&code_verifier=ORIGINAL_VERIFIER_YOU_GENERATED

O servidor de autorização hasha o verificador e o compara com o desafio que você enviou na etapa 2. Um atacante que interceptou o código de autorização não sabe o que é o verificador — não pode usá-lo para trocar.

Valor a saber: o OAuth 2.1 (a modernização em andamento do OAuth 2.0) exige PKCE para todos os fluxos envolvendo redirecionamentos. Se você estiver escrevendo código novo, use PKCE independentemente de seu provedor exigir isso atualmente.

Credenciais do Cliente — Sem Usuário, Sem Problema

Tarefas em segundo plano, microserviços chamando outros microserviços, tarefas cron que acessam uma API — nenhuma dessas envolve um usuário. O fluxo certo é o de Credenciais do Cliente: o serviço se autentica diretamente usando seu próprio ID e segredo do cliente.

POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&scope=api:read api:write

Resposta:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 86400
}

Sem token de atualização — quando ele expira, solicite um novo. O erro comum: chamar o ponto de acesso em cada chamada à API. Armazene o token, verifique a expiração antes de cada requisição, e reconsiga apenas quando estiver prestes a expirar. Uma requisição de token por dia (ou por hora, dependendo de expires_in) em vez de uma por requisição:

let cachedToken = null;
let tokenExpiresAt = 0;

async function getAccessToken() {
  // Refresh 30 seconds before actual expiry
  if (cachedToken && Date.now() < tokenExpiresAt - 30_000) {
    return cachedToken;
  }

  const res = await fetch('https://accounts.example.com/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: process.env.CLIENT_ID,
      client_secret: process.env.CLIENT_SECRET,
      scope: 'api:read api:write',
    }),
  });

  const data = await res.json();
  cachedToken = data.access_token;
  tokenExpiresAt = Date.now() + (data.expires_in * 1000);
  return cachedToken;
}

Erros que Desenvolvedores Fazem de Fato

Armazenar tokens no localStorage

Qualquer vulnerabilidade XSS — em seu próprio código, em uma dependência, em um script de gerenciador de tags — pode ler tudo no localStorage. Para SPAs: armazene o token de acesso na memória (uma variável de escopo de módulo que desaparece ao recarregar). Use cookies httpOnly para tokens de atualização quando tiver um backend que os possa definir. O JavaScript não consegue ler cookies httpOnly.

Usar o fluxo implícito

O fluxo implícito retorna tokens diretamente na fragmentação da URL (#access_token=...). Esses tokens acabam na história do navegador, nos logs de acesso do servidor e nos cabeçalhos Referer. Foi descontinuado na RFC 9700. Não há razão para usar o fluxo implícito em código novo. Use PKCE.

Pular a validação do estado

Baixar como .yml state A validação na resposta de callback, um atacante pode criar uma URL de redirecionamento que complete um fluxo OAuth usando seu próprio código de autorização. O resultado: a conta do usuário é vinculada à identidade do atacante no provedor. Gerar um novo por fluxo, armazenar na sessão e verificar na resposta de callback.

Colocar client_secret no código do frontend

Não existe tal coisa como um segredo que vive no navegador. A minificação não o oculta. A obfuscção não o protege. Se seu ambiente for um navegador ou um app móvel, você tem um cliente público — use PKCE e omita completamente o client_secret. Isso não é uma solução de contorno; é como o especificação pretende que os clientes públicos funcionem.

Não lidar com a expiração do token de forma proativa

Cada token de acesso tem um expires_in valor. Se você mantiver um token até que ele falhe com um 401 e então re-autentique, os usuários terão erros misteriosos. Verifique a expiração antes de fazer requisições, renove proativamente (30 segundos antes da expiração é um bom buffer) e trate o caso raro em que um token de atualização tenha expirado.

Inspeção de Tokens enquanto você trabalha

A maioria dos provedores de OAuth emite JWTs como tokens de acesso. O payload é codificado em base64url e legível sem a chave privada — apenas a assinatura exige a chave para verificar. Quando você está debugando um fluxo e deseja ver as declarações, escopos ou expiração em um token, cole-o no Decodificador JWT.

Se você estiver testando manualmente PKCE e precisar verificar o que um valor codificado em base64url code_challenge decodifica, o Conversor de Base64 trata variantes padrão e seguras para URLs.

A Versão Curta

Uma pergunta determina o fluxo certo: seu ambiente tem um local seguro para manter um segredo?

  • Servidor backend → Código de Autorização ou Credenciais do Cliente (o segredo permanece no servidor)
  • Navegador ou app móvel → Código de Autorização + PKCE (sem segredo algum)
  • Sem usuário envolvido → Credenciais do Cliente

O PKCE funciona para todos os fluxos envolvendo redirecionamento — não há desvantagem em usá-lo mesmo quando seu provedor ainda não o exige. O OAuth 2.1 o exigirá.

Quer eliminar anúncios? Fique sem anúncios hoje mesmo

Instale nossas extensões

Adicione ferramentas de IO ao seu navegador favorito para acesso instantâneo e pesquisa mais rápida

Ao Extensão do Chrome Ao Extensão de Borda Ao Extensão Firefox Ao Extensão Opera

O placar chegou!

Placar é uma forma divertida de acompanhar seus jogos, todos os dados são armazenados em seu navegador. Mais recursos serão lançados em breve!

ANUNCIADO Remover?
ANUNCIADO Remover?
ANUNCIADO Remover?

Notícias com destaques técnicos

Envolver-se

Ajude-nos a continuar fornecendo ferramentas gratuitas valiosas

Compre-me um café
ANUNCIADO Remover?