12k
All articles

Escolhendo uma Estratégia de Autenticação para um Novo Aplicativo Web

Escolha a autenticação de uma web app em 2026: sessões ou JWTs, OAuth 2.0/OIDC, passkeys e providers gerenciados, com revogação, CSRF e UX.

OpenReplay Team
OpenReplay Team
Escolhendo uma Estratégia de Autenticação para um Novo Aplicativo Web

Para um novo aplicativo web em 2026, a estratégia se alinha à arquitetura da sua aplicação: um monolito renderizado no servidor deve começar com sessões server-side em um cookie assinado e HttpOnly; um SPA, cliente mobile ou backend de API deve usar tokens de acesso de curta duração com rotação de refresh token mantidos em cookies HttpOnly; qualquer aplicação que precise de login social ou SSO corporativo deve adicionar OAuth 2.0/OIDC por cima; e se a autenticação não for o produto em si, o padrão mais defensável é um provedor gerenciado. Adicione passkeys como o fator resistente a phishing independentemente da base escolhida. O restante deste artigo justifica cada ramificação com base em critérios concretos — revogação, escalabilidade, exposição a CSRF/XSS, complexidade e os modos de falha de UX que só aparecem em produção.

A armadilha em que a maioria dos novos aplicativos cai é adotar JWTs porque algum tutorial os utilizou, sem uma razão de escalabilidade stateless que justifique o custo de revogação. Esta é uma decisão comparativa, não um padrão — portanto, aqui estão os critérios, uma tabela comparativa e uma matriz que mapeia o arquétipo da aplicação a uma recomendação.

Principais Conclusões

  • Sessões versus tokens é uma questão de onde o estado de autenticação reside — no servidor ou no cliente — enquanto cookies versus o header Authorization decide apenas como a credencial trafega; confundir os dois é o motivo mais comum pelo qual novos aplicativos adotam JWTs desnecessariamente.
  • Armazenar JWTs no localStorage é agora um antipadrão: qualquer XSS na sua página pode ler e exfiltrar o token, portanto mantenha as credenciais em cookies HttpOnly que a camada JavaScript não consegue acessar.
  • O problema difícil com JWTs não é emiti-los — é revogá-los; o logout imediato requer tempos de vida curtos dos tokens com rotação de refresh token ou uma lista de negação server-side, o que silenciosamente reintroduz o estado que os JWTs pretendiam eliminar.
  • O OAuth 2.1 ainda é um Internet-Draft do IETF (draft-15, março de 2026), não um RFC ratificado, mas sua direção está definida: PKCE obrigatório, correspondência exata de redirect URI e remoção dos fluxos Implicit e de credenciais de senha.
  • Se a autenticação não for o seu produto, um provedor gerenciado (Auth0, Clerk, Supabase Auth, WorkOS, Stytch) é o padrão pragmático de 2026 — você herda sessões, login social, MFA e passkeys sem assumir a responsabilidade pela superfície de ataque.

O enquadramento que corta o ruído: onde o estado reside versus como ele trafega

Sessões versus tokens é uma questão de onde o seu estado de autenticação reside — no servidor ou dentro da credencial do cliente — enquanto cookies versus o header Authorization decide apenas como essa credencial trafega via HTTP. Esses são eixos ortogonais, e tratá-los como uma única decisão é o motivo pelo qual tantas equipes adotam JWTs desnecessariamente. Essa distinção é a espinha dorsal esclarecedora de tudo que se segue, e o fornecedor de autenticação Authgear enquadra da mesma forma.

Uma sessão é stateful: o servidor armazena o registro autoritativo e entrega ao cliente um ID opaco que aponta de volta para ele — o modelo do cartão-chave de hotel, onde a recepção mantém a lista mestra. Um JWT é stateless: as claims são autocontidas e assinadas, então qualquer servidor pode verificá-las sem uma consulta — o modelo do passaporte digital, onde qualquer oficial confia na assinatura sem ligar para o emissor. Você pode entregar qualquer um via cookie ou via header Authorization. Cookies e headers são transporte; sessões e tokens são arquitetura.

