Skip to content
This page has been auto-translated and may contain errors.View in English

OAuth e identidade delegada

Todo botão "Sign in with Google" representa uma decisão de não manter um sistema de senhas.

Seu app não verifica uma senha, não confirma a identidade da pessoa e nunca armazena uma credencial. Ele pergunta a um provedor em quem já confia, e aceita a resposta.

O protocolo por trás disso é o OAuth, e o fluxo tem mais engrenagens do que o botão sugere.

Duas autorizações diferentes

Existe uma confusão que pega quase todo mundo, e vale a pena esclarecê-la antes que o fluxo faça sentido.

Você já viu a tela de consentimento: um app pedindo seu nome, foto de perfil e endereço de e-mail. Às vezes mais, às vezes muito mais, como contatos, agenda e todas as mensagens da sua caixa de entrada.

Essa tela é sobre autorização, e não tem nada a ver com os papéis (roles) do seu app. Ela decide quais partes da conta Google do usuário seu app pode acessar, e essas permissões são chamadas de scopes.

Seus próprios papéis de admin e de usuário continuam sendo decididos dentro do seu back-end, depois que o login termina, exatamente como nos capítulos anteriores.

OAuth autoriza, OIDC identifica

Rigorosamente falando, o OAuth concede autorização delegada: permissão para o seu app agir sobre um recurso. A camada de identidade construída em cima dele é o OpenID Connect, ou OIDC, e os botões "sign in with" são, na verdade, OIDC.

Essa distinção importa na prática do código. Um access token do OAuth diz o que seu app pode acessar; um ID token do OIDC diz quem é o usuário. Tratar o primeiro como prova de identidade é um erro comum e real.

JunoDuas autorizações diferentes Duas coisas estão sendo decididas aqui, e elas se confundem o tempo todo.

O Google decide quais dos seus dados do Google o app pode ver. Seu app decide o que essa pessoa pode fazer depois que ela entra. Perguntas diferentes, lugares diferentes, respostas diferentes.

JunoDuas autorizações diferentes Os scopes são definidos quando você configura o provedor, não por usuário, então é uma decisão tomada uma vez só e raramente revisitada. É assim que os apps acabam pedindo muito mais do que realmente usam.

Vale colocar numa checklist: antes de lançar uma integração OAuth, liste cada scope que você pede e aponte a funcionalidade que precisa dele. Tudo que não tiver uma funcionalidade associada deve sair.

JunoDuas autorizações diferentes A versão prática da distinção do OIDC é qual token você lê. O ID token é um JWT com claims sobre o usuário, destinado ao seu app, e é ele que deve ser verificado e consumido.

O access token é destinado às APIs do provedor, e seu app deve tratá-lo como opaco.

Extrair identidade de um access token funciona até o momento em que o provedor muda o formato dele, porque essa estrutura nunca foi uma garantia dada a você.

Isso também abre espaço para o problema do "confused deputy": um access token emitido para outro app é apresentado ao seu e aceito, já que nada nele diz para quem foi emitido.

É exatamente isso que a claim aud do ID token existe para evitar, e verificá-la não é opcional.

A ida e volta

Oito etapas, e o usuário sai do seu app bem no meio delas:

  1. O usuário clica no botão dentro do seu app.
  2. Seu app redireciona o usuário para o Google.
  3. O usuário faz login diretamente com o Google. Seu app não vê nada disso.
  4. O Google mostra a tela de consentimento para os scopes que você pediu.
  5. Se o usuário concordar, o Google envia ao seu servidor um código de aprovação de uso único. Esse código não loga ninguém. Ele representa apenas o consentimento, nada mais.
  6. Seu servidor envia esse código de volta ao Google, junto com as credenciais secretas do seu próprio app.
  7. Essas credenciais provam ao Google que a solicitação veio do seu back-end.
  8. O Google responde com um ID token dizendo quem é o usuário, e seu app faz o login dele.

A troca em duas etapas, das etapas cinco a oito, é a parte que vale a pena entender. O código passa pelo navegador do usuário e é inútil sozinho; o token que realmente importa é trocado diretamente entre servidores, onde nenhum navegador chega a vê-lo.

JunoA ida e volta O redirecionamento para fora e de volta é todo o truque. Seu app fica deliberadamente ausente enquanto a senha é digitada, então não tem nada que ele possa acidentalmente guardar.

É essa a vantagem: você não consegue vazar uma credencial que nunca recebeu.

JunoA ida e volta As etapas cinco e seis respondem a uma pergunta que costuma surgir logo de início: por que um código primeiro, em vez do Google enviar o token direto?

Porque o código passa pelo navegador, onde as coisas ficam registradas em logs e podem ser compartilhadas. Ele é de curta duração e vale pouco sozinho.

A troca na etapa seis acontece de servidor para servidor, com o seu segredo anexado, então o token realmente valioso nunca passa pelo navegador.

JunoA ida e volta Duas adições que a aula não chega a cobrir, e que hoje já são padrão. O PKCE, Proof Key for Code Exchange, faz o cliente gerar um segredo antes da etapa dois e apresentá-lo na troca, de modo que um código roubado não possa ser resgatado por quem o pegou.

Ele começou como uma correção para mobile, e as recomendações atuais o aplicam em todo lugar.

