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

Crie uma experiência de login que utilize chaves de acesso e, ao mesmo tempo, acomode usuários com senhas.

As chaves de acesso substituem as senhas e tornam as contas de usuário na Web mais seguras, simples e fáceis de usar. No entanto, a transição da autenticação baseada em senha para a baseada em chaves 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ário para fazer login com uma chave de acesso?

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

O ideal é que não haja usuários com senha, e o fluxo de autenticação poderia ser tão simples quanto um único botão de login. Quando o usuário toca nesse botão, uma caixa de diálogo de seletor de conta é exibida. Ele pode escolher uma conta, desbloquear a tela para verificar e fazer login.

No entanto, a transição da senha para a autenticação baseada em chave de acesso pode ser desafiadora. À medida que os usuários passam a usar chaves de acesso, ainda há usuários que usam senhas, e os sites precisam atender aos dois tipos de usuário. Os próprios usuários não devem se lembrar em quais sites mudaram para chaves de acesso. Portanto, pedir para os usuários selecionarem qual método usar antecipadamente seria uma experiência de UX insatisfatória.

As chaves de acesso também são uma nova tecnologia. Explicar e garantir que os usuários se sintam à vontade para usá-los pode ser um desafio para sites. Podemos confiar em experiências conhecidas do usuário para o preenchimento automático de senhas 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, você pode 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 do nome de usuário, uma caixa de diálogo de sugestão de preenchimento automático é mostrada, destacando as chaves de acesso armazenadas com 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 de segurança extra das chaves de acesso, se tiverem uma.

Como funciona

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

Os quatro componentes em 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.
  • Front-end: seu 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.
  • Autenticador: o autenticador do usuário que cria e armazena a chave de acesso. Ele pode estar no mesmo dispositivo que o 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 chega ao 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 senha, 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 em relação à chave pública da conta correspondente no banco de dados. Se ela for bem-sucedida, o usuário terá feito login.

Pré-requisitos

A interface condicional da WebAuthn tem suporte público no Safari no iOS 16, iPadOS 16 e macOS Ventura. Ela também está disponível no Chrome para Android, macOS e Windows 11 22H2.

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 condicional get 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() do 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 é responsabilidade fazer o login do usuário.

Anotar o 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 permitir que ele sugira 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 é compatível com WebAuthn.
  • O navegador é compatível com a interface condicional do WebAuthn.
// 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 no servidor da RP

Busque um desafio do servidor RP que precise 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 desconsidere-o após um determinado período ou depois que uma tentativa de login falhar na validação. 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 de 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 da RP é um domínio, e um site pode especificar o domínio dele ou um sufixo que pode ser registrado. Esse valor precisa corresponder ao rp.id usado quando a chave de acesso foi criada.

Lembre-se de especificar mediation: 'conditional' para tornar a solicitação condicional.

Envia a credencial de chave pública retornada ao servidor RP

Depois que o usuário selecionar uma conta e consentir com o bloqueio de tela do dispositivo, a promessa será resolvida retornando um objeto PublicKeyCredential ao front-end da RP.

Uma promessa pode ser rejeitada por vários motivos diferentes. Você precisa lidar com os erros de acordo, dependendo da 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 ao 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 precisará 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 poderá ser usado, no lugar do ID da credencial, se o servidor precisar escolher os valores de ID que usa ou se o back-end quiser evitar a criação de um índice em IDs de credenciais.
  • authenticatorAttachment: retorna platform quando essa credencial veio do dispositivo local. Caso contrário, cross-platform, especialmente quando o usuário usou um smartphone para fazer login. Se o usuário precisar usar um smartphone para fazer login, solicite que ele crie uma chave de acesso no dispositivo local.
  • type: este 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 codificá-lo parcialmente com base64url.

Verificar a assinatura

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

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

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

Recursos