Login com Google, OAuth e outros redirecionamentos
Um login com senha fica na sua página até o envio, então a receita de coleta cobre esse caso. Um login com o Google ou com outro provedor OAuth/OIDC normalmente não fica: o navegador sai da sua página para o provedor e volta para um callback no seu servidor. Esta página mostra como preservar a evidência do navegador nesse fluxo e como verificá-la.
Por que um redirecionamento perde o contexto
Seção intitulada “Por que um redirecionamento perde o contexto”O SDK mantém a sessão, as observações, os handles de evento e o token apenas na memória da página. Ele nunca os grava em armazenamento, e não deve gravar: o token é uma credencial de uso único para um evento de negócio. Uma navegação de página inteira descarta tudo isso. Se a página inicia o SDK e depois navega para o provedor:
- a primeira requisição do SDK pode ser interrompida pela navegação, e assim nenhuma coleta chega ao FingerTrackID;
- mesmo uma coleta concluída fica inútil depois que o handle dela se perde, porque só aquela página poderia ter pedido o contexto do evento;
- o seu callback roda no servidor, onde não há SDK, então o login é avaliado com
event_context_token: null.
Nada falha de forma visível: o login funciona e a avaliação é armazenada. O que falta é a evidência do navegador.
O que acontece sem um contexto
Seção intitulada “O que acontece sem um contexto”Seu backend ainda envia a avaliação de login (etiqueta login; LOGIN na v1) com
event_context_token: null, que o
schema da requisição
aceita como “nenhuma coleta fornecida”. A avaliação armazenada fica então com:
| Campo | Sem evidência do navegador |
|---|---|
assessment_status |
INCONCLUSIVE |
trust_score |
null (sem nota; não é 0, não é 100) |
device_intelligence_snapshot_id, device_intelligence_snapshot_version, sdk_version, device_id |
null |
signals |
Todos os 13 indicadores NOT_EVALUATED |
limitation_codes |
Por exemplo, COLLECTOR_NOT_LOADED |
evidence_coverage |
0 |
device_intelligence_snapshot_id: null é a forma confiável de saber, só pela resposta, que
nenhuma evidência do navegador foi anexada.
Login com senha
Seção intitulada “Login com senha”Use as receitas da página de coleta, para um
POST de formulário clássico ou uma
aplicação de página única. O contexto vai
com a requisição que o seu backend avalia: o formulário de senha, ou o formulário do código de MFA
quando o seu login pede um. O backend avalia depois de verificar a senha e o MFA, se houver, usando
o token só enquanto o expiresAt dele deixa tempo suficiente
(wrapper fail-open).
Logins com redirecionamento: dois padrões
Seção intitulada “Logins com redirecionamento: dois padrões”Os dois padrões produzem um contexto para o login e o levam até o seu backend sem nunca colocá-lo
numa URL, no state do OAuth ou no armazenamento do navegador. Escolha pelo tempo que os usuários
passam no provedor em comparação com a
janela de evidência da sua aplicação:
| Padrão | Coleta | Melhor quando | Custo |
|---|---|---|---|
| A. Antes do redirecionamento | Na primeira intenção em direção a “Continuar com o Google”, com o contexto obtido no clique nele | A ida e volta até o provedor cabe com folga dentro da janela | Até o seu teto antes do redirecionamento |
| B. Depois do callback | Numa página intermediária sua, antes de a sessão ser concluída | Os usuários podem passar muito tempo no provedor (escolha de conta, senha, 2FA do provedor), ou a janela é de 60 segundos | Uma página a mais, que espera até o seu teto |
Com uma janela de 60 segundos, o tempo gasto no provedor corre contra o token, então o padrão B
é a escolha mais confiável. Com 300 segundos, o padrão A cobre a maioria das idas e voltas ao
provedor. Logins por popup, One Tap e FedCM não saem da página, e o usuário pode nunca tocar num
botão seu: chame prepareNextAction() no primeiro focusin ou pointerdown do formulário de login
ou do contêiner que abriga o login, no pointerenter, focus ou pointerdown do botão de login,
ou quando o seu código exibe o prompt do One Tap ou do FedCM depois de uma ação do usuário. Nunca no
carregamento da página: não prepare quando o One Tap ou o FedCM se exibe sozinho enquanto a página
carrega, porque um prompt exibido a cada carregamento abriria, a cada carregamento, uma coleta que
conta como uso (a unidade de uso), também para visitantes que nunca fazem login. Obtenha o contexto
com takeActionContext() quando a credencial chegar e envie-o com a requisição que faz o POST da
credencial, como num login com senha. Sem uma preparação antes, o callback da credencial
espera até o seu teto enquanto a coleta começa, e muitas vezes chega ao seu backend com
null.
Padrão A: obtenha o contexto no clique, antes de redirecionar
Seção intitulada “Padrão A: obtenha o contexto no clique, antes de redirecionar”- Chame
prepareNextAction()da receita de coleta na primeira intenção em direção ao botão do Google (pointerenter,focus,pointerdown), nunca no carregamento da página, para que o contexto possa estar pronto até o clique. No clique, obtenha-o comtakeActionContext(), que espera no máximo o seu teto. Em dispositivos de toque, opointerdownvem logo antes do clique, então o clique pode esperar atéSUBMIT_WAIT_MS. - Envie o contexto (ou
nullcom o motivo) ao seu backend numa requisição de mesma origem, protegida contra CSRF. O backend o guarda no servidor, na sessão pré-login que já guarda ostatedo OAuth e o verificador PKCE (ou o cookie criptografado,HttpOnlye de curta duração que o seu framework usa para eles), vinculado a essestate. - Inicie o redirecionamento para o provedor como você já faz hoje. Não chame
close()antes: fechar pode invalidar o contexto ainda não consumido. - No callback, depois de verificar a resposta do provedor e o seu próprio MFA, se houver, pegue o
contexto armazenado e apague-o (uso único). Envie o token se o
expiresAtdele ainda deixa a sua margem; caso contrário, envienulle registreEXPIRED_BEFORE_CALLBACKno log.
Se a sua biblioteca de autenticação inicia o fluxo do provedor a partir do navegador (uma chamada no
estilo signIn('google')), mantenha essa chamada como último passo e envie o contexto numa
requisição própria logo antes dela, como acima. A página é descartada no redirecionamento sem
close(); a sessão do SDK dela ocupa uma das suas
vagas de sessões simultâneas até que os
30 minutos dela se esgotem.
// prepareNextAction and takeActionContext come from the collection recipe; csrfToken and// startGoogleSignIn stand for your existing CSRF token and provider redirect.// The first intent prepares the context; a page load alone opens no session and bills nothing.for (const intent of ['pointerenter', 'focus', 'pointerdown']) googleButton.addEventListener(intent, () => prepareNextAction())
googleButton.addEventListener('click', async (event) => { event.preventDefault() googleButton.disabled = true // Show progress; the wait is bounded by SUBMIT_WAIT_MS. const { context, failure } = await takeActionContext() try { // Your endpoint: stores { context, failure } server-side with the OAuth state. await fetch('/auth/login-context', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ eventContext: context, contextFailure: failure }), }) } catch { // The login must not depend on this request; the callback then assesses with null. } startGoogleSignIn() // Your existing redirect to the provider, unchanged.})Padrão B: colete numa página depois do callback
Seção intitulada “Padrão B: colete numa página depois do callback”- O callback verifica a resposta do provedor, mas não conclui a sessão ainda. Ele
armazena no servidor um login pendente de curta duração (vinculado ao navegador, por exemplo pela
mesma sessão pré-login) e redireciona para uma página intermediária sua, como
/login/continue, numa das origens permitidas da aplicação. - Essa página coleta e obtém o contexto com
takeActionContext(), que espera no máximo o seu teto, e então o envia por POST (ounullcom o motivo) ao seu backend. - O backend verifica o login pendente, executa o seu próprio MFA, se houver, avalia o login, depois conclui a sessão como já faz hoje e responde com o próximo destino.
- A página chama
close()depois dessa resposta e segue a navegação.
A página intermediária precisa sempre concluir o login: mantenha a espera limitada e dê a ela um
formulário <noscript> (ou um botão “Continuar” simples) que envia por POST sem contexto. Se o
usuário nunca chega a /login/finish, o login pendente expira como qualquer login abandonado.
Quando a sua aplicação pede o próprio MFA depois do provedor, essa página de MFA já é a página
depois do callback: use a receita de login com senha no
formulário dela e dispense a página extra.
// On /login/continue. takeActionContext and closeSdk come from the collection recipe.async function finishLogin() { const { context, failure } = await takeActionContext() const response = await fetch('/login/finish', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ eventContext: context, contextFailure: failure }), }) const { next } = await response.json() // Your own response shape. await closeSdk() // The backend has answered: free the session slot (at most 1500 ms). window.location.assign(next)}finishLogin()Regras do backend para os dois padrões
Seção intitulada “Regras do backend para os dois padrões”- Envie exatamente uma avaliação de login (etiqueta
login;LOGINna v1) para cada login concluído, com senha ou por provedor, com os mesmos pseudônimossubject_refeaccount_refpara a mesma pessoa e a mesma conta. Nunca envie o identificador de sujeito do provedor nem o e-mail. Uma conta fica vinculada aosubject_refcom que foi enviada pela primeira vez: um login por provedor que associa a mesma conta a outra pessoa retorna409 binding-conflict(referências de pessoa e de conta). - Aceite o contexto só da requisição que a sua própria página enviou para este login; apague um contexto armazenado depois do primeiro uso e nunca o use para outro login.
- Envie o token só enquanto o
expiresAtdeixa a sua margem; caso contrário, envienull. Em caso de400 event-context-invalid, avalie mais uma vez, como uma nova operação, comnull(wrapper fail-open). - Registre em log, para cada avaliação, se ela levou um token e, quando não levou, o
contextFailuredo navegador ou o seu próprio motivo (NO_CONTEXT_RECEIVED,EXPIRED_BEFORE_CALLBACK). Nunca registre o token em log. - O login em si nunca espera pelo FingerTrackID além do seu próprio orçamento, e nunca falha por causa dele.
Confira se a evidência do SDK chega
Seção intitulada “Confira se a evidência do SDK chega”Faça estas verificações em cada origem permitida que você usa, para um login com senha e para cada provedor, antes de ligar a integração para todos:
- Painel de rede do navegador. As requisições ao seu domínio de coleta terminam com uma resposta
antes de a página sair. Uma requisição mostrada como cancelada logo antes de um redirecionamento
significa que a coleta rodou tarde demais ou que o redirecionamento não esperou. Cada requisição
OPTIONSao domínio de coleta é respondida com204; um400ali significa que uma instrumentação da página adicionou um cabeçalho à requisição (cabeçalhos de rastreamento). - Logs do seu backend. A maioria dos logins leva um token. Os logins sem token têm um motivo, e
os motivos são esperados (por exemplo,
WAIT_EXCEEDEDem redes lentas), não um mesmo código fixo em todo login por provedor. - A resposta da avaliação. Com evidência do navegador,
device_intelligence_snapshot_idesdk_versionnão sãonull,BOT_AUTOMATIONéDETECTEDouNOT_DETECTED,evidence_coveragefica acima de0elimitation_codesnão contémCOLLECTOR_NOT_LOADED.data_freshness.maximum_required_age_msmostra a janela de evidência da sua aplicação. - Console, Integração e saúde. Última coleta mostra um horário recente em vez de nenhuma coleta na janela; em Da coleta à avaliação, Coletas aceitas cresce com os seus logins e Correlacionadas (avaliações que encontraram a coleta do navegador delas) fica perto de Avaliações; Correlação navegador → backend mostra essa proporção. Esses números cobrem as últimas 24 horas registradas e não são em tempo real.
- Console, Acessos. Abra um login por provedor: Cobertura de evidência fica acima de zero, o indicador Automação (robô) mostra Detectado ou Não detectado em vez de Não avaliado, e as limitações não incluem Coletor não carregado.
Se todo login por provedor falha nestas verificações enquanto os logins com senha passam, o redirecionamento está descartando o contexto: aplique o padrão A ou o B. Veja também o checklist de integração.