OpenID Connect: autenticação sobre OAuth

1. Fundamentos do OpenID Connect (OIDC)

O OpenID Connect (OIDC) é uma camada de identidade construída sobre o protocolo OAuth 2.0. Enquanto o OAuth 2.0 foi projetado para autorizar o acesso a recursos (permitir que um aplicativo acesse APIs em nome do usuário), o OIDC adiciona autenticação — a capacidade de verificar quem é o usuário.

A diferença fundamental é:

  • OAuth 2.0: "Este aplicativo pode acessar seus arquivos no Drive?"
  • OIDC: "Qual é a identidade do usuário que está usando este aplicativo?"

Os papéis no fluxo OIDC são:

  • RP (Relying Party): o aplicativo que deseja autenticar o usuário (seu frontend ou backend)
  • OP (OpenID Provider): o servidor de autenticação (Google, Auth0, Keycloak)
  • End-User: o usuário humano que está sendo autenticado

2. O Token ID (id_token) e sua estrutura

O coração do OIDC é o id_token, um JWT (JSON Web Token) que contém claims sobre a identidade do usuário. Sua estrutura típica:

{
  "iss": "https://accounts.google.com",
  "sub": "1234567890",
  "aud": "seu-client-id.apps.googleusercontent.com",
  "exp": 1718000000,
  "iat": 1717996400,
  "nonce": "a1b2c3d4e5f6",
  "name": "João Silva",
  "email": "joao@exemplo.com",
  "preferred_username": "joaosilva"
}

Claims obrigatórias que todo id_token deve conter:

  • iss (issuer): quem emitiu o token
  • sub (subject): identificador único do usuário no OP
  • aud (audience): o client_id do seu aplicativo
  • exp (expiration): timestamp de expiração
  • iat (issued at): timestamp de emissão

Nonce: um valor aleatório gerado pelo RP e enviado na requisição de autenticação. O OP deve incluí-lo no id_token. Isso protege contra:

  • Replay attacks: um token interceptado não pode ser reutilizado
  • Fixação de sessão: o nonce vincula a autenticação a uma sessão específica do RP

3. Fluxos de Autenticação no OIDC

Authorization Code Flow (com PKCE)

O fluxo mais seguro, especialmente para aplicações públicas (SPA, mobile). O código de autorização nunca é exposto ao navegador do usuário.

1. RP gera code_verifier (string aleatória) e code_challenge = SHA256(code_verifier)
2. Navegador → OP: /authorize?response_type=code&client_id=...&code_challenge=...
3. Usuário autentica no OP
4. OP → Navegador: redirect com ?code=xyz
5. Navegador → RP: envia o code
6. RP → OP: /token?code=xyz&code_verifier=... (troca segura do código por tokens)
7. OP → RP: access_token + id_token + refresh_token

PKCE (Proof Key for Code Exchange) é obrigatório para apps públicos. Sem PKCE, um atacante que intercepte o authorization code pode trocá-lo por tokens.

Implicit Flow (DESCONTINUADO)

Neste fluxo, o id_token era retornado diretamente na URL de redirect:

https://meuapp.com/callback#id_token=eyJhbGciOiJSUzI1NiIs...

Riscos: o token fica visível no histórico do navegador, logs de proxy, e pode ser vazado pelo Referer header. Nunca use este fluxo — foi descontinuado pelo IETF.

Hybrid Flow

Combina elementos dos fluxos Authorization Code e Implicit. Pode retornar id_token + code na primeira resposta. Útil quando o RP precisa do id_token imediatamente, mas aumenta a superfície de ataque porque parte do token trafega pela URL.

4. Validação e Segurança do id_token

Validar o id_token é crítico para segurança. Um token malicioso pode comprometer todo o sistema.

Verificação de assinatura

Algoritmos ACEITÁVEIS:
- RS256 (RSA com SHA-256) — mais comum
- ES256 (ECDSA com P-256) — mais eficiente

Algoritmos PROIBIDOS:
- none — token sem assinatura (atacante pode forjar qualquer identidade)
- HS256 com chave pública — se a chave para HMAC for pública, qualquer um pode criar tokens válidos