CritérioSessões server-sideJWT / tokens autocontidos
Onde o estado resideServidor (armazenamento)Cliente (token assinado)
RevogaçãoImediata — exclui o registroDifícil — TTL curto + rotação, ou lista de negação
EscalabilidadeRequer armazenamento compartilhado (ex.: Redis)Stateless; sem consulta por requisição
Tamanho da credencial~32 bytes de ID opacoFrequentemente 800+ bytes de claims por requisição
Principal superfície de ataqueCSRF (cookies enviados automaticamente)XSS (roubo de token de armazenamento legível pelo JS)
Melhor usoMonolito renderizado no servidorAPI-first, SPA, mobile, microsserviços

O contraste de tamanho resume o trade-off stateless versus stateful em uma linha: um cookie de sessão carrega aproximadamente 32 bytes de ID opaco e requer uma consulta ao servidor, enquanto um JWT carrega as claims inline — frequentemente 800+ bytes em cada requisição — e dispensa a consulta. Você está trocando largura de banda e latência de revogação pela eliminação de um armazenamento.

Sessões: o padrão para aplicações renderizadas no servidor

Para um monolito renderizado no servidor, comece com sessões server-side em um cookie assinado, HttpOnly, Secure e SameSite — é a opção correta mais simples e oferece revogação instantânea gratuitamente. O navegador envia o cookie automaticamente, você não armazena nenhum estado de autenticação em JavaScript, e deslogar um usuário é uma única exclusão no servidor.

A mecânica tem quatro etapas: o usuário envia as credenciais, o servidor cria um registro de sessão e retorna seu ID em um cookie, o navegador reenvia esse cookie em cada requisição, e o logout destrói ambos os lados. Faça o hash das senhas com Argon2 antes de tocarem no armazenamento — o OWASP Password Storage Cheat Sheet o trata como o algoritmo de primeira escolha atual. Gere IDs de sessão com alta entropia e nunca incorpore dados sensíveis neles; o OWASP Session Management Cheat Sheet é a referência canônica para comprimento de ID, rotação e timeout.

# FastAPI: issue an opaque session, store it server-side, set a hardened cookie
import secrets
from fastapi import FastAPI, Response
# redis client + Argon2 password verification omitted for brevity

app = FastAPI()
SESSION_TTL = 60 * 60 * 24  # 24 hours

@app.post("/login")
async def login(response: Response):  # verify Argon2 hash first
    session_id = secrets.token_urlsafe(32)          # ~256 bits of entropy
    await redis.setex(f"sess:{session_id}", SESSION_TTL, user_id)
    response.set_cookie(
        "session", session_id,
        httponly=True,    # JS cannot read it -> blunts XSS exfiltration
        secure=True,      # HTTPS only
        samesite="lax",   # mitigates CSRF on top-level navigations
        max_age=SESSION_TTL,
    )
    return {"ok": True}

Este trecho mostra o ponto central das sessões: o segredo nunca reside em JavaScript, e o servidor mantém a fonte da verdade. Dois trade-offs decorrem disso. Primeiro, escalabilidade: como o registro é server-side, uma implantação com múltiplas instâncias precisa de sessões fixas (sticky sessions) ou — melhor — um armazenamento compartilhado como o Redis para que qualquer instância possa resolver qualquer sessão. Segundo, CSRF: cookies são enviados automaticamente em requisições cross-site, portanto combine SameSite com um token CSRF usando comparação em tempo constante, conforme o OWASP CSRF Cheat Sheet. O flag HttpOnly fecha o caminho de leitura por XSS; SameSite combinado com um token fecha o caminho de CSRF.

JWTs e tokens: escalabilidade stateless com um custo de revogação

Recorra a JWTs puros quando você tiver uma razão concreta de escalabilidade stateless — uma API servindo múltiplos tipos de cliente, microsserviços que passam identidade entre si, ou acesso cross-domain onde cookies são inconvenientes — não como padrão. O benefício é real: um token assinado é verificado localmente contra uma chave pública, então você dispensa a consulta ao armazenamento por requisição e qualquer nó pode autenticar qualquer requisição.

O problema difícil com JWTs não é emiti-los — é revogá-los. Como o token é autocontido, ele permanece válido até expirar independentemente do que o seu servidor pensa. O logout imediato, portanto, requer tempos de vida curtos dos tokens com rotação de refresh token, ou uma lista de negação server-side que você verifica em cada requisição — e essa lista de negação silenciosamente reintroduz o estado exato que os JWTs deveriam eliminar. O padrão usual é um token de acesso de curta duração combinado com um refresh token de maior duração e rotacionado; o OAuth 2.0 Security Best Current Practice (RFC 9700, janeiro de 2025) é a referência ratificada para rotação de refresh token e tempos de vida curtos de tokens de acesso — uma fonte mais sólida do que os TTLs em números redondos que circulam em posts de blog.

