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

Como os aplicativos lembram de você

Você faz login. Você clica em alguma coisa. O servidor não tem a menor ideia de quem você é.

O HTTP é stateless: cada requisição chega do zero, sem memória da anterior e sem nenhum jeito embutido de conectar as duas. O login que deu certo há um segundo não deixou rastro nenhum.

Então um aplicativo que precisa reconhecer você entre requisições precisa guardar a identidade em algum lugar e recuperá-la a cada requisição seguinte.

Toda esta seção gira em torno de uma única pergunta: onde a identidade mora?

Quase todo fluxo de login na web responde isso de uma entre três formas.

O servidor lembra

Na identidade stateful, o servidor guarda a informação.

Quando você faz login, o servidor cria uma sessão, um pequeno pacote de dados que representa você: um id de usuário, um nome, talvez um papel. Essa sessão vive no servidor. O navegador recebe um cookie carregando um id de sessão, nada mais, e o envia automaticamente em toda requisição para aquele domínio. O servidor pega o id, busca a sessão correspondente e sabe de novo quem você é.

Esse é o padrão tradicional de aplicativo web, e o comportamento padrão em muitos frameworks. Se você já usou o express-session, era isso que ele estava fazendo.

JunoO servidor lembra O ponto que vale a pena guardar é o quão pouco o navegador recebe de confiança. Ele fica só com um id e nada mais.

Tudo o que importa sobre você fica no servidor, e o id só é útil para quem também consegue acessar esse servidor.

JunoO servidor lembra A busca feita a cada requisição é o detalhe a observar, porque é ao mesmo tempo o ponto forte e o custo desse modelo. Ela significa que o servidor pode alterar ou destruir uma sessão a qualquer momento, e a próxima requisição sente isso imediatamente.

Também significa que toda requisição toca um armazenamento compartilhado. Em um único servidor, isso é memória e sai de graça. Espalhado por vários servidores, é um repositório que todos precisam alcançar, e essa é a primeira peça de infraestrutura que esse modelo exige que você mantenha rodando.

JunoO servidor lembra A revogação instantânea é a propriedade que você está comprando, e ela vale mais do que parece. Demitir alguém, mudar um papel, reagir a um laptop roubado: apague a sessão e a próxima requisição fica anônima. Sem esperar nada expirar.

O que você paga é um problema de coordenação que cresce junto com sua infraestrutura. Sessões na memória do processo morrem junto com o processo e não existem para a instância vizinha, então qualquer escalonamento horizontal exige um repositório compartilhado, e esse repositório se torna algo que precisa ficar no ar para que qualquer pessoa continue logada.

Sessões fixas (sticky sessions) são o atalho tentador, e elas trocam esse problema por um pior: seu balanceamento de carga agora está amarrado à sua autenticação, e perder uma instância desloga todo mundo que ela estava atendendo.

O cliente carrega a prova

Na identidade stateless, o servidor não guarda nada sobre você.

O cliente mantém um token e o envia a cada requisição. O servidor verifica o token e responde, sem precisar consultar nada sobre quem você é entre uma requisição e outra.

Isso funciona bem para APIs, aplicativos mobile e sistemas distribuídos, onde memória compartilhada entre servidores é complicada ou inexistente. Se você já chamou uma API e recebeu 401 Unauthorized: missing authorization header, é esse modelo pedindo que você traga sua prova.

Existem duas versões disso, e a diferença importa o suficiente para que cada uma ganhe seu próprio capítulo. Um token opaco (bearer token) é uma string aleatória sem significado que o servidor precisa consultar. Um JSON Web Token carrega a identidade dentro de si mesmo, assinada, de modo que nenhuma consulta é necessária.

JunoO cliente carrega a prova O nome engana um pouco no início. O servidor é stateless em relação a você, não em relação a tudo.

Ele ainda tem um banco de dados cheio de usuários. O que ele deixa de manter é o registro do fato de que você está logado neste exato momento.

JunoO cliente carrega a prova "Bearer" (portador) é a palavra que faz todo o sentido em bearer token, e ela significa exatamente isso: quem o carrega, o possui. O token não está preso a um dispositivo, a um navegador ou a uma rede, então um token copiado é um token funcional.

É por isso que transporte e armazenamento importam tanto aqui. HTTPS em tudo, e um cuidado sério com onde o cliente guarda o token, porque qualquer coisa legível por JavaScript é legível por JavaScript injetado.

JunoO cliente carrega a prova A troca é exatamente o inverso das sessões. Você ganha um servidor que não precisa de estado compartilhado e escala horizontalmente de graça, e abre mão da capacidade de mudar de ideia depois.

Um token que você assinou é válido até expirar, esteja onde estiver, aconteça o que acontecer depois. Não existe uma lista de onde apagá-lo. É por isso que o formato usual em produção é um access token de vida curta somado a um refresh token de vida mais longa que pode ser revogado: isso reintroduz uma consulta, mas só no momento do refresh, não em toda requisição.

E vale dizer isso claramente. A identidade totalmente stateless abre mão da revogação, e qualquer solução que promete os dois ao mesmo tempo colocou algum estado de volta em algum lugar.

Alguém mais garante por você

Na identidade delegada, um terceiro em quem você confia confirma quem é o usuário em seu nome.

Seu aplicativo pergunta ao Google se ele reconhece essa pessoa. O Google faz o login e entrega ao seu aplicativo um token comprovando a resposta. Todo botão "Entrar com o Google", "Entrar com o GitHub" e "Entrar com a Apple" é isso.

