Fazer login com uma chave de acesso usando o preenchimento automático de formulários

Crie uma experiência de login que aproveite as chaves de acesso e ainda acomode os usuários de senhas.

As chaves de acesso substituem as senhas e tornam as contas de usuários na Web mais seguras, simples e fáceis de usar. No entanto, a transição da autenticação baseada em senha para a chave de acesso pode complicar a experiência do usuário. Usar o preenchimento automático de formulários para sugerir chaves de acesso pode ajudar a criar uma experiência unificada.

Por que usar o preenchimento automático de formulários para fazer login com uma chave de acesso?

Com uma chave de acesso, o usuário pode fazer login em um site usando apenas impressão digital, rosto ou PIN do dispositivo.

O ideal seria que não houvesse usuários de senha e que o fluxo de autenticação fosse tão simples quanto um único botão de login. Quando o usuário toca no botão, uma caixa de diálogo do seletor de conta aparece. O usuário pode escolher uma conta, desbloquear a tela para verificar e fazer login.

No entanto, a transição de senha para autenticação baseada em chave de acesso pode ser difícil. À medida que os usuários passarem a usar chaves de acesso, ainda haverá aqueles que usam senhas, e os sites precisarão acomodar os dois tipos de usuários. Não é esperado que os usuários se lembrem de quais sites eles mudaram para chaves de acesso. Portanto, pedir que os usuários selecionem qual método usar de antemão seria uma experiência de usuário ruim.

As chaves de acesso também são uma nova tecnologia. Explicar e garantir que os usuários se sintam confortáveis ao usá-las pode ser um desafio para os sites. Podemos usar experiências de usuário conhecidas para preencher senhas automaticamente e resolver os dois problemas.

Interface condicional

Para criar uma experiência do usuário eficiente para usuários de chaves de acesso e senhas, é possível incluir chaves de acesso nas sugestões de preenchimento automático. Isso é chamado de interface condicional e faz parte do padrão WebAuthn.

Assim que o usuário toca no campo de entrada de nome de usuário, uma caixa de diálogo de sugestão de preenchimento automático aparece, destacando as chaves de acesso armazenadas e as sugestões de preenchimento automático de senha. O usuário pode escolher uma conta e usar o bloqueio de tela do dispositivo para fazer login.

Dessa forma, os usuários podem fazer login no seu site com o formulário atual como se nada tivesse mudado, mas com o benefício adicional de segurança das chaves de acesso se tiverem uma.

Como funciona

Para autenticar com uma chave de acesso, use a API WebAuthn.

Os quatro componentes de um fluxo de autenticação de chave de acesso são: o usuário:

  • Back-end: o servidor de back-end que contém o banco de dados de contas que armazena a chave pública e outros metadados sobre a chave de acesso.
  • Frontend: o front-end que se comunica com o navegador e envia solicitações de busca para o back-end.
  • Navegador: o navegador do usuário que está executando o JavaScript.
  • Authenticator: o autenticador do usuário que cria e armazena a chave de acesso. Isso pode acontecer no mesmo dispositivo do navegador (por exemplo, ao usar o Windows Hello) ou em outro dispositivo, como um smartphone.
Diagrama de autenticação da chave de acesso
  1. Assim que um usuário acessa o front-end, ele solicita um desafio do back-end para autenticar com uma chave de acesso e chama navigator.credentials.get() para iniciar a autenticação com uma chave de acesso. Isso retorna um Promise.
  2. Quando o usuário coloca o cursor no campo de login, o navegador mostra uma caixa de diálogo de preenchimento automático de senhas, incluindo chaves de acesso. Uma caixa de diálogo de autenticação vai aparecer se o usuário selecionar uma chave de acesso.
  3. Depois que o usuário verifica a identidade usando o bloqueio de tela do dispositivo, a promessa é resolvida e uma credencial de chave pública é retornada ao front-end.
  4. O front-end envia a credencial de chave pública para o back-end. O back-end verifica a assinatura com a chave pública da conta correspondente no banco de dados. Se for bem-sucedido, o usuário vai fazer login.

Autenticar com uma chave de acesso usando o preenchimento automático de formulários

Quando um usuário quiser fazer login, você poderá fazer uma chamada get condicional da WebAuthn para indicar que as chaves de acesso podem ser incluídas nas sugestões de preenchimento automático. Uma chamada condicional para a API navigator.credentials.get() da WebAuthn não mostra a interface e permanece pendente até que o usuário escolha uma conta para fazer login nas sugestões de preenchimento automático. Se o usuário escolher uma chave de acesso, o navegador vai resolver a promessa com uma credencial em vez de preencher o formulário de login. A página é responsável por fazer o login do usuário.

Fazer anotações no campo de entrada do formulário

Adicione um atributo autocomplete ao campo input do nome de usuário, se necessário. Anexe username e webauthn como tokens para sugerir chaves de acesso.

<input type="text" name="username" autocomplete="username webauthn" ...>

Detecção de recursos

Antes de invocar uma chamada condicional da API WebAuthn, verifique se:

  • O navegador oferece suporte para WebAuthn com PublicKeyCredential.

