Logowanie się za pomocą klucza dostępu

Ten przewodnik jest kontynuacją implementacji kluczy dostępu do uwierzytelniania. Zanim użytkownicy będą mogli logować się za pomocą kluczy dostępu, musisz też wykonać instrukcje podane w artykule Tworzenie kluczy dostępu.

Aby uwierzytelnić się za pomocą klucza dostępu, musisz najpierw pobrać opcje wymagane do pobrania klucza publicznego z serwera aplikacji, a następnie wywołać interfejs Credential Manager API, aby pobrać klucz publiczny. Następnie odpowiednio obsłuż odpowiedź dotyczącą logowania.

Omówienie

Ten przewodnik skupia się na zmianach, które należy wprowadzić w aplikacji klienckiej, aby umożliwić użytkownikowi logowanie się za pomocą klucza dostępu. Zawiera też krótkie omówienie implementacji po stronie serwera aplikacji. Więcej informacji o integracji po stronie serwera znajdziesz w artykule Uwierzytelnianie za pomocą klucza dostępu po stronie serwera.

Aby pobrać wszystkie klucze dostępu i hasła powiązane z kontem użytkownika, wykonaj te czynności:

  1. Pobierz z serwera opcje żądania danych logowania: wyślij z aplikacji do serwera uwierzytelniającego żądanie rozpoczęcia procesu logowania za pomocą klucza dostępu. Z serwera wyślij opcje wymagane do uzyskania danych logowania za pomocą klucza publicznego oraz unikalne wyzwanie.
  2. Utwórz obiekt wymagany do uzyskania danych logowania za pomocą klucza publicznego: umieść opcje wysłane przez serwer w obiekcie GetPublicKeyCredentialOption.
  3. (Opcjonalnie) Przygotuj getCredential: na Androidzie 14 i nowszych możesz zmniejszyć opóźnienie, wyświetlając selektor konta za pomocą metody prepareGetCredential() przed wywołaniem getCredential().
  4. Uruchom proces logowania: wywołaj metodę getCredential(), aby zalogować użytkownika.
  5. Przetwarzanie odpowiedzi: przetwórz każdą z możliwych odpowiedzi dotyczących danych logowania.
  6. Obsługa wyjątków: upewnij się, że odpowiednio obsługujesz wyjątki.

1. Pobieranie z serwera opcji żądania danych logowania

Poproś serwer o opcje wymagane do uzyskania danych logowania klucza publicznego, a także o wartość challenge, która jest unikalna dla każdej próby logowania. Więcej informacji o implementacji po stronie serwera znajdziesz w sekcjach Tworzenie wyzwaniaTworzenie opcji żądania danych logowania.

Opcje wyglądają mniej więcej tak:

{
  "challenge": "<your app challenge>",
  "allowCredentials": [],
  "rpId": "<your app server domain>"
}

Więcej informacji o polach znajdziesz w artykule na blogu o logowaniu się przy użyciu klucza dostępu.

2. Utwórz obiekt wymagany do uzyskania danych logowania klucza publicznego.

W aplikacji użyj opcji, aby utworzyć obiekt GetPublicKeyCredentialOption. W poniższym przykładzie symbol requestJson oznacza opcje wysłane przez serwer.

// Get password logins from the credential provider on the user's device.
val getPasswordOption = GetPasswordOption()

// Get passkeys from the credential provider on the user's device.
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
    requestJson = requestJson
)

Następnie umieść GetPublicKeyCredentialOption w obiekcie GetCredentialRequest.

val credentialRequest = GetCredentialRequest(
    // Include all the sign-in options that your app supports.
    listOf(getPasswordOption, getPublicKeyCredentialOption),
    // Defines whether you prefer to use only immediately available
    // credentials or hybrid credentials.
    preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
)

3. Opcjonalnie: zmniejszanie opóźnienia logowania

Na Androidzie 14 lub nowszym możesz skrócić czas oczekiwania podczas wyświetlania selektora konta, używając metody prepareGetCredential() przed wywołaniem getCredential().

Metoda prepareGetCredential() zwraca obiekt PrepareGetCredentialResponse, który jest przechowywany w pamięci podręcznej. Dzięki temu metoda getCredential() w następnym kroku wyświetli selektor kont z danymi z pamięci podręcznej.

coroutineScope {
    val response = credentialManager.prepareGetCredential(
        GetCredentialRequest(
            listOf(
                // Include all the sign-in options that your app supports
                getPublicKeyCredentialOption, 
                getPasswordOption
            )
        )
    )
}

4. Uruchamianie procesu logowania

Wywołaj metodę getCredential(), aby wyświetlić selektor konta. Użyj tego fragmentu kodu jako odniesienia do uruchamiania procesu logowania:

coroutineScope {
    try {
        result = credentialManager.getCredential(
            // Use an activity-based context to avoid undefined system UI
            // launching behavior.
            context = activityContext,
            request = credentialRequest
        )
        handleSignIn(result)
    } catch (e: GetCredentialException) {
        // Handle failure
    }
}

5. Obsługa odpowiedzi

Obsłuż odpowiedź, która może zawierać jeden z różnych typów obiektów danych logowania.

fun handleSignIn(result: GetCredentialResponse) {
    // Handle the successfully returned credential.
    val credential = result.credential

    when (credential) {
        is PublicKeyCredential -> {
            val responseJson = credential.authenticationResponseJson
            // Share responseJson i.e. a GetCredentialResponse on your server to
            // validate and  authenticate
        }

        is PasswordCredential -> {
            val username = credential.id
            val password = credential.password
            // Use id and password to send to your server to validate
            // and authenticate
        }

        is CustomCredential -> {
            // If you are also using any external sign-in libraries, parse them
            // here with the utility functions provided.
            if (credential.type == ExampleCustomCredential.TYPE) {
                try {
                    val ExampleCustomCredential =
                        ExampleCustomCredential.createFrom(credential.data)
                    // Extract the required credentials and complete the authentication as per
                    // the federated sign in or any external sign in library flow
                } catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
                    // Unlikely to happen. If it does, you likely need to update the dependency
                    // version of your external sign-in library.
                    Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
                }
            } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential")
            }
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential")
        }
    }
}

PublicKeyCredential zwrócony w ramach uwierzytelniania to w zasadzie podpisane potwierdzenie o tej strukturze:

{
  "id": "<credential ID>",
  "type": "public-key",
  "rawId": "<raw credential ID>",
  "response": {
    "clientDataJSON": "<signed client data containing challenge>",
    "authenticatorData": "<authenticator metadata>",
    "signature": "<digital signature to be verified>",
    "userHandle": "<user ID from credential registration>"
  }
}

Na serwerze musisz zweryfikować dane logowania. Więcej informacji znajdziesz w artykule Weryfikowanie użytkownika i logowanie go.

6. Obsługa wyjątków

Musisz obsługiwać wszystkie wyjątki podklasy GetCredentialException. Aby dowiedzieć się, jak postępować w przypadku każdego wyjątku, zapoznaj się z tym przewodnikiem.

coroutineScope {
    try {
        result = credentialManager.getCredential(
            context = activityContext,
            request = credentialRequest
        )
    } catch (e: GetCredentialException) {
        Log.e("CredentialManager", "No credential available", e)
    }
}

Dalsze kroki