Criação condicional de chave de acesso (promoção pós-login)

Este guia detalha como registrar uma chave de acesso de forma automática e silenciosa para um usuário imediatamente após um login bem-sucedido com senha, minimizando o atrito e aumentando a adoção de chaves de acesso.

O momento de acionamento certo

A criação automática de chaves de acesso (também conhecida como criação condicional ou promoção silenciosa pós-login) SÓ PODE ser acionada imediatamente após um login completo e bem-sucedido que envolveu uma senha.

  • Não tente criar condicionalmente para fluxos sem senha (por exemplo, links mágicos, OTP por SMS ou federação de identidade).
  • Se a autenticação multifator for necessária, você PRECISA esperar até que todos os fatores sejam bem-sucedidos antes de iniciar a criação condicional.
  • Verifique se uma sessão de usuário válida e autenticada está ativa antes de fazer solicitações para endpoints de criação.

Etapas de implementação

1. Cancelar ações de preenchimento automático anteriores

Se a página de login usar o preenchimento automático de formulários (interface condicional/Get), a chamada de recebimento de credenciais ativa precisará ser cancelada para evitar conflitos no navegador.

  • Chame abortController.abort() no AbortController anexado à solicitação de preenchimento automático navigator.credentials.get() pendente antes de chamar navigator.credentials.create().

2. Detecção de recursos

Verifique se a criação condicional está disponível conferindo conditionalCreate com PublicKeyCredential.getClientCapabilities().

const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate) {
  // Conditional create is available
}

3. Criar uma chave de acesso com a criação condicional

  • Transmita mediation: 'conditional' nas opções navigator.credentials.create(). Isso sinaliza ao navegador para processar o fluxo de criação de chaves de acesso em segundo plano ou de forma contextual, sem mostrar caixas de diálogo modais invasivas.
  • Preencha excludeCredentials com os IDs de credencial de chave de acesso do usuário para evitar o registro de chaves duplicadas.

4. Tratamento de erros silenciosos

  • Insira o comando de criação da chave de acesso (navigator.credentials.create) em um bloco try/catch. É OBRIGATÓRIO capturar e ignorar silenciosamente as exceções típicas voltadas ao usuário (InvalidStateError, NotAllowedError, AbortError) sem renderizar nenhuma interface de erro para o usuário.

5. Verificação de presença do usuário do lado do servidor

  • O endpoint de verificação do lado do servidor PRECISA flexibilizar o requisito de presença do usuário (UP, na sigla em inglês) (requireUserPresence: false) SOMENTE ao verificar as credenciais produzidas por um gatilho de criação condicional. A verificação de presença rigorosa precisa permanecer ativa para criações explícitas padrão.

6. Tratar falhas na verificação do servidor de forma adequada

  • Se navigator.credentials.create() for bem-sucedido, mas a busca de verificação do servidor retornar uma resposta inválida (por exemplo, falha na verificação de assinatura), invoque PublicKeyCredential.signalUnknownCredential() para evitar que credenciais órfãs permaneçam no provedor de chaves de acesso.

Exemplo de código

// optionsFetch and registerVerifyFetch are app-defined server endpoint requests
import { optionsFetch, registerVerifyFetch } from './api.js';

async function triggerConditionalCreate(loginAbortController) {
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  if (capabilities.conditionalCreate !== true) {
    return; // Platform does not support conditional creation
  }

  // 1. Abort any active autofill conditional-get controllers to clear the WebAuthn pipeline
  loginAbortController.abort();

  // 2. Fetch creation options signaling the backend that this is a conditional request
  const creationOptionsJSON = await optionsFetch({ conditional: true });
  const publicKey = PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);

  let credential;
  try {
    // 3. Invoke silent credentials creation prompt
    credential = await navigator.credentials.create({ 
      publicKey,
      mediation: 'conditional' // Silent background creation mediation
    });
  } catch (e) {
    // 4. Silently swallow common WebAuthn browser exceptions
    if (['InvalidStateError', 'NotAllowedError', 'AbortError'].includes(e.name)) {
      return; 
    }
    console.error('Unexpected conditional create error:', e);
    return;
  }

  // 5. Server verification step using dedicated Try/Catch block
  let encodedResponse = credential.toJSON();
  try {
    const response = await registerVerifyFetch(encodedResponse);
    if (!response.ok) {
      // If the server verification fails, clean up using Signal API
      if (PublicKeyCredential.signalUnknownCredential) {
        await PublicKeyCredential.signalUnknownCredential({
          rpId, // RP ID must match the one defined on the server
          credentialId: encodedResponse.id
        });
      }
    }
  } catch (serverErr) {
    console.error('Verification network failure:', serverErr);
    if (PublicKeyCredential.signalUnknownCredential) {
      await PublicKeyCredential.signalUnknownCredential({
        rpId, // RP ID must match the one defined on the server
        credentialId: encodedResponse.id
      });
    }
  }
}

Estratégias de fallback

Substituto para detecção de recursos de chave de acesso

Sempre instale "webauthn-polyfills" e importe-o no contexto. Se PublicKeyCredential for compatível, PublicKeyCredential.getClientCapabilities também será.

import 'webauthn-polyfills';

Substituição de sincronização da API Signal

A API WebAuthn Signal (webauthn-signals) é uma otimização progressiva usada para manter os gerenciadores de senhas sincronizados com o estado das credenciais do servidor.

  • Experiência de fallback: restrita por if (PublicKeyCredential.signalUnknownCredential). Se não houver suporte, a sincronização de verificação em segundo plano será ignorada sem gerar exceções do navegador.

Easy JSON Serialization Fallback

Sempre instale "webauthn-polyfills" e importe-o no contexto. Se PublicKeyCredential for compatível, PublicKeyCredential.parseCreationOptionsFromJSON e PublicKeyCredential.prototype.toJSON também serão.

import 'webauthn-polyfills';