Utwórz klucz dostępu

Zanim użytkownicy będą mogli uwierzytelniać się za pomocą kluczy dostępu, Twoja aplikacja musi najpierw zarejestrować lub utworzyć klucz dostępu na ich koncie.

Aby utworzyć klucz dostępu, pobierz z serwera aplikacji szczegóły wymagane do jego utworzenia, a następnie wywołaj interfejs Credential Manager API, który zwraca parę kluczy – publiczny i prywatny. Zwrócony klucz prywatny jest przechowywany w dostawcy danych logowania, np. w Menedżerze haseł Google, jako klucz dostępu. Klucz publiczny jest przechowywany na serwerze aplikacji.

Klucze dostępu są przechowywane u dostawcy danych logowania, a klucze publiczne – na serwerze aplikacji.
Rysunek 1. Tworzenie kluczy dostępu

Wymagania wstępne

Upewnij się, że masz skonfigurowane Digital Asset Links i że kierujesz aplikację na urządzenia z Androidem 9 (poziom API 28) lub nowszym.

Przegląd

Ten przewodnik koncentruje się na zmianach, które należy wprowadzić w aplikacji klienta strony ufającej , aby utworzyć klucz dostępu. Zawiera też krótki opis implementacji serwera aplikacji strony ufającej. Więcej informacji o integracji po stronie serwera znajdziesz w artykule Rejestracja klucza dostępu po stronie serwera.

  1. Dodaj zależności do aplikacji: dodaj wymagane biblioteki Credential Manager.
  2. Utwórz instancję Credential Manager: utwórz instancję Credential Manager.
  3. Pobierz opcje tworzenia danych logowania z serwera aplikacji: z serwera aplikacji wyślij do aplikacji klienta szczegóły wymagane do utworzenia klucza dostępu, takie jak informacje o aplikacji i użytkowniku oraz challenge i inne pola.
  4. Poproś o klucz dostępu: w aplikacji użyj szczegółów otrzymanych z serwera aplikacji, aby utworzyć obiekt GetPublicKeyCredentialOption, a następnie wywołaj metodę credentialManager.getCredential() w celu utworzenia klucza dostępu.
  5. Obsłuż odpowiedź dotyczącą tworzenia klucza dostępu: gdy otrzymasz dane logowania w aplikacji klienta, musisz je zakodować, zserializować, a następnie wysłać klucz publiczny na serwer aplikacji. Musisz też obsłużyć wszystkie wyjątki, które mogą wystąpić podczas tworzenia klucza dostępu.
  6. **Zweryfikuj i zapisz klucz publiczny na serwerze**: wykonaj czynności po stronie serwera , aby zweryfikować pochodzenie danych logowania, a następnie zapisz klucz publiczny.
  7. Powiadom użytkownika: powiadom użytkownika o utworzeniu klucza dostępu.

Dodawanie zależności do aplikacji

Dodaj te zależności do pliku build.gradle modułu aplikacji:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.7.0-alpha02")
    implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02")
}

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.7.0-alpha02"
    implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02"
}

Tworzenie instancji Credential Manager

Aby utworzyć obiekt CredentialManager, użyj kontekstu aplikacji lub aktywności.

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)

Pobieranie opcji tworzenia danych logowania z serwera aplikacji

Gdy użytkownik kliknie przycisk „Utwórz klucz dostępu” lub gdy nowy użytkownik się zarejestruje, wyślij z aplikacji żądanie do serwera aplikacji, aby uzyskać informacje potrzebne do rozpoczęcia procesu rejestracji klucza dostępu.

Użyj w serwerze aplikacji biblioteki zgodnej z FIDO, aby wysłać do aplikacji klienta informacje wymagane do utworzenia klucza dostępu, takie jak informacje o użytkowniku i aplikacji oraz dodatkowe właściwości konfiguracyjne. Więcej informacji znajdziesz w artykule Rejestracja klucza dostępu po stronie serwera.

W aplikacji klienta zdekoduj opcje tworzenia klucza publicznego wysłane przez serwer aplikacji. Zwykle są one przedstawiane w formacie JSON. Więcej informacji o tym, jak odbywa się dekodowanie w przypadku klientów internetowych, znajdziesz w artykule Kodowanie i dekodowanie. W przypadku aplikacji klienckich na Androida musisz osobno obsłużyć dekodowanie.

