Um app mobile faz o login de um usuário com um provedor de identidade e passa o access token para o seu backend, que chama o endpoint de perfil do provedor, obtém um endereço de e-mail e cria uma sessão. Todos os testes passam. Isso também permite que qualquer outro app em que o usuário entrou com esse provedor faça login no seu app como ele, com o próprio access token, porque nada verifica para qual app o token foi emitido.
O OpenID Connect (OIDC) fecha essa brecha. Ele adiciona um ID token ao OAuth 2.0: um JWT assinado em que o provedor informa quem é o usuário, para qual app o token se destina e quando o usuário fez login.
Este artigo desenha o authorization code flow com PKCE como diagrama de sequência e cobre o que o OIDC acrescenta: o scope openid, nonce, a validação do ID token, discovery, a rotação de chaves, UserInfo e logout.
OAuth 2.0 versus OpenID Connect
O OAuth 2.0 trata de acesso: um token para chamar uma API em nome do usuário. O OpenID Connect trata de identidade: quem fez login.
| OAuth 2.0 | OpenID Connect | |
|---|---|---|
| Pergunta que responde | Este app pode chamar esta API? | Quem é o usuário e quando ele fez login? |
| Token | Access token | ID token |
| O token se destina a | A API | Seu app |
| Formato do token | Não definido: opaco ou JWT | Um JWT assinado |
| Como pedir | Scopes que a API define | scope inclui openid |
| Define | Grants e endpoints | Claims, UserInfo, discovery, logout |
Na rede, um login OIDC é uma requisição de autorização OAuth com openid no scope, e a resposta de tokens traz um id_token ao lado do access token. O diagrama do fluxo OAuth 2.0 cobre a parte OAuth em detalhe.
O fluxo, passo a passo
O OpenID Connect Core 1.0 chama o seu app de relying party (RP) e o servidor de autorização de OpenID provider (OP). O que passa pelo navegador, o canal de frente, pode vazar para URLs, histórico e logs; as chamadas do seu backend ao provedor, o canal de fundo, não. Os números correspondem ao diagrama.
- Início (1–3). O usuário clica em Sign in. Seu app guarda na sessão um
state, umnoncee umcode_verifierde PKCE novos e redireciona o navegador para o provedor. - Requisição de autenticação (4). O navegador segue o redirecionamento:
GET /authorize?response_type=code
&client_id=orders-web
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&scope=openid%20profile%20email
&state=7fJq2LbX9aQe
&nonce=n-0S6_WzA2Mj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256 HTTP/1.1
Host: op.example.com
- Login e consentimento (5). O provedor autentica o usuário (senha, passkey, MFA ou uma sessão existente) e pede consentimento. Seu app nunca vê as credenciais.
- Volta do código (6–7). O provedor redireciona para o
redirect_uriregistrado com umcodede curta duração e o mesmostate, que seu app compara com o da sessão. - Requisição de tokens (8–9). Seu backend envia por POST o
code, oredirect_urie ocode_verifiercom suas credenciais de cliente. O provedor confere o verifier com o challenge e devolve umid_token, umaccess_tokene, muitas vezes, umrefresh_token. - Validação (10). Seu app valida o ID token, buscando as chaves de assinatura somente quando a que ele cita não está em cache.
- UserInfo (11–12), opcional. Somente quando o ID token não traz os claims de que você precisa.
- Sessão (13). Seu app cria a própria sessão e define um cookie.
O que state, nonce e PKCE protegem
| Valor | Trafega em | Verificado por | Impede |
|---|---|---|---|
state |
A etapa 4 e o redirecionamento de volta | Seu app | Uma resposta de login que o usuário nunca iniciou (CSRF) |
nonce |
A etapa 4 e depois dentro do ID token | Seu app | Replay do ID token e injeção de código em clientes confidenciais |
code_verifier |
Seu hash SHA-256 na etapa 4, ele mesmo na etapa 8 | O provedor | Resgatar um código roubado |
A melhor prática atual de segurança do OAuth, a RFC 9700, permite que um valor substitua outro em alguns casos, mas cada um custa apenas um valor aleatório, então envie os três. Clientes públicos, como aplicações de página única e mobile, devem usar PKCE (RFC 7636) com S256.
Dentro do ID token
O ID token da etapa 9, decodificado:
{
"iss": "https://op.example.com",
"sub": "248289761001",
"aud": "orders-web",
"nonce": "n-0S6_WzA2Mj",
"iat": 1793736400,
"exp": 1793740000,
"auth_time": 1793736390
}
| Claim | Significado | Seu app |
|---|---|---|
iss |
Emissor, uma URL https | Deve ser exatamente igual ao issuer descoberto |
sub |
ID do usuário, nunca reatribuído dentro do emissor, no máximo 255 caracteres ASCII | Usa iss mais sub como chave da conta |
aud |
Audiência, uma string ou um array | Deve encontrar o seu client_id nela |
exp, iat |
Expiração e hora de emissão, em segundos Unix | Rejeita tokens expirados ou muito antigos |
nonce |
O valor enviado na etapa 4 | Deve corresponder à sessão |
auth_time |
Quando o usuário realmente se autenticou | Verifica quando você envia max_age |
acr, amr |
Nível de garantia e métodos, como pwd ou mfa |
Verifica antes de ações sensíveis |
O Core é explícito: somente iss mais sub identifica um usuário, porque endereços de e-mail mudam e podem ser reatribuídos.
Validando o ID token
A seção 3.1.3.7 do Core lista as regras:
- Fixe o algoritmo (RS256 por padrão) em vez de confiar no cabeçalho
alg, e nunca aceitenone. - Verifique a assinatura com a chave JWKS indicada por
kid. O Core permite que um cliente de code flow confie no TLS em vez disso, já que o token veio direto do token endpoint, mas verificar custa pouco. - Confira
isscom o emissor descoberto, caractere por caractere. - Confira
audpara o seuclient_ide rejeite audiências em que você não confia. - Confira
expeiat, permitindo de segundos a alguns minutos de diferença de relógio. - Confira
noncecom a sessão e depois apague-o. - Confira
auth_timeeacrse você os solicitou.
Só então crie uma sessão. Com a biblioteca jose no Node.js:
import { createRemoteJWKSet, jwtVerify } from 'jose'
// issuer and jwks_uri come from the discovery document.
// The key set is cached; an unknown kid triggers a refetch
// (at most once every 30 seconds by default).
const JWKS = createRemoteJWKSet(new URL(oidc.jwks_uri))
const { payload } = await jwtVerify(tokens.id_token, JWKS, {
issuer: oidc.issuer, // exact match
audience: CLIENT_ID, // must appear in aud
algorithms: ['RS256'], // never trust the header alone
clockTolerance: 60, // seconds of clock skew
maxTokenAge: 600, // rejects an old iat
})
if (payload.nonce !== session.nonce) throw new Error('nonce mismatch')
Discovery e rotação de chaves
O OpenID Connect Discovery 1.0 coloca as configurações de um provedor em um único documento JSON na URL do emissor mais /.well-known/openid-configuration. O do Google, reduzido aos campos que este fluxo usa, no momento da escrita (novembro de 2026):
{
"issuer": "https://accounts.google.com",
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
"token_endpoint": "https://oauth2.googleapis.com/token",
"userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
"jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
"id_token_signing_alg_values_supported": ["RS256"],
"code_challenge_methods_supported": ["plain", "S256"]
}
O issuer deve ser idêntico à URL do emissor de onde você o buscou e ao iss de todo ID token. Configure você mesmo o emissor esperado; nunca o tire de um token que você não verificou.
As chaves rotacionam pelo jwks_uri: o provedor adiciona uma chave nova, assina com ela e mantém a antiga publicada por um tempo (Core, seção 10.1.1). Coloque o conjunto em cache, respeite Cache-Control, busque de novo diante de um kid desconhecido e limite a taxa dessas buscas. O diagrama do fluxo de autenticação JWT cobre a mesma mecânica para access tokens.
UserInfo
O UserInfo devolve claims para os scopes concedidos (profile, email, address, phone) quando você o chama com o access token. A resposta sempre contém sub, que deve corresponder exatamente ao sub do ID token, ou você descarta a resposta.
Opções de logout
Três especificações OpenID finais cobrem o logout:
| Especificação | Como funciona | Atenção a |
|---|---|---|
| RP-Initiated Logout 1.0 | Seu app redireciona para o end_session_endpoint do provedor com id_token_hint |
Seu app também deve encerrar a própria sessão |
| Front-Channel Logout 1.0 | O provedor carrega a URL de logout de cada app em um iframe | O bloqueio de cookies de terceiros pode quebrá-lo |
| Back-Channel Logout 1.0 | O provedor envia por POST um logout token assinado a cada app | Seu app deve encontrar e encerrar a sessão no servidor |
O Core também observa que o exp do ID token não tem relação com a duração da sessão.
Erros que aparecem nas revisões
- Um access token como prova de identidade. A cena de abertura. Faça login de usuários somente com um ID token que seu backend obteve do token endpoint e validou.
- Ignorar
audounonce. Todo app registrado no mesmo provedor recebe ID tokens validamente assinados com o mesmoiss; sóauddiz que um token foi emitido para você, e sónonceo vincula a este login. - O implicit flow.
response_type=id_token tokencoloca tokens no fragmento da URL; a RFC 9700 diz que clientes não devem usar response types que devolvem access tokens na resposta de autorização. Usecodecom PKCE, também em aplicações de página única. - ID tokens enviados a APIs. As APIs devem aceitar somente access tokens emitidos para elas; o diagrama de arquitetura de um API gateway mostra onde essa verificação costuma ficar.
O diagrama do fluxo do OpenID Connect
Conteúdo do diagrama
- User
- Browser
- Relying party (your app)
- OpenID provider
- all checks must pass
- claims missing from the ID token
- ID token: for your app (aud = client_id)
- Access token: for UserInfo and APIs
- 1. Click Sign in
- 2. GET /login
- 3. 302 to the provider's /authorize
- 4. GET /authorize: scope=openid, state, nonce, code_challenge
- 5. Log in (password, passkey, MFA) and consent
- 6. 302 to redirect_uri with code, state
- 7. GET /callback?code&state
- 8. POST /token: code, code_verifier, client auth
- 9. id_token, access_token
- 10. GET jwks_uri (when kid is not cached)
- 11. GET /userinfo, Bearer access_token
- 12. Claims; sub must match the ID token
- 13. 302 to the app, Set-Cookie: session
Como ler:
- Quatro participantes: o usuário, o navegador, seu app (a relying party) e o OpenID provider. O tempo corre para baixo.
- As cores mostram o canal: azul pelo navegador, verde-azulado do seu backend ao provedor, violeta para as ações do usuário. Setas tracejadas são respostas e redirecionamentos.
- As etapas 3 e 4 carregam
scope=openid,state,noncee o challenge do PKCE; a etapa 8 carrega o verifier. - O fragmento
criticalcontém a busca de chaves e as verificações que precisam passar antes de existir uma sessão. O fragmentoopté a chamada ao UserInfo. - A coluna da direita associa os campos de discovery às etapas, lista os parâmetros do PKCE, decodifica um ID token e mostra qual token é para quem.
Desenhando diagramas de OpenID Connect com clareza
- Dê ao navegador a sua própria linha de vida. A divisão entre canal de frente e de fundo é a história de segurança do fluxo.
- Escreva nas setas os parâmetros verificados:
state,nonce,code_challenge,code_verifier. - Detalhe a validação. «Validar token» não dá para revisar; «assinatura, iss, aud, exp, iat, nonce» dá.
- Diga qual token vai para onde, com a sua audiência. Muitos bugs de OIDC são um token enviado à parte errada.
- Um fluxo por diagrama. Desenhe login, renovação e logout separadamente, com IDs e tokens de exemplo.
O exemplo de diagrama de sequência cobre a notação; o diagrama do fluxo SAML SSO mostra o protocolo baseado em XML que clientes corporativos ainda pedem.
Monte no Archboard
- Entre com Google ou GitHub e crie um diagrama.
- No pack Sequência, adicione um Ator para o usuário e três nós Linha de vida de mesma altura: Browser, Relying party (seu app) e OpenID provider.
- Desenhe as 13 mensagens como arestas retas numeradas, de cima para baixo. Deixe as respostas tracejadas e use cores diferentes para o canal de frente e o de fundo.
- Adicione um Fragmento combinado do pack Sequência com o operador
criticalem volta da busca de chaves e das verificações, e um segundo Fragmento combinado comoptem volta da chamada ao UserInfo. - Adicione um Texto do pack Básico ao lado da relying party para os valores de sessão e as verificações, além de uma legenda de cores.
- Adicione um bloco Markdown do pack Básico para discovery, PKCE e um ID token decodificado, e dois nós Token do pack Segurança para o ID token e o access token.
O exemplo usa 9 nós de diagrama e fica dentro do plano Free. Seu agente de IA também pode desenhá-lo via MCP.
Perguntas frequentes
Qual é a diferença entre OAuth 2.0 e OpenID Connect?
O OAuth 2.0 dá a um app um access token para chamar uma API. O OpenID Connect adiciona o login por cima: o scope openid, um ID token que diz quem é o usuário e endpoints padrão de discovery e UserInfo.
Um ID token é o mesmo que um access token? Não. O ID token é um JWT assinado para o seu app que diz quem fez login. O access token é para APIs, incluindo o UserInfo. Nunca envie ID tokens a APIs nem aceite access tokens como prova de login.
Preciso de PKCE se já envio um nonce? Use os dois. A RFC 9700 exige PKCE para clientes públicos. Um cliente confidencial pode contar com o nonce com precauções extras, mas o PKCE impede o resgate de um código roubado, enquanto o nonce só detecta um código injetado depois da resposta de tokens.
Como valido um ID token?
Verifique a assinatura com a chave JWKS que corresponde ao seu kid e confira se iss é igual ao emissor, se aud contém o seu client ID, se exp não passou, se iat é recente e se nonce corresponde à sessão.
O que é /.well-known/openid-configuration?
O documento de discovery do OpenID Connect. Ele lista o emissor, os endpoints, os algoritmos e o jwks_uri de um provedor, para que seu app possa se configurar a partir de uma única URL.