Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

OAuth com PKCE no Node.js

Atualizado em: 6 de outubro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

OAuth permite que uma aplicação obtenha acesso limitado a recursos de um usuário sem receber sua senha. PKCE, ou Proof Key for Code Exchange, protege o Authorization Code Flow contra interceptação do código. Em aplicações Node.js, ele é indicado para clientes públicos, SPAs, aplicativos móveis e também pode reforçar clientes confidenciais.

Participantes

  • resource owner: usuário;
  • client: aplicação;
  • authorization server: autentica e emite tokens;
  • resource server: API protegida.

OAuth delega autorização. Para login, use OpenID Connect e valide ID token, nonce, issuer e audience.

Code verifier

import crypto from 'node:crypto';

function base64url(buffer) {
  return buffer.toString('base64url');
}

const codeVerifier = base64url(crypto.randomBytes(32));
const codeChallenge = base64url(
  crypto.createHash('sha256').update(codeVerifier).digest(),
);

Use método S256. Não use verifier curto, previsível ou reutilizado.

State

state vincula a resposta à tentativa iniciada e reduz CSRF:

const state = crypto.randomBytes(24).toString('base64url');

req.session.oauth = {
  state,
  codeVerifier,
  createdAt: Date.now(),
};

Armazene em sessão server-side ou cookie protegido, com TTL curto.

Redirecionamento

const authorizationUrl = new URL('https://auth.example.com/authorize');
authorizationUrl.searchParams.set('response_type', 'code');
authorizationUrl.searchParams.set('client_id', CLIENT_ID);
authorizationUrl.searchParams.set('redirect_uri', REDIRECT_URI);
authorizationUrl.searchParams.set('scope', 'openid profile email');
authorizationUrl.searchParams.set('state', state);
authorizationUrl.searchParams.set('code_challenge', codeChallenge);
authorizationUrl.searchParams.set('code_challenge_method', 'S256');

res.redirect(authorizationUrl.toString());

A redirect URI deve estar previamente registrada e ser comparada exatamente pelo provedor.

Callback

app.get('/auth/callback', async (req, res) => {
  const { code, state } = req.query;
  const pending = req.session.oauth;

  if (!pending || state !== pending.state) {
    res.status(400).send('Fluxo inválido');
    return;
  }

  const tokens = await exchangeCode({
    code,
    codeVerifier: pending.codeVerifier,
  });

  delete req.session.oauth;
  await createApplicationSession(tokens);
  res.redirect('/');
});

Consuma state e verifier uma única vez, inclusive em falhas.

Troca do código

const body = new URLSearchParams({
  grant_type: 'authorization_code',
  code,
  redirect_uri: REDIRECT_URI,
  client_id: CLIENT_ID,
  code_verifier: codeVerifier,
});

const response = await fetch('https://auth.example.com/token', {
  method: 'POST',
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
  body,
  signal: AbortSignal.timeout(5000),
});

Clientes confidenciais também autenticam o client por método suportado. Proteja client secret em secret manager.

Validação da resposta

Não basta verificar status 200. Limite tamanho, valide Content-Type, schema, token_type, expires_in e campos obrigatórios. Nunca registre tokens.

OpenID Connect

Para autenticação, inclua openid no scope e valide o ID token:

  • assinatura;
  • issuer;
  • audience;
  • expiração;
  • nonce;
  • azp quando aplicável;
  • algoritmo permitido.

Nonce

const nonce = crypto.randomBytes(24).toString('base64url');
req.session.oauth.nonce = nonce;
authorizationUrl.searchParams.set('nonce', nonce);

Compare com a claim do ID token e consuma uma única vez.

Discovery

OpenID Connect Discovery publica endpoints e JWKS. Configure o issuer confiável e busque o documento com timeout e cache. Não aceite issuer fornecido pelo usuário.

Scopes mínimos

Solicite apenas o necessário. Scopes amplos aumentam impacto de vazamento e assustam usuários. Separe consentimento incremental para recursos opcionais.

Refresh token

Alguns provedores emitem refresh token conforme scope e tipo de cliente. Armazene com criptografia ou token opaco protegido, rotacione quando suportado e revogue no desligamento da integração.

Token no backend

Em aplicações web server-side, prefira manter tokens do provedor no backend e criar uma sessão própria para o navegador. Isso reduz exposição de access token ao frontend.

Redirect URI

Não aceite um parâmetro arbitrário de retorno e o use como redirect_uri. Para redirecionar internamente após login, valide caminhos relativos ou uma allowlist.

Open redirect

function safeReturnPath(value) {
  if (typeof value !== 'string') return '/';
  if (!value.startsWith('/') || value.startsWith('//')) return '/';
  return value;
}

URLs abertas podem ser usadas em phishing e vazamento de códigos.

Mix-up attacks

Quando há múltiplos provedores, vincule a tentativa ao issuer esperado e valide o issuer na resposta. Não escolha o endpoint de token com base em dados não confiáveis.

Parâmetros de erro

O callback pode receber error em vez de code. Trate cancelamento de consentimento sem registrar query completa. Limpe a tentativa pendente.

Cookies

A sessão usada para state e verifier deve ter HttpOnly, Secure e SameSite adequado. Fluxos de redirecionamento podem exigir Lax em vez de Strict.

Aplicativos móveis

Use navegador do sistema e redirect URI baseada em universal/app links ou esquema registrado com proteção contra interceptação. Não use WebView embutida para coletar credenciais.

SPAs

Use Authorization Code com PKCE, nunca Implicit Flow. Mantenha tokens em memória quando possível e proteja contra XSS com CSP e dependências seguras.

Logout

Logout local remove a sessão da aplicação. Logout federado e revogação de tokens dependem do provedor. Não presuma que encerrar uma camada encerra todas.

Erros comuns

  • não usar PKCE;
  • usar método plain;
  • não validar state;
  • reutilizar verifier;
  • redirect URI dinâmica;
  • não validar issuer e audience;
  • registrar code ou tokens;
  • usar Implicit Flow;
  • solicitar scopes excessivos;
  • confundir OAuth com autenticação sem OIDC.

Testes

Teste state incorreto, verifier incorreto, code reutilizado, callback sem sessão, issuer errado, nonce errado, redirect inválido, timeout, resposta grande e cancelamento pelo usuário.

Fluxo recomendado

  1. use Authorization Code;
  2. gere verifier forte;
  3. use S256;
  4. gere state e nonce;
  5. fixe redirect URI;
  6. valide issuer e audience;
  7. mantenha tokens no backend;
  8. solicite scopes mínimos;
  9. redija logs;
  10. implemente revogação.

Combine OAuth com JWT no Node.js, Refresh Tokens, Cookies Seguros, CSRF e Secret Management.

Consulte a especificação PKCE e o BCP de segurança OAuth.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita