Logowanie się za pomocą klucza dostępu

Ten przewodnik jest kontynuacją implementacji uwierzytelniania za pomocą kluczy dostępu. Zanim użytkownicy będą mogli logować się za pomocą kluczy dostępu, musisz też wykonać instrukcje opisane 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ź logowania.

Przegląd

Ten przewodnik koncentruje się na zmianach wymaganych w aplikacji klienckiej, aby umożliwić użytkownikowi logowanie się za pomocą klucza dostępu. Zawiera też krótki opis 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 opcje klucza dostępu i hasła powiązane z kontem użytkownika, wykonaj te czynności:

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

Pobieranie opcji żądania danych logowania z serwera

Poproś serwer o opcje wymagane do uzyskania danych logowania klucza publicznego oraz o challenge, które jest unikalne dla każdej próby logowania. Więcej informacji o implementacji po stronie serwera znajdziesz w artykułach Tworzenie wyzwania i Tworzenie opcji żądania danych logowania.

Opcje wyglądają podobnie do tych:

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

Więcej informacji o polach znajdziesz w poście na blogu na temat logowania się za pomocą klucza dostępu.

Tworzenie obiektu wymaganego do uzyskania danych logowania klucza publicznego

W aplikacji użyj opcji, aby utworzyć obiekt GetPublicKeyCredentialOption. W poniższym przykładzie requestJson reprezentuje 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 opakuj GetPublicKeyCredentialOption w obiekt 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
)

Opcjonalnie: zmniejszenie opóźnienia logowania

W Androidzie 14 lub nowszym możesz zmniejszyć opóźnienie podczas wyświetlania selektora konta, używając metody prepareGetCredential() przed wywołaniem getCredential().

Metoda prepareGetCredential() zwraca obiekt PrepareGetCredentialResponse, który jest zapisywany w pamięci podręcznej. Dzięki temu metoda getCredential() w następnym kroku może wyświetlić selektor konta 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
            )
        )
    )
}

Uruchamianie procesu logowania

Wywołaj metodę getCredential(), aby wyświetlić użytkownikowi selektor konta. Jako odniesienie do sposobu uruchamiania procesu logowania użyj tego fragmentu kodu:

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

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

Element PublicKeyCredential zwrócony z uwierzytelniania jest zasadniczo podpisanym potwierdzeniem o takiej 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 i logowanie użytkownika.

Obsługa wyjątków

Powinieneś obsługiwać wszystkie wyjątki podklasy GetCredentialException. Aby dowiedzieć się, jak obsługiwać poszczególne wyjątki, zapoznaj się z przewodnikiem po rozwiązywaniu problemów.

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

Dalsze kroki