Credentials Management API - Terra Webmail
Este artigo tem como objetivo mostrar por que e como foi implementada a Credential Management, bem como os benefícios dessa API, usando como exemplo o login do novo Terra Webmail.
LET'S GO BABY
Sabemos que hoje em dia, por mais que pareça superficialmente simples realizar a implementação de um login, logo mais a frente pode se tornar um Frankenstein ou até mesmo acabar em um grau de complexidade alto. Muitas vezes, além de gerenciar o login da nossa aplicação, temos os logins de terceiros [Facebook, Twitter, GitHub, Google].
Como temos por costume não compartilhar senhas entre aplicações por motivos de segurança, acabamos tendo um problema para a memorizar todas elas - é nessa hora que entra o trabalho da Credential Management API. Olhando por cima, temos uma lista de:
Esses são os mais comuns, uma pessoa pode ter muito mais que isso para memorizar...
BENEFÍCIOS
facilita a escolha em caso de múltiplos usuários;
permite uso de interface nativa para múltiplos devices;
identifica o login que o usuário fez anteriormente na página, mesmo que o usuário não tenha autologin no browser, permitindo a ele se logar com apenas um tap/click.
possilita iteração com o browser já que ele autopreenche campos de login/senha atualmente;
oferece auto sign in entre diferentes dispositivos;
AUTO SIGN IN
Esse comportamento nos fez dizer "wow!"
Caso você esteja logado em sua no Google Chrome e faça login em outro dispositivo na mesma página com a mesma conta, o browser identificará que foi efetuado o login e, com isso, ocorrerá o auto sign in.
CREDENTIALS MANAGEMENT API E SEUS PILARES
A API tem como base três pilares:
salvar/gerenciar os dados da credencial;
permitir acesso com apenas um tap no seletor de contas;
simplificar o fluxo de acesso;
PASSO 1 - EFETUANDO LOGIN E ARMAZENANDO A CREDENCIAL
Aqui, vamos ver como foi implementada essa feature no novo Terra Webmail.
Temos nossa tela de login com um formulário que contém dois campos:
usuário
senha
var loginForm = document.querySelector('#signin'); loginForm.addEventListener('submit', function(e) { e.preventDefault(); validateLoginForm().then(function(res) { doLogin(res.user, res.pwd); }); });
O usuário irá digitar seu username/password para efetuar o login pela primeira vez e aí começará a mágica do Credentials.
Podemos ver que, quando o evento de envio do formulário for disparado, os valores do formulário de login serão validados. Caso esteja tudo OK, vai disparar a funcão doLogin, que será responsável por efetuar o login efetivo e salvar a credencial usando a Credentials Management API.
Um pouco antes de entrarmos no login, vamos validar se a feature está disponível no browser atual que estamos usando.
var cmapiAvailable = window.PasswordCredential && 'credentials' in navigator;
Agora que sabemos se está ou não disponível, podemos seguir nossa implementação.
function doLogin(user, pwd) { return authenticate(user, pwd).then(function(response) { // var cmapiAvailable = window.PasswordCredential && 'credentials' in navigator; if(cmapiAvailable) { var pwdCred = new window.PasswordCredential({ id: user, password: pwd }); navigator.credentials.store(pwdCred); } return response; }); };
Agora, iremos ver o que a função doLogin faz. Ela recebe dois parâmetros, que são os responsáveis por realizar a autenticação:
user - username
pwd - password
Após nos certificarmos de que temos a feature disponível para usá-la, iremos pegar os parâmetros user e pwd e criar um objeto JavaScript com os atributos id e password. Logo na próxima linha, passaremos esse objeto para uma nova instância de PasswordCredential.
A variável pwdCred armazenará um objeto de credencial, o qual será possível armazenar no browser.
Esse foi o primeiro passo: efetuamos o login e, logo em seguida, a credencial foi armazenada.
Pronto. Mais fácil do que se imaginava, certo?
PASSO 2 - VERIFICANDO CREDENCIAL SALVA E AUTO SIGN IN
function autoSignin() { // var cmapiAvailable = window.PasswordCredential && 'credentials' in navigator; if(!cmapiAvailable) { return; } navigator.credentials.get({ password: true, mediation: 'optional' }).then(function(cred) { if(cred) { doLogin(cred.id, cred.password); } }); };
A função autoSignin é disparada logo após o carregamento da página por completo. Agora, iremos ver como e quando buscaremos a credencial caso ela seja existente.
Como já vimos, iremos na primeira linha verificar a existência da API. Caso ela exista, "matamos" o fluxo.
Agora, sim, usaremos o método get do navigator.credentials em que temos alguns parâmetros para cuidar na config do objeto. Para entender melhor, temos uma demonstração logo abaixo:
O atributo password caso não seja true, não conseguirá pegar a credencial caso existente.
O mediation pode ter os seguintes valores:
silent: com uma ou mais pessoas, caso tenha credencial salva, tentará logar de forma silenciosa. Caso preventSilentAccess() seja disparado antes, irá ignorar a ação de tentar logar de forma silenciosa e automática.
optional: com uma pessoa, caso tenha credencial salva, tentará logar de forma silenciosa. Caso preventSilentAccess() seja disparado antes, irá ignorar a ação de tentar logar de forma automática e abrirá o seletor de contas.
required: sempre exibirá o seletor de contas caso já tenha memorizado alguma credencial.
POR QUE ESTAMOS USANDO OPTIONAL?
Vamos entender linha por linha.
Estamos dizendo que caso já tenha sido efetuada e salva a credencial de um login, e o usuário não tenha disparado preventSilentAccess(), ele irá tentar logar de forma automática e silenciosa.
Não havendo a credencial porque o usuário disparou o preventSilentAccess(), 'optional' respeita a opção do usuário e abrirá o seletor de contas caso um ou mais usernames já estejam salvos.
PASSO 3 - LOGOUT
var logoutBtn = document.querySelector('a.logout'); logoutBtn.addEventListener('click', logout);
A nossa função que efetuará o logout irá ocorrer após um clique no botão de sair.
function logout() { if(cmapiAvailable) { navigator.credentials.preventSilentAccess(); } fetch({ url: '/ws/signout', withCredentials: true }).then(function() { window.location = '/signout' + window.location.search; }); };
Optamos por permitir que o usuário tenha o poder de cancelar o auto sign in. Para isso, é necessário disparar a função preventSilentAccess(), que é a responsável por comunicar a API que, na próxima visita, não deve disparar o auto sign in.
CONFIGURAÇÕES - chrome://password-manager-internals/
Anteriormente, citei que o mediation: 'optional' respeita a escolha do usuário. Essa escolha ocorre na opção Auto Sign-in.
DEBUGGER - chrome://password-manager-internals/
É possível acompanhar o que está acontecendo com a API, ver quando fluxos de auto sign in e sign out ocorrem, qual formulário ele está interceptando, entre outras coisas.
DESVANTAGENS
Por enquanto, apenas o Chrome tem suporte para essa feature, tanto desktop quanto mobile.
Porém, a API w3c credential management já está em draft na W3C.
Em um futuro breve teremos uma implementação cross browser.