O local onde você armazena o token determina seu raio de impacto. Armazenar JWTs no localStorage é agora um antipadrão: qualquer XSS na sua página pode lê-lo e exfiltrar o token, portanto mantenha os tokens em cookies HttpOnly que a camada JavaScript não consegue acessar. Para aplicações de navegador especificamente, o rascunho de melhores práticas do IETF OAuth for Browser-Based Apps recomenda o fluxo Authorization Code com PKCE e alerta que JavaScript malicioso pode fazer muito mais do que apenas ler um token.

# FastAPI: verify an access token with PyJWT (not python-jose)
import jwt  # PyJWT
from datetime import datetime, timezone
from fastapi import HTTPException

def verify_access_token(token: str, public_key: str) -> dict:
    try:
        return jwt.decode(
            token, public_key,
            algorithms=["RS256"],          # never accept "none"; pin the alg
            options={"require": ["exp", "iat", "sub"]},
        )
    except jwt.ExpiredSignatureError:
        raise HTTPException(401, "token expired")  # client should refresh
    except jwt.InvalidTokenError:
        raise HTTPException(401, "invalid token")

# When you mint tokens, use timezone-aware UTC:
# exp = datetime.now(timezone.utc) + timedelta(minutes=15)

Duas notas de versão mantêm isso atualizado. Use o PyJWT — o tutorial oficial de segurança do FastAPI migrou para ele (import jwt) junto com pwdlib, e o python-jose agora tem manutenção mínima. E gere expirações com datetime.now(timezone.utc): datetime.utcnow() foi depreciado no Python 3.12 e, conforme a documentação de remoções pendentes do Python, permanece depreciado sem versão de remoção agendada — ainda funciona, mas a chamada com fuso horário explícito é a substituição recomendada.

OAuth 2.0/2.1 e OIDC: identidade delegada quando necessário

Use OAuth 2.0 com OpenID Connect quando quiser que os usuários façam login com um provedor de identidade existente — “Entrar com o Google,” GitHub, Microsoft, Okta — ou quando precisar de SSO corporativo. Este é o modelo da lista VIP: você não verifica o usuário diretamente; você confia em um emissor que já o conhece. O OAuth 2.0 lida com a autorização delegada (tokens de acesso para APIs), e o OpenID Connect adiciona um token de identidade por cima para que você saiba quem é o usuário.

Vale a complexidade apenas quando a delegação ou o SSO é um requisito genuíno, pois o fluxo traz partes móveis reais — códigos de autorização, PKCE, escopos, redirect URIs e rotação de chaves JWKS. Para um MVP com login próprio e sem requisito de login social, o OAuth é excessivo; você carregará a maquinaria de redirecionamento sem usar a delegação para a qual ela existe.

Se você o adotar, mire no perfil moderno. O OAuth 2.1 ainda é um Internet-Draft do IETF (draft-15, publicado em 2 de março de 2026), não um RFC ratificado, mas sua direção está definida. As principais diferenças em relação ao OAuth 2.0 são que o PKCE é obrigatório para todos os clientes que usam o fluxo de código de autorização, os redirect URIs devem ser comparados usando correspondência exata de string, e os fluxos Implicit e Resource Owner Password Credentials são omitidos. O próprio IETF observa que os Internet-Drafts são válidos por no máximo seis meses e que é inadequado citá-los exceto como “trabalho em andamento”, portanto trate o 2.1 como a consolidação alvo — já aplicada na prática pelos principais provedores de identidade — em vez de um padrão publicado. O conteúdo é o que importa: use Authorization Code + PKCE para todos os clientes, incluindo os confidenciais server-side, e abandone os redirect URIs com wildcard.

Passkeys e WebAuthn: a direção resistente a phishing

Adicione passkeys como o fator resistente a phishing, não como uma ideologia. Construída sobre WebAuthn/FIDO2, uma passkey é um par de chaves pública/privada onde a chave privada permanece no hardware seguro do dispositivo e assina um desafio do servidor após uma verificação biométrica local ou PIN. Como a credencial é vinculada à origem e nunca transmitida, ela resiste a phishing, replay e credential stuffing de uma forma que senhas e códigos SMS não conseguem.