Compatibilidade com navegadores

  • Chrome: 67.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Origem

Compatibilidade com navegadores

  • Chrome: 108.
  • Edge: 108.
  • Firefox: 119.
  • Safari: 16.

Origem

// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.​​isConditionalMediationAvailable) {  
  // Check if conditional mediation is available.  
  const isCMA = await PublicKeyCredential.​​isConditionalMediationAvailable();  
  if (isCMA) {  
    // Call WebAuthn authentication  
  }  
}  

Buscar um desafio do servidor RP

Extrair um desafio do servidor RP que é necessário para chamar navigator.credentials.get():

  • challenge: um desafio gerado pelo servidor em um ArrayBuffer. Isso é necessário para evitar ataques de repetição. Gere um novo desafio em cada tentativa de login e descarte-o após um determinado período ou depois que uma tentativa de login não seja validada. Considere-o como um token CSRF.
  • allowCredentials: uma matriz de credenciais aceitáveis para essa autenticação. Transmita uma matriz vazia para permitir que o usuário selecione uma chave de acesso disponível em uma lista mostrada pelo navegador.
  • userVerification: indica se a verificação do usuário usando o bloqueio de tela do dispositivo é "required", "preferred" ou "discouraged". O padrão é "preferred", o que significa que o autenticador pode pular a verificação do usuário. Defina como "preferred" ou omita a propriedade.

Chamar a API WebAuthn com a flag conditional para autenticar o usuário

Chame navigator.credentials.get() para começar a aguardar a autenticação do usuário.

// To abort a WebAuthn call, instantiate an `AbortController`.
const abortController = new AbortController();

const publicKeyCredentialRequestOptions = {
  // Server generated challenge
  challenge: ****,
  // The same RP ID as used during registration
  rpId: 'example.com',
};

const credential = await navigator.credentials.get({
  publicKey: publicKeyCredentialRequestOptions,
  signal: abortController.signal,
  // Specify 'conditional' to activate conditional UI
  mediation: 'conditional'
});
  • rpId: um ID de RP é um domínio, e um site pode especificar o próprio domínio ou um sufixo registrável. Esse valor precisa corresponder ao rp.id usado quando a chave de acesso foi criada.

Especifique mediation: 'conditional' para tornar a solicitação condicional.

Enviar a credencial de chave pública retornada para o servidor RP

Depois que o usuário seleciona uma conta e consente usando o bloqueio de tela do dispositivo, a promessa é resolvida, retornando um objeto PublicKeyCredential para o front-end do RP.

Uma promessa pode ser rejeitada por vários motivos. Você precisa processar os erros de acordo com a propriedade name do objeto Error:

  • NotAllowedError: o usuário cancelou a operação.
  • Outras exceções: algo inesperado aconteceu. O navegador mostra uma caixa de diálogo de erro para o usuário.

O objeto de credencial de chave pública contém as seguintes propriedades:

  • id: o ID codificado em base64url da credencial da chave de acesso autenticada.
  • rawId: uma versão ArrayBuffer do ID da credencial.
  • response.clientDataJSON: um ArrayBuffer de dados do cliente. Esse campo contém informações, como o desafio e a origem que o servidor da RP precisa verificar.
  • response.authenticatorData: um ArrayBuffer de dados do autenticador. Esse campo contém informações como o ID da RP.
  • response.signature: um ArrayBuffer da assinatura. Esse valor é o núcleo da credencial e precisa ser verificado no servidor.
  • response.userHandle: um ArrayBuffer que continha o ID do usuário definido no momento da criação. Esse valor pode ser usado em vez do ID da credencial se o servidor precisar escolher os valores de ID que ele usa ou se o back-end quiser evitar a criação de um índice nos IDs das credenciais.
  • authenticatorAttachment: retorna platform quando a credencial veio do dispositivo local. Caso contrário, cross-platform, especialmente quando o usuário usou um telefone para fazer login. Se o usuário precisar usar um smartphone para fazer login, peça que ele crie uma chave de acesso no dispositivo local.
  • type: esse campo é sempre definido como "public-key".

Se você usar uma biblioteca para processar o objeto de credencial de chave pública no servidor RP, recomendamos enviar o objeto inteiro para o servidor depois de codificar parcialmente com base64url.

Verificar a assinatura

Quando você receber a credencial de chave pública no servidor, transmita-a à biblioteca FIDO para processar o objeto.

Procure o ID de credencial correspondente com a propriedade id. Se você precisar determinar a conta de usuário, use a propriedade userHandle, que é o user.id especificado ao criar a credencial. Confira se a signature da credencial pode ser verificada com a chave pública armazenada. Para isso, recomendamos usar uma biblioteca do lado do servidor ou uma solução em vez de escrever seu próprio código. Você pode encontrar bibliotecas de código aberto no repositório awesome-webauth do GitHub.

Depois que a credencial for verificada com uma chave pública correspondente, faça login do usuário.

Siga instruções mais detalhadas em Autenticação de chave de acesso do servidor.

Recursos