O atrativo é que você deixa de manter um sistema de senhas. Sem armazenamento de senha, sem fluxo de redefinição, sem vazamento de credenciais que você nunca chegou a guardar. OAuth e identidade delegada explica como essa transferência realmente funciona.

JunoAlguém mais garante por você Pense nisso como ser afiançado por alguém. Você não se prova diretamente para o aplicativo. Alguém em quem o aplicativo já confia confirma você, e o aplicativo aceita a palavra dessa pessoa.

É por isso que você é redirecionado para uma página do Google e depois trazido de volta. A comprovação aconteceu lá.

JunoAlguém mais garante por você Isso elimina uma categoria de risco e adiciona uma dependência. Seu login agora fica fora do ar quando o do provedor fica, e usuários sem conta lá simplesmente não conseguem se cadastrar.

É por isso que a maioria dos aplicativos para consumidor oferece isso junto com email e senha em vez de substituir, e depois precisa lidar com a mesma pessoa chegando pelas duas portas.

JunoAlguém mais garante por você Vale ser preciso aqui, porque o vocabulário costuma sair errado. O OAuth concede autorização delegada: permissão para seu aplicativo agir sobre um recurso. O OpenID Connect é a camada de identidade construída em cima disso, e os botões "entrar com" são OIDC. Usar um access token do OAuth como prova de quem alguém é, em vez de um identity token do OIDC, é um erro real e comum.

O que você herda são as decisões do provedor. O tempo de vida da sessão dele, a recuperação de conta dele, a visão dele sobre o que um endereço de email significa. Se ele deixa alguém recuperar uma conta por número de telefone, isso agora também é a recuperação de conta do seu sistema.

A vinculação de contas é o ponto mais delicado. Casar um login delegado com uma conta existente pelo endereço de email é a primeira coisa que qualquer pessoa tenta, e isso só é seguro quando o provedor verifica o email, o que nem todos fazem.

Lado a lado

StatefulStatelessDelegado
Onde a identidade moraNo servidorNo token que o cliente carregaCom o provedor
Custo por requisiçãoUma consultaUma verificação de assinatura, ou uma consultaDepende do token que você acaba usando
Revogar acessoImediatoDifícil, geralmente espera a expiraçãoEm parte, decisão do provedor
Escala horizontalmentePrecisa de um repositório compartilhadoLivrementeLivremente
Principal coisa que você sacrificaCoordenação entre servidoresA capacidade de mudar de ideiaControle e independência

Nenhuma delas é a resposta certa. Elas otimizam para coisas diferentes, e é por isso que sistemas reais costumam usar mais de uma ao mesmo tempo: um login delegado para estabelecer quem você é, uma sessão para o site, tokens para a API.

JunoLado a lado Não tente decorar essa tabela ainda. Ela vai fazer muito mais sentido depois que você vir cada modelo funcionando.

A única frase que vale a pena levar para o próximo capítulo: stateful é para controle, stateless é para escala, delegado é para passar o problema para outra pessoa.

JunoLado a lado Quando você encontrar um sistema desconhecido, o jeito mais rápido de entendê-lo é perguntar onde a identidade é armazenada. Tudo o mais decorre dessa resposta.

Se o servidor guarda, procure como as sessões expiram e onde ficam armazenadas. Se o cliente guarda, olhe o que acontece quando um token vaza e como alguém o revoga. Se um provedor guarda, olhe como as contas são vinculadas.

JunoLado a lado A linha da revogação é a que decide a maioria das discussões reais, e costuma ser descoberta tarde demais. Times escolhem stateless pelas propriedades de escala, colocam em produção, e depois enfrentam o primeiro incidente que exige deslogar imediatamente uma pessoa específica.

O jeito honesto de encarar isso é que você está escolhendo em que vai ser ruim. Stateful é ruim em escala e bom em controle. Stateless é o inverso. Qualquer coisa que promete os dois silenciosamente reintroduziu uma consulta em algum lugar, o que é um design perfeitamente válido, e vale reconhecer isso pelo que é em vez de tratar como se fosse ganho de graça.

Experimente

Para cada sistema, indique o modelo que você esperaria e a propriedade que decide isso.

  1. Um painel administrativo interno onde revogar o acesso de alguém precisa surtir efeito imediatamente.
  2. Uma API pública atendendo um aplicativo mobile com algumas centenas de milhares de usuários.
  3. Um aplicativo de fotos cujo cadastro inteiro é um botão "Continuar com o Google".
  4. Uma arquitetura de microsserviços onde uma dezena de serviços precisa saber quem está chamando.
Compare suas respostas
#ModeloA propriedade decisiva
1StatefulRevogação imediata. Apague a sessão e a próxima requisição fica anônima. Nada mais oferece isso.
2StatelessNão há repositório de sessão compartilhado para coordenar conforme o tráfego cresce, e os clientes mobile carregam um token sem problema.
3DelegadoNenhum sistema de senhas para construir, armazenar ou ter vazado, e o cadastro mais rápido possível.
4Stateless, especificamente JWTsCada serviço consegue verificar o token sozinho, sem precisar perguntar a um servidor central a cada chamada.

O número 1 é o mais interessante. É o menor sistema da lista e o que tem menos pressão de escala, e mesmo assim leva o modelo que escala pior, porque aqui controle importa mais do que crescimento.

Para onde isso vai a seguir

Três famílias, cada uma resolvendo um problema de um jeito diferente.

Sessões e cookies desmonta a primeira: o que o servidor armazena, o que o navegador carrega, e os erros que transformam um modelo sólido em um modelo furado.