O cenário regulatório mudou decisivamente. O NCSC anunciou em abril de 2026 que as passkeys devem ser agora a primeira escolha de login dos consumidores em todos os serviços digitais, e que começará a recomendar passkeys onde quer que um serviço as suporte, e verificação em duas etapas onde não suportar — uma mudança refletida por meio de uma atualização contínua das diretrizes em vez de uma mudança repentina e única. O suporte é efetivamente universal: as passkeys são suportadas pelos principais sistemas operacionais, incluindo Windows, ChromeOS, macOS, Android, iOS e muitas distribuições Linux, bem como navegadores como Chrome, Firefox, Edge e Safari.

O suporte a passkeys está resolvido; o cadastramento de passkeys não está — e essa é a verdadeira restrição de seleção. Segundo o Corbado’s Passkey Benchmark 2026 (um benchmark de fornecedor, não uma estatística de população neutra), o cadastramento web na primeira tentativa gira em torno de 49–83% no iOS, mas apenas 25–39% no Windows. Trate a UX de cadastramento e a recuperação de conta como problemas de design de primeira classe: ofereça passkeys como uma atualização opt-in com um fallback claro, e projete a recuperação antes do lançamento, pois um dispositivo perdido sem caminho de recuperação equivale a um usuário bloqueado.

Auth-as-a-Service: o padrão pragmático quando autenticação não é seu produto

Se a autenticação não for o seu produto, o padrão mais defensável de 2026 é um provedor gerenciado — Auth0, Clerk, Supabase Auth, WorkOS ou Stytch — pois você herda sessões, login social, MFA e passkeys sem assumir a responsabilidade pela superfície de ataque ou pela manutenção. Esses fornecedores entregam SDKs plug-and-play que lidam com o ciclo de vida completo, incluindo a maquinaria de redirecionamento OAuth e as cerimônias WebAuthn que você de outra forma teria que construir e depurar.

A decisão é diretamente econômica. Construir a sua própria solução oferece controle e custo zero por MAU; um provedor gerenciado oferece uma implementação robusta, tempo mais rápido até o login funcional e a rotação de plantão de outra empresa para o caminho crítico de segurança. Para a maioria dos aplicativos greenfield onde o login é um requisito básico em vez de um diferencial, a rota gerenciada é o ponto de partida racional — e você pode migrar para fora dela mais tarde se a escala ou o custo exigirem.

Uma matriz de decisão para métodos de autenticação

A estratégia segue o arquétipo da aplicação. Mapeie sua arquitetura para a linha correspondente, comece por ela e adicione passkeys em todos os casos como o fator resistente a phishing.

Arquétipo da aplicaçãoComece comAdicione quando necessário
Monolito renderizado no servidorSessões server-side (cookie HttpOnly)OIDC para login social; passkeys opt-in
SPA / mobile / backend de APITokens de acesso de curta duração + rotação de refresh, em cookies HttpOnlyOIDC para identidade de terceiros
Precisa de login social ou SSO corporativoOAuth 2.0 + OIDC (mire no perfil 2.1)Sessões ou tokens para suas próprias superfícies
Autenticação não é o seu produtoProvedor gerenciado (Auth0/Clerk/Supabase/WorkOS/Stytch)— já inclui o restante
Alta segurança / voltado ao consumidorQualquer base acima + passkeysFluxo de recuperação projetado antecipadamente

A arquitetura de produção comum é híbrida: sessões para o aplicativo web renderizado no servidor, JWTs para a API consumida por SPAs e mobile, OIDC para login social e passkeys como uma atualização opt-in. A tendência do setor por trás de tudo isso é enviar a senha menos vezes, em menos lugares, com mais escopo e responsabilidade — que é exatamente o motivo pelo qual tokens com escopo substituíram a autenticação Git baseada em senha e por que as passkeys estão agora deslocando as senhas completamente.

O que o session replay revela sobre os modos de falha de UX em autenticação

