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

OpenID Connect no Node.js

Atualizado em: 18 de setembro de 2026

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

O OpenID Connect no Node.js permite autenticar usuários por meio de um provedor de identidade, como uma plataforma corporativa, social login ou servidor próprio. Ele adiciona uma camada de identidade sobre OAuth 2.0 e entrega um ID Token assinado com informações sobre o evento de autenticação.

OAuth 2.0 responde principalmente “esta aplicação pode acessar este recurso?”. OpenID Connect responde “quem foi autenticado e como o cliente pode confiar nessa identidade?”. A diferença é importante: um access token não deve ser usado automaticamente como prova de login, e um ID Token não deve ser enviado para APIs como autorização.

Neste guia, você aprenderá a configurar Authorization Code Flow com PKCE, discovery, state, nonce, callback, validação do ID Token, sessão local, logout e cuidados com issuer, audience, redirect URI e claims.

O que é OpenID Connect?

A especificação OpenID Connect Core 1.0 define uma camada de identidade sobre OAuth 2.0. O cliente, chamado Relying Party, solicita o scope openid. O provedor autentica o usuário e retorna um ID Token.

Para aplicações modernas, o fluxo recomendado é Authorization Code com PKCE. Consulte também OAuth 2.0 com PKCE no Node.js.

Participantes

  • End-User: pessoa que faz login.
  • Relying Party: aplicação Node.js.
  • OpenID Provider: servidor que autentica.
  • Authorization Endpoint: página de login e consentimento.
  • Token Endpoint: troca code por tokens.
  • UserInfo Endpoint: retorna claims autorizadas.
  • JWKS URI: publica chaves de assinatura.

Discovery

O cliente descobre endpoints por:

https://identity.example.com/.well-known/openid-configuration

O documento normalmente inclui:

{
  "issuer": "https://identity.example.com",
  "authorization_endpoint": "https://identity.example.com/authorize",
  "token_endpoint": "https://identity.example.com/oauth/token",
  "userinfo_endpoint": "https://identity.example.com/userinfo",
  "jwks_uri": "https://identity.example.com/.well-known/jwks.json"
}

Não aceite um issuer arbitrário enviado pelo usuário. Use uma allowlist ou configuração controlada para evitar SSRF e login com um provedor malicioso.

Instalando openid-client

Uma biblioteca consolidada é openid-client:

npm install openid-client

A API muda entre versões. Fixe a dependência e siga a documentação correspondente ao lockfile.

Descobrindo a configuração

import * as oidc from 'openid-client';

const issuer = new URL(process.env.OIDC_ISSUER);

const config = await oidc.discovery(
  issuer,
  process.env.OIDC_CLIENT_ID,
  process.env.OIDC_CLIENT_SECRET
);

Aplicações públicas não devem depender de client secret. Aplicações confidenciais no servidor protegem a credencial em um secret manager.

Redirect URI

const redirectUri = 'https://app.example.com/auth/callback';

A URI deve estar cadastrada exatamente no provedor. Não crie redirects dinâmicos a partir de query strings. Uma validação permissiva pode transformar o callback em open redirect ou roubo de code.

State

state liga a solicitação ao callback e ajuda a impedir CSRF:

const state = oidc.randomState();
req.session.oidc = { state };

O valor precisa ser aleatório, associado à sessão e usado uma única vez.

Nonce

nonce liga a sessão ao ID Token e reduz replay:

const nonce = oidc.randomNonce();
req.session.oidc = { state, nonce };

No callback, valide que o claim nonce corresponde ao valor enviado.

PKCE

const codeVerifier = oidc.randomPKCECodeVerifier();
const codeChallenge = await oidc.calculatePKCECodeChallenge(codeVerifier);

req.session.oidc = {
  state,
  nonce,
  codeVerifier
};

O code challenge vai para o endpoint de autorização. O verifier fica somente no servidor e é enviado no token endpoint.

Iniciando login

const authorizationUrl = oidc.buildAuthorizationUrl(config, {
  redirect_uri: redirectUri,
  scope: 'openid profile email',
  response_type: 'code',
  code_challenge: codeChallenge,
  code_challenge_method: 'S256',
  state,
  nonce
});

res.redirect(authorizationUrl.href);

Solicite somente scopes necessários. openid ativa OIDC; profile e email liberam claims conforme política do provedor.

Callback

app.get('/auth/callback', async (req, res, next) => {
  try {
    const currentUrl = new URL(
      `${req.protocol}://${req.get('host')}${req.originalUrl}`
    );

    const tokens = await oidc.authorizationCodeGrant(
      config,
      currentUrl,
      {
        pkceCodeVerifier: req.session.oidc.codeVerifier,
        expectedState: req.session.oidc.state,
        expectedNonce: req.session.oidc.nonce
      }
    );

    const claims = tokens.claims();
    await completeLogin(req, claims, tokens);
    res.redirect('/app');
  } catch (error) {
    next(error);
  }
});

Consulte a API exata da versão instalada. O importante é não trocar o code sem validar state, nonce e PKCE.

Claims essenciais do ID Token

  • iss: issuer esperado.
  • sub: identificador estável do usuário naquele issuer.
  • aud: deve conter o client ID.
  • exp: token não pode estar expirado.
  • iat: momento da emissão.
  • nonce: quando enviado na requisição.
  • azp: authorized party em cenários específicos.

Uma biblioteca OIDC deve validar assinatura, algoritmo, issuer, audience e tempo. Não faça apenas JSON.parse no JWT.

Sub é a identidade principal

Use a combinação issuer + sub como identidade externa:

