Actualizaciones más recientes de la API de administración de credenciales

Algunas de las actualizaciones que se describen aquí se explican en la sesión de Google I/O, Acceso seguro y sin problemas: Mantén a los usuarios comprometidos:

Chrome 57

Chrome 57 introdujo este cambio importante en la API de Credential Management.

Las credenciales se pueden compartir desde un subdominio diferente

Chrome ahora puede recuperar una credencial almacenada en un subdominio diferente con la API de Credential Management. Por ejemplo, si una contraseña se almacena en login.example.com, una secuencia de comandos en www.example.com puede mostrarla como uno de los elementos de la cuenta en el diálogo del selector de cuentas.

Debes almacenar la contraseña de forma explícita con navigator.credentials.store(), de modo que, cuando un usuario elija una credencial presionando el diálogo, la contraseña se pase y se copie en el origen actual.

Una vez que se almacena, la contraseña está disponible como credencial en el mismo origen www.example.com en adelante.

En la siguiente captura de pantalla, la información de credenciales almacenada en login.aliexpress.com es visible para m.aliexpress.com y está disponible para que el usuario la elija:

Selector de cuentas que muestra los detalles de acceso del subdominio seleccionado

Chrome 60

Chrome 60 presenta varios cambios importantes en la API de Credential Management:

La detección de características requiere atención

Para ver si la API de Credential Management para acceder a credenciales federadas ybasadas en contraseñas está disponible, verifica si window.PasswordCredential o window.FederatedCredential están disponibles.

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

El objeto PasswordCredential ahora incluye la contraseña

La API de Credential Management adoptó un enfoque conservador para controlar las contraseñas. Ocultaba las contraseñas de JavaScript, lo que requería que los desarrolladores enviaran el objeto PasswordCredential directamente a su servidor para su validación a través de una extensión de la API de fetch().

Sin embargo, este enfoque introdujo varias restricciones. Recibimos comentarios de que los desarrolladores no podían usar la API por los siguientes motivos:

  • Tuvieron que enviar la contraseña como parte de un objeto JSON.

  • Tuvieron que enviar el valor hash de la contraseña a su servidor.

Después de realizar un análisis de seguridad y reconocer que ocultar las contraseñas de JavaScript no impidió todos los vectores de ataque con la eficacia que esperábamos, decidimos hacer un cambio.

La API de Credential Management ahora incluye una contraseña sin procesar en un objeto de credencial que se muestra para que tengas acceso a ella como texto sin formato. Puedes usar métodos existentes para enviar información de credenciales a tu 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);
});

La recuperación personalizada dejará de estar disponible pronto

Para determinar si usas una función fetch() personalizada, verifica si usa un objeto PasswordCredential o un objeto FederatedCredential como valor de la propiedad credentials, por ejemplo:

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

Se recomienda usar una función fetch() normal, como se muestra en el ejemplo de código anterior, o usar un XMLHttpRequest.

Hasta Chrome 60, navigator.credentials.get() aceptaba una propiedad unmediated opcional con una marca booleana. Por ejemplo:

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

    // Sign-in
});

La configuración de unmediated: true evita que el navegador muestre el selector de cuentas cuando se pasa una credencial.

Ahora, la marca se extiende como mediación. La mediación del usuario puede ocurrir en los siguientes casos:

  • El usuario debe elegir una cuenta para acceder.

  • Un usuario quiere acceder de forma explícita después de la llamada a navigator.credentials.requireUseMediation().

Elige una de las siguientes opciones para el valor mediation:

Valor mediation Comparación con la marca booleana Comportamiento
silent Es igual a unmediated: true Se pasó la credencial sin mostrar un selector de cuentas.
optional Es igual a unmediated: false Muestra un selector de cuentas si se llamó a preventSilentAccess() anteriormente.
required Una nueva opción Mostrar siempre un selector de cuentas Es útil cuando quieres permitir que un usuario cambie de cuenta con el diálogo del selector de cuentas nativo.

En este ejemplo, se pasa la credencial sin mostrar un selector de cuentas, el equivalente de la marca anterior, unmediated: true:

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

    // Sign-in
});

Se cambió el nombre de requireUserMediation() por preventSilentAccess()

Para alinearse de forma correcta con la nueva propiedad mediation que se ofrece en la llamada get(), se cambió el nombre del método navigator.credentials.requireUserMediation() a navigator.credentials.preventSilentAccess().

El método con nombre cambiado evita pasar una credencial sin mostrar el selector de cuentas (a veces llamado sin mediación del usuario). Esto es útil cuando un usuario cierra la sesión en un sitio web o se da de baja de uno y no quiere volver a acceder automáticamente en la próxima visita.

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

Crea objetos de credencial de forma asíncrona con el nuevo método navigator.credentials.create()

Ahora tienes la opción de crear objetos de credenciales de forma asíncrona con el nuevo método, navigator.credentials.create(). Sigue leyendo para ver una comparación entre los enfoques síncronos y asíncronos.

Cómo crear un objeto PasswordCredential

Enfoque de sincronización
let c = new PasswordCredential(form);
Enfoque asíncrono (nuevo)
let c = await navigator.credentials.create({
    password: form
});

o:

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

Cómo crear un objeto FederatedCredential

Enfoque de sincronización
let c = new FederatedCredential({
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
});
Enfoque asíncrono (nuevo)
let c = await navigator.credentials.create({
    federated: {
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
    }
});

Guía de migración

¿Tienes una implementación existente de la API de Credential Management? Tenemos un documento de la guía de migración que puedes seguir para actualizar a la nueva versión.