Dernières mises à jour de l'API de gestion des identifiants

Certaines des mises à jour décrites ici sont expliquées dans la session Google I/O intitulée Secure and fluide Sign-In: Keeping Users Engaged (Connexion sécurisée et fluide:

Chrome 57

Chrome 57 a introduit cette modification importante dans l'API Credential Management.

Les identifiants peuvent être partagés à partir d'un autre sous-domaine

Chrome peut désormais récupérer un identifiant stocké dans un autre sous-domaine à l'aide de l'API Credential Management. Par exemple, si un mot de passe est stocké dans login.example.com, un script sur www.example.com peut l'afficher comme l'un des éléments de compte dans la boîte de dialogue de sélection du compte.

Vous devez stocker explicitement le mot de passe à l'aide de navigator.credentials.store(). Ainsi, lorsqu'un utilisateur choisit un identifiant en appuyant sur la boîte de dialogue, le mot de passe est transmis et copié à l'origine actuelle.

Une fois stocké, le mot de passe est disponible en tant qu'identifiant dans la même origine www.example.com et au-delà.

Dans la capture d'écran suivante, les informations d'identification stockées sous login.aliexpress.com sont visibles par m.aliexpress.com et peuvent être sélectionnées par l'utilisateur:

Sélecteur de compte affichant les informations de connexion au sous-domaine sélectionné

Chrome 60

Chrome 60 apporte plusieurs modifications importantes à l'API Gestion des identifiants:

La détection de fonctionnalités nécessite votre attention

Pour savoir si l'API Gestion des identifiants pour accéder aux identifiants basés sur un mot de passe et fédérés est disponible, vérifiez si window.PasswordCredential ou window.FederatedCredential est disponible.

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

L'objet PasswordCredential inclut désormais le mot de passe

L'API Gestion des identifiants a adopté une approche conservatrice pour la gestion des mots de passe. Il cachait les mots de passe à JavaScript, obligeant les développeurs à envoyer l'objet PasswordCredential directement à leur serveur pour validation via une extension de l'API fetch().

Toutefois, cette approche a introduit un certain nombre de restrictions. Nous avons reçu des commentaires indiquant que les développeurs ne pouvaient pas utiliser l'API pour les raisons suivantes:

  • Il devait envoyer le mot de passe dans un objet JSON.

  • Il a dû envoyer la valeur de hachage du mot de passe à son serveur.

Après avoir effectué une analyse de sécurité et constaté que le masquage des mots de passe dans JavaScript n'empêchait pas tous les vecteurs d'attaque aussi efficacement que nous l'espérions, nous avons décidé d'apporter un changement.

L'API Gestionnaire d'identifiants inclut désormais un mot de passe brut dans un objet d'identifiants renvoyé afin que vous y ayez accès en texte brut. Vous pouvez utiliser des méthodes existantes pour transmettre des informations d'identification à votre serveur:

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 récupération personnalisée sera bientôt abandonnée

Pour déterminer si vous utilisez une fonction fetch() personnalisée, vérifiez si elle utilise un objet PasswordCredential ou un objet FederatedCredential comme valeur de la propriété credentials, par exemple:

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

Il est recommandé d'utiliser une fonction fetch() standard comme indiqué dans l'exemple de code précédent ou un XMLHttpRequest.

Jusqu'à Chrome 60, navigator.credentials.get() acceptait une propriété unmediated facultative avec un indicateur booléen. Exemple :

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

    // Sign-in
});

Le paramètre unmediated: true empêche le navigateur d'afficher le sélecteur de compte lors de la transmission d'identifiants.

L'indicateur est désormais étendu en tant que médiation. La médiation de l'utilisateur peut se produire dans les cas suivants:

  • L'utilisateur doit choisir un compte avec lequel se connecter.

  • Un utilisateur souhaite se connecter explicitement après l'appel navigator.credentials.requireUseMediation().

Choisissez l'une des options suivantes pour la valeur mediation:

Valeur mediation Par rapport à l'option booléenne Comportement
silent Est égal à unmediated: true Identifiants transmis sans afficher de sélecteur de compte.
optional Est égal à unmediated: false Affiche un sélecteur de compte si preventSilentAccess() a été appelé précédemment.
required Une nouvelle option Toujours afficher un sélecteur de compte. Utile lorsque vous souhaitez autoriser un utilisateur à changer de compte à l'aide de la boîte de dialogue de sélection de compte native.

Dans cet exemple, les identifiants sont transmis sans afficher de sélecteur de compte, l'équivalent de l'indicateur précédent, unmediated: true:

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

    // Sign-in
});

Remplacement du nom requireUserMediation() par preventSilentAccess().

Pour s'aligner correctement sur la nouvelle propriété mediation proposée dans l'appel get(), la méthode navigator.credentials.requireUserMediation() a été renommée navigator.credentials.preventSilentAccess().

La méthode renommée empêche de transmettre des identifiants sans afficher le sélecteur de compte (parfois appelé "sans médiation de l'utilisateur"). Cette option est utile lorsqu'un utilisateur se déconnecte d'un site Web ou s'en désinscrit et ne souhaite pas être reconnecté automatiquement lors de sa prochaine visite.

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

Créer des objets d'identification de manière asynchrone avec la nouvelle méthode navigator.credentials.create()

Vous pouvez désormais créer des objets d'identifiants de manière asynchrone avec la nouvelle méthode navigator.credentials.create(). Lisez la suite pour comparer les approches synchrones et asynchrones.

Créer un objet PasswordCredential

Approche de synchronisation
let c = new PasswordCredential(form);
Approche asynchrone (nouvelle)
let c = await navigator.credentials.create({
    password: form
});

ou :

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

Créer un objet FederatedCredential

Approche de synchronisation
let c = new FederatedCredential({
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
});
Approche asynchrone (nouvelle)
let c = await navigator.credentials.create({
    federated: {
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
    }
});

Guide de migration

Avez-vous déjà implémenté l'API Gestion des identifiants ? Un guide de migration est à votre disposition pour vous aider à passer à la nouvelle version.