CREATE TABLE external_identities (
  issuer text NOT NULL,
  subject text NOT NULL,
  user_id uuid NOT NULL REFERENCES users(id),
  PRIMARY KEY (issuer, subject)
);

E-mail pode mudar, ser reciclado ou não estar verificado. Não use apenas email como chave de conta.

Email verificado

if (claims.email_verified !== true) {
  // Não vincule automaticamente a uma conta sensível
}

Mesmo verificado, o e-mail identifica uma caixa, não necessariamente uma pessoa imutável.

ID Token versus Access Token

  • ID Token: destinado ao cliente; prova autenticação.
  • Access Token: destinado ao resource server; autoriza API.

Não envie ID Token no header Authorization de uma API. Não use access token opaco como fonte de perfil.

Para JWTs em APIs, veja JWT Seguro no Node.js.

UserInfo

Quando precisa de claims adicionais:

const userInfo = await oidc.fetchUserInfo(
  config,
  tokens.access_token,
  claims.sub
);

Valide que o sub do UserInfo corresponde ao ID Token. Busque apenas quando necessário.

Criando sessão local

Após validar o login, crie uma sessão própria:

req.session.regenerate(error => {
  if (error) return next(error);

  req.session.user = {
    id: localUser.id,
    issuer: claims.iss,
    subject: claims.sub
  };

  res.redirect('/app');
});

Regenerar o ID reduz session fixation. Consulte Sessões Seguras no Node.js.

Armazenamento dos tokens

Se a aplicação precisa chamar APIs em nome do usuário, armazene tokens criptografados no servidor. Não coloque refresh token em localStorage. Defina expiração, rotação, revogação e escopo mínimo.

Veja Refresh Tokens no Node.js.

Clock skew

Validações podem aceitar poucos segundos de tolerância, mas relógios precisam estar sincronizados. Uma margem grande torna tokens expirados válidos por mais tempo.

Prompt e max_age

const url = oidc.buildAuthorizationUrl(config, {
  scope: 'openid profile',
  prompt: 'login',
  max_age: 300,
  // demais parâmetros
});

Use reautenticação para ações sensíveis. O claim auth_time ajuda a verificar a idade do login.

ACR e AMR

acr representa classe de autenticação; amr lista métodos usados, como password e OTP. Só tome decisões se houver acordo claro com o provedor sobre a semântica.

Login iniciado pelo provedor

Fluxos iniciados por terceiros exigem cuidado com destino e CSRF. Prefira iniciar login pela própria aplicação e usar destinos internos validados.

Logout local

app.post('/logout', (req, res, next) => {
  req.session.destroy(error => {
    if (error) return next(error);
    res.clearCookie('session');
    res.redirect('/');
  });
});

Logout no provedor

Alguns provedores suportam RP-Initiated Logout com end_session_endpoint. Nunca construa post_logout_redirect_uri com entrada arbitrária. Logout local e logout do provedor são operações diferentes.

Múltiplos provedores

Para vários issuers, mantenha uma configuração por provedor:

const providers = new Map([
  ['corporate', corporateConfig],
  ['customers', customersConfig]
]);

Não descubra qualquer URL fornecida pelo navegador. Isso pode permitir SSRF e token substitution.

Vinculação de contas

Não vincule automaticamente duas identidades apenas porque compartilham e-mail. Exija autenticação nas duas contas ou um processo administrativo seguro.

Autorização depois do login

OIDC autentica; ele não decide se o usuário pode editar um projeto. Use RBAC, ABAC ou ReBAC depois de mapear a identidade.

Consulte OpenFGA no Node.js e RBAC no Node.js.

Segurança de cookies

cookie: {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  maxAge: 8 * 60 * 60 * 1000
}

SameSite=Lax normalmente permite o redirect GET do provedor, mas teste o fluxo. Proteja endpoints POST com CSRF.

Headers de proxy

Se a aplicação está atrás de proxy, configure host e protocolo confiáveis. Não use qualquer X-Forwarded-Host para construir redirect URI. Prefira uma base URL fixa.

Observabilidade

Monitore:

  • logins iniciados e concluídos;
  • erros por provedor;
  • state ou nonce inválidos;
  • troca de code;
  • issuer e audience inválidos;
  • latência do discovery e token endpoint;
  • refresh failures;
  • logout.

Não registre code, tokens ou claims sensíveis.

Testes

Cubra:

  • login válido;
  • state incorreto;
  • nonce incorreto;
  • code reutilizado;
  • issuer falso;
  • audience errada;
  • token expirado;
  • e-mail não verificado;
  • session fixation;
  • logout.

Erros comuns

  • Usar OAuth como login sem OIDC: identidade não é padronizada.
  • Confiar em access token como perfil: audience pode ser outra API.
  • Não validar nonce: replay do ID Token.
  • Redirect URI dinâmica: open redirect ou roubo de code.
  • Conta pelo e-mail: identidade pode ser vinculada incorretamente.
  • Tokens no localStorage: XSS aumenta impacto.
  • Issuer arbitrário: SSRF ou provedor malicioso.
  • Confundir autenticação e autorização: login concede privilégios indevidos.

Conclusão

O OpenID Connect no Node.js padroniza login federado com discovery, Authorization Code, PKCE e ID Token. O cliente valida issuer, audience, assinatura, expiração, state e nonce antes de criar uma sessão local.

Use issuer + sub como identidade, mantenha tokens no servidor e solicite poucos scopes. Depois da autenticação, aplique autorização separadamente. Com redirect URIs fixas, sessões seguras e bibliotecas atualizadas, OIDC integra provedores de identidade sem transformar tokens em atalhos inseguros.

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