Najnowsze aktualizacje interfejsu API do zarządzania danymi logowania

Niektóre z opisanych tu aktualizacji zostały omówione w sesji Google I/O: Bezpieczne i bezproblemowe logowanie: utrzymanie zaangażowania użytkowników:

Chrome 57

Ta ważna zmiana została wprowadzona w Chrome 57 w interfejsie Credential Management API.

Dane logowania mogą być udostępniane z innej subdomeny

Chrome może teraz pobrać dane logowania przechowywane w innej subdomenie za pomocą interfejsu Credential Management API. Jeśli np. hasło jest przechowywane w usłudze login.example.com, skrypt w www.example.com może wyświetlić je jako jeden z elementów konta w oknie wyboru konta.

Musisz wyraźnie zapisać hasło za pomocą navigator.credentials.store(), aby gdy użytkownik wybierze dane logowania, klikając okno, zostanie ono przekazane i skopiowane do bieżącego źródła.

Po zapisaniu hasło jest dostępne jako dane logowania w dokładnie tym samym źródle (www.example.com).

Na poniższym zrzucie ekranu dane logowania przechowywane w systemie login.aliexpress.com są widoczne dla platformy m.aliexpress.com i dostępne dla użytkownika:

Wybór konta wyświetlający dane logowania w wybranej subdomenie

Chrome 60

W Chrome 60 wprowadziliśmy kilka ważnych zmian w interfejsie Credential Management API:

Wykrywanie funkcji wymaga uwagi

Aby sprawdzić, czy jest dostępny interfejs Credential Management API umożliwiający dostęp do danych logowania sfederowanych i opartych na hasłach, sprawdź dostępność interfejsu window.PasswordCredential lub window.FederatedCredential.

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

PasswordCredential obiekt zawiera teraz hasło

Interfejs Credential Management API stosuje zachowawcze podejście do obsługi haseł. Ukrywała hasła przed JavaScriptem, umożliwiając programistom wysyłanie obiektu PasswordCredential bezpośrednio do serwera w celu weryfikacji za pomocą rozszerzenia interfejsu API fetch().

Jednak takie podejście spowodowało kilka ograniczeń. Otrzymaliśmy opinie, że deweloperzy nie mogą korzystać z interfejsu API, ponieważ:

  • Musieli wysłać hasło jako część obiektu JSON.

  • Musieli przesłać wartość skrótu hasła na swój serwer.

Gdy przeprowadziliśmy analizę zabezpieczeń i zauważyliśmy, że ukrycie haseł z kodu JavaScript nie powstrzymało wszystkich wektorów ataku tak, jak tego oczekiwaliśmy, postanowiliśmy wprowadzić zmianę.

Interfejs Credential Management API zawiera teraz nieprzetworzone hasło w zwróconym obiekcie danych logowania, dzięki czemu masz do niego dostęp w postaci zwykłego tekstu. Aby dostarczyć dane logowania na swój serwer, możesz użyć istniejących metod:

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);
});

Pobieranie niestandardowe zostanie wkrótce wycofane

Aby określić, czy używasz niestandardowej funkcji fetch(), sprawdź, czy używa ona obiektu PasswordCredential czy obiektu FederatedCredential jako wartości właściwości credentials, np.:

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

Zalecamy użycie zwykłej funkcji fetch(), jak pokazano w poprzednim przykładzie kodu, lub użycie XMLHttpRequest.

Do wersji Chrome 60 navigator.credentials.get() akceptował opcjonalną właściwość unmediated z flagą wartości logicznej. Na przykład:

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

    // Sign-in
});

Ustawienie unmediated: true uniemożliwia przeglądarce wyświetlanie okna wyboru konta podczas przekazywania danych logowania.

Flaga jest teraz rozszerzona jako zapośredniczenie. Zapośredniczenie użytkownika może nastąpić, gdy:

  • Użytkownik musi wybrać konto, na które chce się zalogować.

  • Użytkownik chce zalogować się bezpośrednio po wywołaniu funkcji navigator.credentials.requireUseMediation().

Wybierz jedną z tych opcji dla wartości mediation:

Wartość: mediation Porównanie z flagą wartości logicznej Sposób działania
silent Równa się unmediated: true Dane logowania zostały zaliczone bez wyświetlania opcji wyboru konta.
optional Równa się unmediated: false Wyświetla wybór konta, jeśli usługa preventSilentAccess() zadzwoniła wcześniej.
required Nowa opcja Zawsze pokazuj wybór konta. Ta opcja jest przydatna, gdy chcesz pozwolić użytkownikowi na przełączanie konta w natywnym oknie wyboru konta.

W tym przykładzie dane logowania są przekazywane bez wyświetlania funkcji wyboru konta, czyli odpowiednika poprzedniej flagi – unmediated: true:

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

    // Sign-in
});

Nazwa tematu zmieniona z requireUserMediation() na preventSilentAccess()

Aby dopasować ją do nowej właściwości mediation oferowanej w wywołaniu get(), nazwa metody navigator.credentials.requireUserMediation() została zmieniona na navigator.credentials.preventSilentAccess().

Metoda ze zmienioną nazwą uniemożliwia przekazywanie danych logowania bez pokazywania opcji wyboru konta (czasami jest wywoływana bez zapośredniczenia użytkownika). Jest to przydatne, gdy użytkownik wylogowuje się z witryny lub wyrejestruje się z niej i nie chce, by przy następnej wizycie automatycznie się z niej logował.

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

Asynchronicznie twórz obiekty danych logowania przy użyciu nowej metody navigator.credentials.create()

Możesz teraz asynchronicznie tworzyć obiekty danych logowania za pomocą nowej metody navigator.credentials.create(). W dalszej części artykułu znajdziesz porównanie metody synchronizacji i asynchronicznej.

Tworzenie obiektu PasswordCredential

Sposób synchronizacji
let c = new PasswordCredential(form);
Podejście asynchroniczne (nowe)
let c = await navigator.credentials.create({
    password: form
});

lub

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

Tworzenie obiektu FederatedCredential

Sposób synchronizacji
let c = new FederatedCredential({
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
});
Podejście asynchroniczne (nowe)
let c = await navigator.credentials.create({
    federated: {
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
    }
});

Przewodnik po migracji

Czy masz już implementację interfejsu Credential Management API? Udostępniliśmy przewodnik po migracji, z którego dowiesz się, jak przejść na nową wersję.