Ten fragment kodu pokazuje strukturę opcji tworzenia klucza publicznego wysyłanych przez serwer aplikacji:

{
  "challenge": "<base64url-encoded challenge>",
  "rp": {
    "name": "<relying party name>",
    "id": "<relying party host name>"
  },
  "user": {
    "id": "<base64url-encoded user ID>",
    "name": "<user name>",
    "displayName": "<user display name>"
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    }
  ],
  "attestation": "none",
  "excludeCredentials": [
    {
        "id": "<base64url-encoded credential ID to exclude>", 
        "type": "public-key"
    }
  ],
  "authenticatorSelection": {
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

Kluczowe pola w opcjach tworzenia klucza publicznego:

  • challenge: losowy ciąg znaków wygenerowany przez serwer, który służy do zapobiegania atakom typu replay.
  • rp: szczegóły dotyczące aplikacji.
    • rp.name: nazwa aplikacji.
    • rp.id: domena lub subdomena aplikacji.
  • user: szczegóły dotyczące użytkownika.
    • id: unikalny identyfikator użytkownika. Ta wartość nie może zawierać informacji umożliwiających identyfikację, np. adresów e-mail lub nazw użytkowników. Możesz użyć losowej wartości 16-bajtowej.
    • name: unikalny identyfikator konta, który użytkownik rozpozna, np. adres e-mail lub nazwa użytkownika. Będzie on wyświetlany w selektorze kont. Jeśli używasz nazwy użytkownika, użyj tej samej wartości co w przypadku uwierzytelniania za pomocą hasła.
    • displayName: opcjonalna, przyjazna dla użytkownika nazwa konta, która ma być wyświetlana w selektorze kont.
  • authenticatorSelection: szczegóły dotyczące urządzenia, które będzie używane do uwierzytelniania.

    • authenticatorAttachment: wskazuje preferowany uwierzytelniacz. Możliwe wartości: - platform: ta wartość jest używana w przypadku uwierzytelniacza wbudowanego w urządzenie użytkownika, np. czujnika linii papilarnych. - cross-platform: ta wartość jest używana w przypadku urządzeń przenośnych, takich jak klucze bezpieczeństwa. Zwykle nie jest używana w kontekście klucza dostępu. - Nieokreślona (zalecane): pozostawienie tej wartości nieokreślonej daje użytkownikom możliwość tworzenia kluczy dostępu na preferowanych urządzeniach. W większości przypadków najlepszym rozwiązaniem jest pozostawienie parametru nieokreślonego.
      • requireResidentKey: aby utworzyć klucz dostępu, ustaw wartość tego pola Boolean na true.
      • residentKey: aby utworzyć klucz dostępu, ustaw wartość na required.
      • userVerification: służy do określania wymagań dotyczących weryfikacji użytkownika podczas rejestracji klucza dostępu. Możliwe wartości: są następujące: - preferred: użyj tej wartości, jeśli priorytetem jest wygoda użytkownika, a nie zabezpieczenie, np. w środowiskach, w których weryfikacja użytkownika powoduje więcej problemów niż zabezpieczenia. - required: użyj tej wartości, jeśli wymagane jest wywołanie metody weryfikacji użytkownika dostępnej na urządzeniu. - discouraged: użyj tej wartości, jeśli używanie metody weryfikacji użytkownika jest odradzane.
        Więcej informacji o userVerification, znajdziesz w artykule Szczegółowe informacje o userVerification.
  • excludeCredentials: umieść identyfikatory danych logowania na liście w tablicy, aby zapobiec utworzeniu duplikatu klucza dostępu, jeśli taki klucz już istnieje u tego samego dostawcy danych logowania.

Tworzenie klucza dostępu

Po przeanalizowaniu opcji tworzenia klucza publicznego po stronie serwera utwórz klucz dostępu, umieszczając te opcje w obiekcie CreatePublicKeyCredentialRequest i wywołując metodę createCredential().

createPublicKeyCredentialRequest zawiera te elementy:

  • requestJson: opcje tworzenia danych logowania wysłane przez serwer aplikacji.
  • preferImmediatelyAvailableCredentials: opcjonalne pole logiczne , które określa, czy do realizacji żądania należy używać tylko danych logowania dostępnych lokalnie lub zsynchronizowanych z dostawcą danych logowania , zamiast danych logowania z kluczy bezpieczeństwa lub hybrydowych przepływów kluczy. Możliwe zastosowania:
    • false (domyślnie): użyj tej wartości, jeśli wywołanie Credential Manager zostało wywołane przez wyraźne działanie użytkownika.
    • true: użyj tej wartości, jeśli Credential Manager jest wywoływany oportunistycznie , np. podczas pierwszego otwierania aplikacji.
      Jeśli ustawisz wartość true i nie ma dostępnych od razu danych logowania, Credential Manager nie wyświetli żadnego interfejsu, a żądanie zakończy się natychmiast niepowodzeniem, zwracając NoCredentialException w przypadku żądań get i CreateCredentialNoCreateOptionException w przypadku żądań create.
  • origin: to pole jest automatycznie ustawiane w przypadku aplikacji na Androida. W przypadku przeglądarek i podobnych aplikacji z uprawnieniami, które muszą ustawić origin, zobacz Wykonywanie wywołań Credential Manager w imieniu innych podmiotów w przypadku aplikacji z uprawnieniami.
  • isConditional: to pole opcjonalne, którego domyślna wartość to false. Więcej informacji znajdziesz w artykule Automatyczne tworzenie klucza dostępu.

Wywołanie funkcji createCredential() uruchamia wbudowany w Credential Manager interfejs w postaci planszy dolnej, który prosi użytkownika o użycie klucza dostępu i wybranie dostawcy danych logowania oraz konta do przechowywania. Jeśli jednak isConditional jest ustawione na true, interfejs planszy dolnej nie jest wyświetlany, a klucz dostępu jest tworzony automatycznie.

Automatyczne tworzenie klucza dostępu

Możesz automatycznie utworzyć klucz dostępu dla użytkownika po udanym zalogowaniu się za pomocą hasła, ustawiając parametr isConditional na true w CreatePublicKeyCredentialRequest podczas tworzenia klucza dostępu. Jeśli użytkownik nie ma jeszcze klucza dostępu, Twoja aplikacja automatycznie spróbuje utworzyć go w tle i zapisać u dostawcy danych logowania użytkownika, np. w Menedżerze haseł Google. Przykład implementacji znajdziesz w publicznym przykładzie.

Przykład powiadomienia wyświetlanego przez Menedżera haseł Google po utworzeniu klucza dostępu
Rysunek 2. Powiadomienie Menedżera haseł Google

Obsługa odpowiedzi

Po zweryfikowaniu użytkownika za pomocą blokady ekranu urządzenia klucz dostępu jest tworzony i przechowywany u wybranego przez użytkownika dostawcy danych logowania.

Odpowiedzią po pomyślnym wywołaniu createCredential() jest obiekt PublicKeyCredential.

PublicKeyCredential wygląda tak:

{
  "id": "<identifier>",
  "type": "public-key",
  "rawId": "<identifier>",
  "response": {
    "clientDataJSON": "<ArrayBuffer encoded object with the origin and signed challenge>",
    "attestationObject": "<ArrayBuffer encoded object with the public key and other information.>"
  },
  "authenticatorAttachment": "platform"
}

W aplikacji klienta zserializuj obiekt i wyślij go na serwer aplikacji.

Dodaj kod do obsługi błędów, jak pokazano w tym fragmencie:

fun handleFailure(e: CreateCredentialException) {
    when (e) {
        is CreatePublicKeyCredentialDomException -> {
            // Handle the passkey DOM errors thrown according to the
            // WebAuthn spec.
        }
        is CreateCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to register the credential.
        }
        is CreateCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is CreateCredentialProviderConfigurationException -> {
            // Your app is missing the provider configuration dependency.
            // Most likely, you're missing the
            // "credentials-play-services-auth" module.
        }
        is CreateCredentialCustomException -> {
            // You have encountered an error from a 3rd-party SDK. If you
            // make the API call with a request object that's a subclass of
            // CreateCustomCredentialRequest using a 3rd-party SDK, then you
            // should check for any custom exception type constants within
            // that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
    }
}

Weryfikowanie i zapisywanie klucza publicznego na serwerze aplikacji

Na serwerze aplikacji musisz zweryfikować dane logowania klucza publicznego, a następnie zapisać klucz publiczny.

Aby zweryfikować pochodzenie danych logowania klucza publicznego, porównaj je z listą dozwolonych aplikacji. Jeśli klucz ma nierozpoznane pochodzenie, odrzuć go.

Aby uzyskać odcisk cyfrowy SHA-256 aplikacji:

  1. Wydrukuj certyfikat podpisywania aplikacji w wersji produkcyjnej, uruchamiając w terminalu to polecenie:

    keytool -list -keystore <path-to-apk-signing-keystore>
    

    W odpowiedzi znajdź odcisk cyfrowy SHA-256 certyfikatu podpisywania, który jest wymieniony jako Certificate fingerprints block : SHA256.

  2. Zakoduj odcisk cyfrowy SHA256 za pomocą kodowania base64url. Ten przykład w Pythonie pokazuje, jak prawidłowo zakodować odcisk cyfrowy:

    import binascii
    import base64
    fingerprint = '<SHA256 finerprint>' # your app's SHA256 fingerprint
    print(base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))
    
  3. Na początku danych wyjściowych z poprzedniego kroku dodaj android:apk-key-hash:, aby uzyskać coś podobnego do tego:

    android:apk-key-hash:<encoded SHA 256 fingerprint>
    

    Wynik powinien być zgodny z dozwolonym pochodzeniem na serwerze aplikacji. Jeśli masz kilka certyfikatów podpisywania, np. certyfikaty do debugowania i wersji produkcyjnej, lub kilka aplikacji, powtórz proces i zaakceptuj wszystkie pochodzenia jako prawidłowe na serwerze aplikacji.

