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:
- 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.
- Utwórz obiekt wymagany do uzyskania danych logowania klucza publicznego: opakuj
opcje wysłane przez serwer w obiekt
GetPublicKeyCredentialOption. - (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łaniemgetCredential(). - Uruchom proces logowania: wywołaj metodę
getCredential(), aby zalogować użytkownika. - Obsłuż odpowiedź: obsłuż każdą z możliwych odpowiedzi dotyczących danych logowania.
- 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)
}
}