Najnowsze aktualizacje interfejsu API do zarządzania danymi logowania

Niektóre z opisanych tu zmian zostały omówione podczas sesji na konferencji Google I/O Bezpieczne i płynne logowanie: jak utrzymać zaangażowanie użytkowników:

Chrome 57

W Chrome 57 wprowadzono tę ważną zmianę w interfejsie Credential Management API.

Dane logowania można udostępnić z innej subdomeny

Chrome może teraz pobierać dane logowania zapisane w innej subdomenie za pomocą interfejsu Credential Management API. Jeśli np. hasło jest zapisane w koncie login.example.com, skrypt na koncie 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ą funkcji navigator.credentials.store(), aby gdy użytkownik wybierze dane logowania, klikając okno dialogowe, hasło zostało przekazane i skopiowane do bieżącego źródła.

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

Na poniższym zrzucie ekranu informacje o kwalifikacjach przechowywane w sekcji login.aliexpress.com są widoczne dla m.aliexpress.com i dostępne dla użytkownika:

Selektor konta wyświetlający szczegóły logowania do wybranej subdomeny

Chrome 60

W Chrome 60 wprowadzono kilka ważnych zmian w interfejsie Credential Management API:

Wykrywanie funkcji wymaga uwagi

Aby sprawdzić, czy interfejs Credential Management API jest dostępny dla haseł i poświadczeń uwierzytelniania federacyjnego, sprawdź, czy jest dostępna usługa window.PasswordCredential lub window.FederatedCredential.

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

Obiekt PasswordCredential zawiera teraz hasło

Interfejs Credential Management API stosował konserwatywne podejście do obsługi haseł. Ukrywało hasła przed JavaScriptem, wymagając od deweloperów wysyłania obiektu PasswordCredential bezpośrednio na serwer w celu sprawdzenia za pomocą rozszerzenia interfejsu API fetch().

Takie podejście wiązało się jednak z pewnymi ograniczeniami. Otrzymaliśmy informację, że deweloperzy nie mogli korzystać z interfejsu API, ponieważ:

  • Hasło musi być wysyłane jako część obiektu JSON.

  • Musieli wysłać na swój serwer wartość hasha hasła.

Po przeprowadzeniu analizy bezpieczeństwa i stwierdzeniu, że ukrywanie haseł w JavaScript nie zapobiegało wszystkim wektorom ataków tak skutecznie, jak mieliśmy nadzieję, postanowiliśmy wprowadzić zmianę.

Interfejs Credential Management API zawiera teraz hasło w postaci zwykłego tekstu w zwróconym obiekcie danych logowania, dzięki czemu masz do niego dostęp w postaci zwykłego tekstu. Aby przekazać informacje o danych logowania na serwer, możesz użyć dotychczasowych 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);
});

Wkrótce wycofamy niestandardowe pobieranie

Aby sprawdzić, czy używasz niestandardowej funkcji fetch(), sprawdź, czy używa ona obiektu PasswordCredential lub FederatedCredential jako wartości właściwości credentials, na przykład:

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

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

Do wersji Chrome 60 funkcja navigator.credentials.get() przyjmowała opcjonalną właściwość unmediated z flagą logiczną. 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 selektora kont podczas przekazywania danych logowania.

Flaga jest teraz rozszerzona o zapośredniczenie. Mediacja użytkownika może się zdarzyć, gdy:

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

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

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

Wartość: mediation W porównaniu z flagą logiczną Zachowanie
silent Równa się unmediated: true Dane logowania zostały przekazane bez wyświetlenia selektora kont.
optional Równa się unmediated: false Wyświetla selektor kont, jeśli preventSilentAccess() został wywołany wcześniej.
required Nowa opcja Zawsze pokazuj selektor kont. Przydatne, gdy chcesz umożliwić użytkownikowi przełączanie kont za pomocą domyślnego okna wyboru konta.

W tym przykładzie dane logowania są przekazywane bez wyświetlania selektora kont, co jest równoznaczne z poprzednią flagą: unmediated: true.

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

    // Sign-in
});

Zmieniono nazwę użytkownika z requireUserMediation() na preventSilentAccess()

Aby dostosować się do nowej właściwości mediation oferowanej w wywołaniu get(), nazwę metody navigator.credentials.requireUserMediation() zmieniliśmy na navigator.credentials.preventSilentAccess().

Przemianowana metoda zapobiega przekazywaniu danych logowania bez wyświetlania selektora kont (czasami wywoływanego bez pośrednictwa użytkownika). Jest to przydatne, gdy użytkownik wyloguje się z witryny lub anuluje rejestrację i nie chce, aby podczas następnej wizyty nastąpiło automatyczne zalogowanie.

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

Tworzenie obiektów danych logowania w tle za pomocą nowej metody navigator.credentials.create()

Teraz możesz asynchronicznie tworzyć obiekty danych logowania za pomocą nowej metody navigator.credentials.create(). Czytaj dalej, aby porównać podejścia synchroniczne i asynchroniczne.

Tworzenie obiektu PasswordCredential

Podejście do synchronizacji
let c = new PasswordCredential(form);
Podejście asynchroniczne (nowość)
let c = await navigator.credentials.create({
    password: form
});

lub

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

Tworzenie obiektu FederatedCredential

Podejście do synchronizacji
let c = new FederatedCredential({
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
});
Podejście asynchroniczne (nowość)
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? Aby przejść na nową wersję, możesz skorzystać z przewodnika po migracji.