Embora as credenciais FIDO, como as chaves de acesso, tenham como objetivo substituir as senhas, a maioria delas também pode liberar o usuário da necessidade de digitar um nome de usuário. Isso permite que os usuários se autentiquem selecionando uma conta em uma lista de chaves de acesso que eles têm para o site atual.
As versões anteriores de chaves de segurança foram projetadas como métodos de autenticação em duas etapas e exigiam os IDs de possíveis credenciais, o que exigia a entrada de um nome de usuário. As credenciais que uma chave de segurança pode encontrar sem saber os IDs são chamadas de credenciais detectáveis. A maioria das credenciais FIDO criadas hoje são detectáveis, principalmente as chaves de acesso armazenadas em um gerenciador de senhas ou em uma chave de segurança moderna.
Para garantir que suas credenciais sejam criadas como chaves de acesso (credenciais detectáveis), especifique residentKey e requireResidentKey ao criar a credencial.
As partes confiáveis (RPs, na sigla em inglês) podem usar credenciais detectáveis omitindo
allowCredentials durante a autenticação com chave de acesso. Nesses casos, o navegador ou o sistema mostra ao usuário uma lista de chaves de acesso disponíveis, identificadas pela propriedade user.name definida no momento da criação. Se o usuário selecionar uma, o valor user.id será incluído na assinatura resultante. O servidor pode usar esse ID ou o ID da credencial retornada para procurar a conta em vez de um nome de usuário digitado.
As UIs do seletor de contas, como as discutidas anteriormente, nunca mostram credenciais não detectáveis.
requireResidentKey e residentKey
Para criar uma chave de acesso, especifique authenticatorSelection.residentKey e authenticatorSelection.requireResidentKey em navigator.credentials.create() com os valores indicados abaixo.
async function register () {
// ...
const publicKeyCredentialCreationOptions = {
// ...
authenticatorSelection: {
authenticatorAttachment: 'platform',
residentKey: 'required',
requireResidentKey: true,
}
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
// This does not run until the user selects a passkey.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// ...
}
residentKey:
'required': uma credencial detectável precisa ser criada. Se não for possível criar,NotSupportedErrorserá retornado.'preferred': o RP prefere criar uma credencial detectável, mas aceita uma não detectável.'discouraged': o RP prefere criar uma credencial não detectável, mas aceita uma detectável.
requireResidentKey:
- Essa propriedade é mantida para compatibilidade com versões anteriores do WebAuthn Nível 1, uma versão mais antiga da especificação. Defina como
trueseresidentKeyfor'required'. Caso contrário, defina comofalse.
allowCredentials
Os RPs podem usar allowCredentials em navigator.credentials.get() para controlar a experiência de autenticação com chave de acesso. Geralmente, há três tipos de experiências de autenticação por chaves de acesso:
- Mostrar um seletor de conta modal
- Mostrar um preenchimento automático de formulário com chave de acesso
- Reautenticação
Mostrar um seletor de conta modal
Com as credenciais detectáveis, os RPs podem mostrar um seletor de contas modal para o usuário escolher uma conta para fazer login, seguido da verificação do usuário. Isso é adequado para o fluxo de autenticação de chaves de acesso iniciado ao pressionar um botão dedicado a esse tipo de autenticação.
Para alcançar essa experiência do usuário, omita ou transmita uma matriz vazia ao parâmetro allowCredentials em navigator.credentials.get().
async function authenticate() {
// ...
const publicKeyCredentialRequestOptions = {
// Server generated challenge:
challenge: ****,
// The same RP ID as used during registration:
rpId: 'example.com',
// You can omit `allowCredentials` as well:
allowCredentials: []
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal
});
// This does not run until the user selects a passkey.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// ...
}
Mostrar o preenchimento automático de formulários com uma chave de acesso
O seletor de contas modal descrito acima funciona bem se a maioria dos usuários usa chaves de acesso e as tem disponíveis no dispositivo local. Para um usuário que não tem chaves de acesso locais, a caixa de diálogo modal ainda aparece e oferece a opção de apresentar uma chave de acesso de outro dispositivo. Ao fazer a transição dos usuários para chaves de acesso, talvez você queira evitar essa interface para quem ainda não configurou uma.
Em vez disso, a seleção de uma chave de acesso pode ser incluída nos avisos de preenchimento automático dos campos em um formulário de login tradicional, junto com nomes de usuário e senhas salvas. Assim, um usuário com chaves de acesso pode "preencher" o formulário de login selecionando a chave de acesso. Usuários com pares de nome de usuário/senha salvos podem selecionar esses dados, e usuários sem nenhum dos dois ainda podem digitar o nome de usuário e a senha.
Essa experiência do usuário é ideal quando o RP está em uma migração com uso misto de senhas e chaves de acesso.
Para alcançar essa experiência do usuário, além de transmitir uma matriz vazia para a propriedade allowCredentials ou omitir o parâmetro, especifique mediation: 'conditional' em navigator.credentials.get() e anote um campo de entrada HTML username com autocomplete="username webauthn" ou um campo de entrada password com autocomplete="password webauthn".
A chamada para navigator.credentials.get() não vai mostrar nenhuma interface, mas, se o usuário focar o campo de entrada anotado, todas as chaves de acesso disponíveis serão incluídas nas opções de preenchimento automático. Se o usuário selecionar uma opção, ele passará pela verificação normal de desbloqueio do dispositivo. Só então a promessa retornada por .get() será resolvida com um resultado. Se o usuário não selecionar uma chave de acesso, a promessa nunca será resolvida.
async function authenticate() {
// ...
const publicKeyCredentialRequestOptions = {
// Server generated challenge:
challenge: ****,
// The same RP ID as used during registration:
rpId: 'example.com',
// You can omit `allowCredentials` as well:
allowCredentials: []
};
const cred = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal,
// Specify 'conditional' to activate conditional UI
mediation: 'conditional'
});
// This does not run until the user selects a passkey.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// ...
}
<input type="text" name="username" autocomplete="username webauthn" ...>
Você pode aprender a criar essa experiência do usuário em Fazer login com uma chave de acesso usando o preenchimento automático de formulários e no codelab Implementar chaves de acesso com o preenchimento automático de formulário em um app da Web.
Reautenticação
Em alguns casos, como ao usar chaves de acesso para reautenticação, o identificador do usuário já é conhecido. Nesse caso, queremos usar uma chave de acesso sem que o navegador ou o SO mostre qualquer tipo de seletor de conta. Para isso, transmita uma lista de IDs de credenciais no parâmetro allowCredentials.
Nesse caso, se alguma das credenciais nomeadas estiver disponível localmente, o usuário vai precisar do desbloqueio do dispositivo imediatamente. Caso contrário, o usuário vai precisar apresentar outro dispositivo (um smartphone ou uma chave de segurança) com uma credencial válida.
Para oferecer essa experiência do usuário, forneça uma lista de IDs de credenciais para o usuário que está fazendo login. O RP precisa conseguir consultá-los porque o usuário já é conhecido. Forneça IDs de credenciais como objetos PublicKeyCredentialDescriptor na propriedade allowCredentials em navigator.credentials.get().
async function authenticate() {
// ...
const publicKeyCredentialRequestOptions = {
// Server generated challenge:
challenge: ****,
// The same RP ID as used during registration:
rpId: 'example.com',
// Provide a list of PublicKeyCredentialDescriptors:
allowCredentials: [{
id: ****,
type: 'public-key',
transports: [
'internal',
'hybrid'
]
}, {
id: ****,
type: 'public-key',
transports: [
'internal',
'hybrid'
]
}, ...]
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal
});
// This does not run until the user selects a passkey.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// ...
}
Um objeto PublicKeyCredentialDescriptor consiste em:
id: um ID da credencial de chave pública que o RP recebeu no registro da chave de acesso.type: geralmente, esse campo é'public-key'.transports: uma dica de transportes compatíveis com o dispositivo que contém essa credencial, usada pelos navegadores para otimizar a interface que pede ao usuário para apresentar um dispositivo externo. Se fornecida, essa lista precisa conter o resultado da chamada degetTransports()durante o registro de cada credencial.
Resumo
As credenciais detectáveis tornam a experiência de login com chaves de acesso muito mais fácil para o usuário, permitindo que ele pule a etapa de inserir um nome de usuário. Com a combinação de residentKey, requireResidentKey e allowCredentials, os RPs podem oferecer experiências de login que:
- Mostra um seletor de contas modal.
- Mostrar um preenchimento automático de formulário com chave de acesso.
- Reautenticação.
Use as credenciais detectáveis com sabedoria. Assim, você pode criar experiências de login com chaves de acesso sofisticadas que os usuários vão achar perfeitas e com as quais terão mais chances de interagir.