E o parâmetro state: um valor aleatório que você envia na etapa dois e verifica quando o usuário retorna, o que amarra a resposta à solicitação que você iniciou. Sem ele, um atacante pode completar um fluxo escolhido por ele no navegador da vítima e vincular a própria conta à sessão da vítima.

Os dois são baratos de implementar. E os dois costumam faltar em integrações feitas na mão, o que é o argumento mais forte para usar uma biblioteca mantida em vez de escrever o fluxo do zero.

Três formas de dar errado

Mandar o usuário para longe e confiar que o provedor vai trazê-lo de volta abre três brechas silenciosas.

O que dá erradoPor que isso importaA correção
1Pedir demaisScopes além do que o app usa significam que um vazamento expõe muito mais do que deveria, e os usuários perdem a confiança num app que parece intrometido. Divulgação de informaçãoPeça o menor conjunto de scopes que suas funcionalidades realmente usam
2Vazamento do código de aprovaçãoO código aparece na barra de endereço, no histórico do navegador, em ferramentas de analytics ou em logs do servidor. Qualquer um que o veja antes de expirar pode terminar o login como aquele usuário. Falsificação (spoofing)Mantenha-o fora de qualquer coisa que fique registrada em log, e troque-o imediatamente
3Confiar no ID token sem verificarQualquer um pode escrever um token com a cara certa. Só o Google pode assinar um de verdade. Aceitá-lo sem verificação permite que um atacante finja ser qualquer pessoa. Falsificação (spoofing)Verifique a assinatura, a audience e a expiração antes de confiar em qualquer coisa dentro dele

A falha 3 é a que desmonta o modelo inteiro. O sentido de delegar era que uma parte confiável garantisse a identidade do usuário; pular a verificação significa passar a confiar em quem quer que tenha enviado a solicitação.

JunoTrês formas de dar errado A comparação com um bilhete funciona bem aqui. O Google entrega ao seu app um bilhete assinado dizendo quem é o usuário.

Qualquer um pode escrever um bilhete. Verificar a assinatura é como você sabe que esse veio do Google, e pular essa verificação é confiar numa letra que você nunca viu antes.

JunoTrês formas de dar errado A verificação envolve três checagens, não uma só, e a segunda é a que costuma ser pulada. Foi assinado pelo provedor? Foi emitido para o seu app? Já expirou?

A checagem do meio é a claim de audience. Sem ela, um token gerado para outra aplicação é uma assinatura válida do Google que não diz nada sobre se ele era destinado a você.

JunoTrês formas de dar errado A vinculação de contas é o ponto delicado que essa lista de falhas deixa de fora. Associar um login delegado a uma conta existente pelo endereço de e-mail é o primeiro recurso de todo mundo, e só é seguro quando o provedor verifica esse endereço. Confira a claim email_verified, e trate sua ausência como não verificado.

Errar isso permite que alguém registre uma conta em um provedor usando o e-mail do seu usuário, faça login por ela e acabe caindo dentro da conta existente.

A outra coisa a planejar antes de lançar é o que acontece quando o provedor sai do ar ou um usuário perde o acesso a ele. Um login exclusivamente delegado faz a sua disponibilidade depender da disponibilidade deles, e a recuperação de conta vira uma conversa sobre a política de recuperação de conta de outra empresa.

Mão na massa

Cinco situações de uma integração OAuth real. Identifique a falha, o risco e a correção.

  1. Seu app só precisa de um endereço de e-mail para logar as pessoas, mas a solicitação OAuth também pede contatos, agenda e arquivos do Drive.
  2. A tela de consentimento avisa que seu app quer permissão para excluir eventos da agenda. Seu app nunca mexe na Agenda.
  3. Depois do login, a página mostra brevemente o código de aprovação de curta duração na URL.
  4. Seu back-end recebe o ID token e o aceita sem verificar a assinatura ou para quem ele foi emitido.
  5. Seu back-end aceita um ID token que expirou há uma hora.
Compare suas respostas
#FalhaRiscoCorreção
1Pedir demaisUm token vazado expõe muito mais da conta do usuário do que o app jamais precisouPeça apenas os scopes que uma funcionalidade usa
2Pedir demaisA mesma exposição, além de uma tela de consentimento que faz o app parecer suspeito antes mesmo de alguém usá-loRemova o scope não utilizado
3Vazamento do código de aprovaçãoURLs ficam registradas em log, salvas e compartilhadas. Quem capturar o código antes que ele expire pode completar o loginMantenha os códigos fora da URL, troque-os imediatamente
4Confiar no ID tokenQualquer um pode forjar algo com cara de token. Sem verificar assinatura e audience, um atacante pode se passar por qualquer usuárioVerifique a assinatura e a audience antes de confiar nele
5Confiar no ID tokenUm token antigo pode ser reaproveitado para logar repetidamente como aquele usuárioVerifique a expiração e rejeite qualquer token vencido

As falhas dois e um são a mesma falha com consequências diferentes, por isso vale ler a lista como três problemas, não cinco. Quatro e cinco também são uma única falha: verificação é um conjunto de checagens, e pular qualquer uma delas é pular a verificação.

Para onde isso leva

Três modelos, cada um resolvendo o mesmo problema por um caminho diferente. O servidor lembra, o cliente carrega a prova, ou um provedor garante a identidade.

Escolhendo um modelo de identidade coloca os três lado a lado e explica quando cada um é a resposta certa.