Session replay é uma técnica que reconstrói o fluxo real do lado do cliente, e reproduzir jornadas de autenticação revela um catálogo reconhecível de modos de falha que os logs de backend não capturam — pois escolher a estratégia certa no papel não garante que os usuários consigam realmente fazer login (estas são capacidades da técnica, não taxas de incidentes medidas):

  • Loops de logout silencioso por expiração de token. Quando um refresh token expira no meio de uma sessão, um SPA pode redirecionar o usuário para o login repetidamente. O replay mostra o loop de redirecionamento na linha do tempo, que os logs de erro perdem porque cada requisição individual retorna um 401 tecnicamente válido.
  • Cadastramento de passkey abandonado. O replay mostra onde os usuários desistem da cerimônia WebAuthn — o prompt do sistema operacional aparece e o usuário cancela — que é exatamente a falha que os baixos números de cadastramento no Windows preveem.
  • Dead-ends de redirecionamento OAuth. Um redirecionamento pós-consentimento que chega a uma página em branco ou de erro (redirect URI incompatível, parâmetro state perdido) é invisível nos seus logs de backend porque o round-trip acontece fora do seu servidor; o replay captura o dead-end como o usuário o viu.
  • Falhas de cookie específicas de navegador. Problemas com SameSite e cookies Secure que não se reproduzem localmente aparecem quando o replay correlaciona a sessão com falha a um navegador e versão específicos.

O ponto é que a autenticação é uma superfície de UX, não apenas uma escolha de protocolo — e a estratégia que você escolhe determina quais dessas classes de falha você estará depurando.

Escolha a base que corresponde à arquitetura da sua aplicação, proteja o armazenamento de credenciais (cookies HttpOnly, nunca localStorage) e adicione passkeys como o fator opt-in resistente a phishing. Para a maioria dos novos aplicativos, o primeiro movimento honesto é um provedor gerenciado ou um monolito com sessões em primeiro lugar; recorra a JWTs puros apenas quando um requisito real de escalabilidade stateless justificar o custo de revogação. Assim que o login for publicado, observe o fluxo real — a lacuna entre uma estratégia sólida e uma funcional é exatamente o detalhe de UX que só aparece em produção.

Perguntas Frequentes

Devo usar JWTs por padrão em um novo aplicativo web?

Não. Use JWTs puros apenas quando você tiver uma razão concreta de escalabilidade stateless, como uma API servindo múltiplos tipos de cliente ou microsserviços passando identidade entre si. Para um monolito renderizado no servidor, as sessões server-side são mais simples e oferecem revogação instantânea. Os JWTs são autocontidos, portanto revogá-los antes da expiração requer tempos de vida curtos dos tokens com rotação de refresh token ou uma lista de negação server-side, o que reintroduz o estado exato no servidor que os JWTs pretendiam eliminar.

Um token de sessão é o mesmo que um JWT?

Não. Um token de sessão é um ID opaco, de aproximadamente 32 bytes, que aponta para o estado de autenticação armazenado no servidor, portanto cada requisição requer uma consulta. Um JWT carrega suas claims inline e é verificado localmente contra uma assinatura sem consulta, frequentemente adicionando 800 ou mais bytes por requisição. Os dois representam o trade-off stateless versus stateful: uma sessão mantém o estado no servidor, enquanto um JWT o move para a credencial assinada do cliente.

Posso armazenar um JWT no localStorage se eu sanitizar meus inputs?

Não, armazenar um JWT no localStorage é um antipadrão independentemente da sanitização de inputs, pois qualquer falha de cross-site scripting em qualquer parte da página pode ler o localStorage e exfiltrar o token. Mantenha as credenciais em cookies HttpOnly, que o JavaScript não consegue acessar, e combine-os com SameSite e um token CSRF. Para aplicações de navegador, o rascunho de melhores práticas do IETF OAuth for Browser-Based Apps recomenda o fluxo Authorization Code com PKCE em vez de armazenamento de token legível pelo cliente.

O OAuth é excessivo para um MVP com login próprio?

Sim, se o seu MVP precisa apenas de login próprio sem login social ou SSO corporativo, o OAuth 2.0 adiciona maquinaria que você não utilizará. O fluxo traz códigos de autorização, PKCE, escopos, redirect URIs e rotação de chaves JWKS, todos existentes para suportar identidade delegada. Adote OAuth com OpenID Connect apenas quando 'Entrar com o Google,' GitHub ou SSO corporativo for um requisito genuíno; caso contrário, comece com sessões ou um provedor gerenciado.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.