Powiadamianie użytkownika

Po pomyślnym utworzeniu klucza dostępu powiadom użytkowników o kluczu dostępu i poinformuj ich, że mogą zarządzać kluczami dostępu w aplikacji dostawcy danych logowania lub z poziomu ustawień aplikacji. Powiadom użytkowników za pomocą niestandardowego okna, powiadomienia lub paska powiadomień. Ponieważ nieoczekiwane utworzenie klucza dostępu przez złośliwy podmiot wymaga natychmiastowego alertu bezpieczeństwa, rozważ uzupełnienie tych metod w aplikacji o komunikację zewnętrzną, np. e-mail.

Ulepszanie wygody użytkowników

Aby poprawić wygodę użytkowników podczas implementowania rejestracji za pomocą Credential Manager, rozważ dodanie funkcji przywracania danych logowania i wyłączenia wyświetlania okien autouzupełniania.

Dodawanie funkcji przywracania danych logowania na nowym urządzeniu

Aby umożliwić użytkownikom bezproblemowe logowanie się na kontach na nowym urządzeniu, zaimplementuj funkcję przywracania danych logowania. Dodanie przywracania danych logowania za pomocą BackupAgent powoduje zalogowanie użytkowników po otwarciu przywróconej aplikacji na nowym urządzeniu, co pozwala im od razu korzystać z aplikacji.

Wyłączanie autouzupełniania w polach danych logowania (opcjonalnie)

W przypadku ekranów aplikacji, na których użytkownicy mają używać interfejsu w postaci arkusza u dołu ekranu Credential Manager do uwierzytelniania, dodaj atrybut isCredential do pól nazwy użytkownika i hasła. Zapobiega to nakładaniu się okien autouzupełniania (FillDialog i SaveDialog) na interfejs planszy dolnej Credential Manager.

Atrybut isCredential jest obsługiwany w Androidzie 14 i nowszym.

Ten przykład pokazuje, jak dodać atrybut isCredential do odpowiednich pól nazwy użytkownika i hasła w odpowiednich widokach aplikacji:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:isCredential="true" />

Dalsze kroki