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.
- 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 umPromise
. - 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.
- 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.
- 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
.
- O navegador oferece suporte à IU condicional do
WebAuthn com
PublicKeyCredential.isConditionalMediationAvailable()
.
// 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
: retornaplatform
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.