Validação de claims

aud: deve ser EXATAMENTE seu client_id
iss: deve ser o emissor esperado (lista branca)
exp: token não pode estar expirado (com margem de tolerância, ex: 5 min)
iat: token não pode ter sido emitido no futuro
nonce: deve corresponder ao valor que você enviou

Proteção contra substituição de token

Se um atacante obtém um id_token válido de outro cliente (com aud diferente), ele pode tentar usá-lo no seu sistema. A validação de aud bloqueia isso.

A claim azp (authorized party) deve ser verificada quando o aud contiver múltiplos valores — indica qual cliente realmente usou o token.

5. UserInfo Endpoint e Claims Adicionais

O UserInfo Endpoint é uma API protegida do OP que retorna claims sobre o usuário. Use-o quando:

  • O id_token não contém claims suficientes
  • Você precisa de claims atualizadas (o id_token pode expirar)
  • A política de privacidade exige chamadas autenticadas
GET /userinfo HTTP/1.1
Authorization: Bearer <access_token>

Atenção: use o access_token para chamar o UserInfo, nunca o id_token. O id_token é apenas para verificação de identidade, não para autorização.

Escopos OIDC

openid — obrigatório, ativa o fluxo OIDC
profile — name, family_name, preferred_username, picture
email — email, email_verified
address — endereço postal
phone — phone_number, phone_number_verified

Perigo de escopos excessivos: solicitar profile quando você só precisa do email expõe dados desnecessários. Se o vazamento ocorrer, sua responsabilidade aumenta. Solicite apenas o mínimo necessário.

6. Logout Seguro (Single Logout - SLO)

RP-Initiated Logout

O RP inicia o logout redirecionando o usuário para o OP:

GET /logout?id_token_hint=eyJhbGciOiJSUzI1NiIs...
&post_logout_redirect_uri=https://meuapp.com/logout
&state=xyz

O parâmetro id_token_hint ajuda o OP a identificar qual sessão encerrar. Sem ele, o OP pode não conseguir associar a requisição a uma sessão ativa.

Session Management via iframe

O OP pode expor um endpoint check_session_iframe que permite ao RP monitorar o estado da sessão no OP via postMessage. Riscos: vulnerável a clickjacking se não usar headers X-Frame-Options: DENY ou Content-Security-Policy: frame-ancestors 'none'.

Riscos de logout incompleto

  • O access_token pode continuar válido mesmo após o logout — implemente uma lista de revogação ou use tokens de curta duração
  • A sessão do OP pode não ser encerrada — o usuário ainda está logado no Google, por exemplo
  • SLO (Single Logout): garanta que o OP notifique todos os RPs sobre o logout, mas lembre-se que isso é difícil em ecossistemas heterogêneos

7. Ameaças Comuns e Mitigações

Ataque de confusão de provedor

O atacante faz o RP aceitar tokens de um OP malicioso. Mitigação: valide rigorosamente o iss contra uma lista branca de emissores confiáveis. O JWKS URI (jwks_uri) também deve vir de um domínio conhecido.

Mix-up attack

O atacante confunde o RP sobre qual OP está sendo usado, fazendo o RP enviar o código de autorização para o OP errado. Mitigação: use redirect_uri fixo (um único URI por client_id) e valide o parâmetro state para associar a resposta à requisição original.

Roubo de authorization code

Sem PKCE, um atacante pode roubar o código de autorização (via malware no dispositivo do usuário, por exemplo) e trocá-lo por tokens. Mitigação: PKCE é obrigatório para apps públicos (SPA, mobile, desktop).

Cross-Site Request Forgery (CSRF)

O atacante força o usuário a iniciar um fluxo de autenticação sem seu conhecimento. Mitigação: use state e nonce. O state protege contra CSRF no redirect, e o nonce protege contra replay do id_token.

Resumo de boas práticas:
- Sempre use Authorization Code Flow + PKCE
- Valide assinatura do id_token (nunca aceite 'alg: none')
- Verifique aud, iss, exp, nonce
- Use escopos mínimos necessários
- Implemente logout completo com revogação de tokens
- Mantenha uma lista branca de emissores confiáveis

Referências