Atualizações mais recentes na API de gerenciamento de credenciais

Algumas das atualizações descritas aqui são explicadas na sessão do Google I/O, Login seguro e perfeito: manter os usuários engajados:

Chrome 57

O Chrome 57 introduziu essa mudança importante na API Credential Management.

As credenciais podem ser compartilhadas de um subdomínio diferente

Agora o Chrome pode recuperar uma credencial armazenada em um subdomínio diferente usando a Credential Management API. Por exemplo, se uma senha for armazenada em login.example.com, um script em www.example.com poderá mostrá-la como um dos itens da conta na caixa de diálogo do seletor de contas.

É necessário armazenar a senha explicitamente usando navigator.credentials.store(), para que, quando um usuário escolher uma credencial tocando na caixa de diálogo, a senha seja transmitida e copiada para a origem atual.

Depois de armazenada, a senha fica disponível como uma credencial na mesma origem www.example.com.

Na captura de tela abaixo, as informações de credencial armazenadas em login.aliexpress.com são visíveis para m.aliexpress.com e disponíveis para o usuário escolher:

Seletor de contas mostrando os detalhes de login do subdomínio selecionado

Chrome 60

O Chrome 60 apresenta várias mudanças importantes na API Credential Management:

A detecção de recursos precisa de atenção

Para saber se a API Credential Management para acessar credenciais com base em senha e federadas está disponível, verifique se window.PasswordCredential ou window.FederatedCredential estão disponíveis.

if (window.PasswordCredential || window.FederatedCredential) {
  // The Credential Management API is available
}

O objeto PasswordCredential agora inclui a senha

A API Credential Management adotou uma abordagem conservadora para o processamento de senhas. Ele ocultava senhas do JavaScript, exigindo que os desenvolvedores enviassem o objeto PasswordCredential diretamente para o servidor para validação usando uma extensão da API fetch().

No entanto, essa abordagem introduziu várias restrições. Recebemos feedback de que os desenvolvedores não conseguiam usar a API porque:

  • Eles precisavam enviar a senha como parte de um objeto JSON.

  • Ele teve que enviar o valor de hash da senha para o servidor.

Depois de realizar uma análise de segurança e reconhecer que o ocultamento de senhas do JavaScript não impediu todos os vetores de ataque com a eficácia esperada, decidimos fazer uma mudança.

A API Credential Management agora inclui uma senha bruta em um objeto de credencial retornado para que você tenha acesso a ela como texto simples. Você pode usar métodos existentes para enviar informações de credencial ao servidor:

navigator.credentials.get({
    password: true,
    federated: {
    providers: [ 'https://accounts.google.com' ]
    },
    mediation: 'silent'
}).then(passwordCred => {
    if (passwordCred) {
    let form = new FormData();
    form.append('email', passwordCred.id);
    form.append('password', passwordCred.password);
    form.append('csrf_token', csrf_token);
    return fetch('/signin', {
        method: 'POST',
        credentials: 'include',
        body: form
    });
    } else {

    // Fallback to sign-in form
    }
}).then(res => {
    if (res.status === 200) {
    return res.json();
    } else {
    throw 'Auth failed';
    }
}).then(profile => {
    console.log('Auth succeeded', profile);
});

A busca personalizada será descontinuada em breve

Para determinar se você está usando uma função fetch() personalizada, verifique se ela usa um objeto PasswordCredential ou FederatedCredential como um valor da propriedade credentials, por exemplo:

fetch('/signin', {
    method: 'POST',
    credentials: c
})

É recomendável usar uma função fetch() regular, conforme mostrado no exemplo de código anterior, ou usar um XMLHttpRequest.

Até o Chrome 60, navigator.credentials.get() aceitava uma propriedade unmediated opcional com uma flag booleana. Exemplo:

navigator.credentials.get({
    password: true,
    federated: {
    providers: [ 'https://accounts.google.com' ]
    },
    unmediated: true
}).then(c => {

    // Sign-in
});

A configuração unmediated: true impede que o navegador mostre o seletor de conta ao transmitir uma credencial.

A flag agora é estendida como mediação. A mediação do usuário pode acontecer quando:

  • O usuário precisa escolher uma conta para fazer login.

  • Um usuário quer fazer login explicitamente após a chamada navigator.credentials.requireUseMediation().

Escolha uma das seguintes opções para o valor mediation:

Valor mediation Comparado com a flag booleana Comportamento
silent Igual a unmediated: true A credencial foi transmitida sem mostrar um seletor de contas.
optional Igual a unmediated: false Mostra um seletor de contas se preventSilentAccess() for chamado anteriormente.
required Uma nova opção Sempre mostrar um seletor de contas. Útil quando você quer permitir que um usuário troque de conta usando a caixa de diálogo nativa do seletor de contas.

Neste exemplo, a credencial é transmitida sem mostrar um seletor de conta, o equivalente da flag anterior, unmediated: true:

navigator.credentials.get({
    password: true,
    federated: {
    providers: [ 'https://accounts.google.com' ]
    },
    mediation: 'silent'
}).then(c => {

    // Sign-in
});

requireUserMediation() foi renomeado como preventSilentAccess().

Para se alinhar à nova propriedade mediation oferecida na chamada get(), o método navigator.credentials.requireUserMediation() foi renomeado como navigator.credentials.preventSilentAccess().

O método renomeado impede a transmissão de uma credencial sem mostrar o seletor de conta (às vezes chamado de mediação do usuário). Isso é útil quando um usuário faz logout de um site ou cancela o registro e não quer fazer login automaticamente na próxima visita.

signoutUser();
if (navigator.credentials) {
    navigator.credentials.preventSilentAccess();
}

Criar objetos de credencial de forma assíncrona com o novo método navigator.credentials.create()

Agora você tem a opção de criar objetos de credencial de forma assíncrona com o novo método, navigator.credentials.create(). Confira a seguir uma comparação entre as abordagens síncrona e assíncrona.

Como criar um objeto PasswordCredential

Abordagem de sincronização
let c = new PasswordCredential(form);
Abordagem assíncrona (nova)
let c = await navigator.credentials.create({
    password: form
});

ou

let c = await navigator.credentials.create({
    password: {
    id: id,
    password: password
    }
});

Como criar um objeto FederatedCredential

Abordagem de sincronização
let c = new FederatedCredential({
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
});
Abordagem assíncrona (nova)
let c = await navigator.credentials.create({
    federated: {
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
    }
});

Guia de migração

Já tem uma implementação da API Credential Management? Temos um documento de guia de migração que você pode seguir para fazer upgrade para a nova versão.