Menedżer danych logowania obsługuje klucze dostępu, logowanie sfederowane i usługodawców uwierzytelniania innych niż Google. Jest to zalecany interfejs API do uwierzytelniania na Androidzie, który zapewnia bezpieczne i wygodne środowisko, w którym użytkownicy mogą synchronizować swoje dane logowania i nimi zarządzać. Jeśli używasz lokalnych danych uwierzytelniających FIDO2, zaktualizuj swoją aplikację, aby obsługiwała uwierzytelnianie za pomocą klucza dostępu. W tym celu zintegruj ją z interfejsem Credential Manager API. Z tego dokumentu dowiesz się, jak przenieść projekt z FIDO2 do usługi Credential Manager.
Powody przejścia z FIDO2 na Menedżera danych logowania
W większości przypadków należy przenieść dostawcę uwierzytelniania aplikacji na Androida do usługi Credential Manager. Powody migracji do usługi Credential Manager obejmują:
- Obsługa kluczy dostępu: Menedżer danych logowania obsługuje klucze dostępu, czyli nowy mechanizm uwierzytelniania bez hasła, który jest bezpieczniejszy i łatwiejszy w użyciu niż hasła.
- Wiele metod logowania: Menedżer danych logowania obsługuje wiele metod logowania, w tym hasła, klucze dostępu i federowane metody logowania. Ułatwia to użytkownikom uwierzytelnianie się w aplikacji niezależnie od preferowanej metody uwierzytelniania.
- Obsługa zewnętrznych dostawców danych logowania: w Androidzie 14 i nowszych Menedżer danych logowania obsługuje wielu zewnętrznych dostawców danych logowania. Oznacza to, że użytkownicy mogą logować się w Twojej aplikacji za pomocą swoich danych logowania od innych dostawców.
- Spójność wrażeń użytkowników: menedżer danych logowania zapewnia większą spójność uwierzytelniania w różnych aplikacjach i mechanizmach logowania. Dzięki temu użytkownicy łatwiej zrozumieją i wykorzystują proces uwierzytelniania w aplikacji.
Aby rozpocząć migrację z FIDO2 do Menedżera danych logowania, wykonaj te czynności.
Zaktualizuj zależności
Zaktualizuj wtyczkę Kotlin w pliku build.gradle projektu do wersji 1.8.10 lub nowszej.
plugins { //… id 'org.jetbrains.kotlin.android' version '1.8.10' apply false //… }
W pliku build.gradle projektu zaktualizuj zależności, aby używać Menedżera danych logowania i uwierzytelniania Usług Google Play.
dependencies { // ... // Credential Manager: implementation 'androidx.credentials:credentials:<latest-version>' // Play Services Authentication: // Optional - needed for credentials support from play services, for devices running // Android 13 and below: implementation 'androidx.credentials:credentials-play-services-auth:<latest-version>' // ... }
Zastąp inicjowanie FIDO inicjalizacją za pomocą usługi Credential Manager. Dodaj tę deklarację w klasie, której używasz do tworzenia kluczy dostępu i metod logowania:
val credMan = CredentialManager.create(context)
Tworzenie kluczy dostępu
Musisz utworzyć nowy klucz dostępu, powiązać go z kontem użytkownika i zapisać klucz publiczny klucza dostępu na serwerze, zanim użytkownik będzie mógł się zalogować. Aby skonfigurować taką możliwość w aplikacji, zaktualizuj wywołania funkcji rejestrowania.
Aby uzyskać niezbędne parametry, które są wysyłane do metody
createCredential()
podczas tworzenia klucza dostępu, dodajname("residentKey").value("required")
zgodnie z opisem w specyfikacji WebAuthn do wywołania serweraregisterRequest()
.suspend fun registerRequest(sessionId: String ... { // ... .method("POST", jsonRequestBody { name("attestation").value("none") name("authenticatorSelection").objectValue { name("residentKey").value("required") } }).build() // ... }
Ustaw typ
return
dla funkcjiregisterRequest()
i wszystkich funkcji podrzędnych naJSONObject
.suspend fun registerRequest(sessionId: String): ApiResult<JSONObject> { val call = client.newCall( Request.Builder() .url("$BASE_URL/<your api url>") .addHeader("Cookie", formatCookie(sessionId)) .method("POST", jsonRequestBody { name("attestation").value("none") name("authenticatorSelection").objectValue { name("authenticatorAttachment").value("platform") name("userVerification").value("required") name("residentKey").value("required") } }).build() ) val response = call.await() return response.result("Error calling the api") { parsePublicKeyCredentialCreationOptions( body ?: throw ApiException("Empty response from the api call") ) } }
Bezpiecznie usuń z widoku wszystkie metody, które obsługują wywołania programu uruchamiającego intencji i wyników działań.
registerRequest()
zwraca terazJSONObject
, więc nie musisz tworzyćPendingIntent
. Zastąp zwróconą intencję elementemJSONObject
. Zaktualizuj wywołania programu uruchamiającego intencji, aby wywoływały funkcjęcreateCredential()
z interfejsu API Credential Manager. Wywołaj metodę interfejsu APIcreateCredential()
.suspend fun createPasskey( activity: Activity, requestResult: JSONObject ): CreatePublicKeyCredentialResponse? { val request = CreatePublicKeyCredentialRequest(requestResult.toString()) var response: CreatePublicKeyCredentialResponse? = null try { response = credMan.createCredential( request as CreateCredentialRequest, activity ) as CreatePublicKeyCredentialResponse } catch (e: CreateCredentialException) { showErrorAlert(activity, e) return null } return response }
Po zakończeniu wywołania prześlij odpowiedź z powrotem na serwer. Żądanie i odpowiedź dla tego wywołania są podobne do implementacji FIDO2, więc nie są wymagane żadne zmiany.
Uwierzytelnienie za pomocą kluczy dostępu
Po skonfigurowaniu tworzenia kluczy dostępu możesz skonfigurować aplikację, aby umożliwić użytkownikom logowanie się i uwierzytelnianie za pomocą kluczy dostępu. W tym celu zaktualizuj kod uwierzytelniania, aby obsługiwał wyniki Menedżera danych logowania, i wdróż funkcję uwierzytelniania za pomocą kluczy dostępu.
- Wywołanie żądania logowania do serwera w celu uzyskania niezbędnych informacji do wysłania do żądania
getCredential()
jest takie samo jak w przypadku implementacji FIDO2. Nie musisz wprowadzać żadnych zmian. Podobnie jak w przypadku wywołania metody register request, odpowiedź jest zwracana w formacie JSONObject.
/** * @param sessionId The session ID to be used for the sign-in. * @param credentialId The credential ID of this device. * @return a JSON object. */ suspend fun signinRequest(): ApiResult<JSONObject> { val call = client.newCall(Builder().url(buildString { append("$BASE_URL/signinRequest") }).method("POST", jsonRequestBody {}) .build() ) val response = call.await() return response.result("Error calling /signinRequest") { parsePublicKeyCredentialRequestOptions( body ?: throw ApiException("Empty response from /signinRequest") ) } } /** * @param sessionId The session ID to be used for the sign-in. * @param response The JSONObject for signInResponse. * @param credentialId id/rawId. * @return A list of all the credentials registered on the server, * including the newly-registered one. */ suspend fun signinResponse( sessionId: String, response: JSONObject, credentialId: String ): ApiResult<Unit> { val call = client.newCall( Builder().url("$BASE_URL/signinResponse") .addHeader("Cookie",formatCookie(sessionId)) .method("POST", jsonRequestBody { name("id").value(credentialId) name("type").value(PUBLIC_KEY.toString()) name("rawId").value(credentialId) name("response").objectValue { name("clientDataJSON").value( response.getString("clientDataJSON") ) name("authenticatorData").value( response.getString("authenticatorData") ) name("signature").value( response.getString("signature") ) name("userHandle").value( response.getString("userHandle") ) } }).build() ) val apiResponse = call.await() return apiResponse.result("Error calling /signingResponse") { } }
Bezpiecznie usuń z widoku wszystkie metody, które obsługują wywołania programu uruchamiającego intencji i wyników działań.
Funkcja
signInRequest()
zwraca teraz wartośćJSONObject
, więc nie musisz tworzyć funkcjiPendingIntent
. Zastąp zwrócony zamiar wartościąJSONObject
i wywołaj funkcjęgetCredential()
z metod interfejsu API.suspend fun getPasskey( activity: Activity, creationResult: JSONObject ): GetCredentialResponse? { Toast.makeText( activity, "Fetching previously stored credentials", Toast.LENGTH_SHORT) .show() var result: GetCredentialResponse? = null try { val request= GetCredentialRequest( listOf( GetPublicKeyCredentialOption( creationResult.toString(), null ), GetPasswordOption() ) ) result = credMan.getCredential(activity, request) if (result.credential is PublicKeyCredential) { val publicKeycredential = result.credential as PublicKeyCredential Log.i("TAG", "Passkey ${publicKeycredential.authenticationResponseJson}") return result } } catch (e: Exception) { showErrorAlert(activity, e) } return result }
Po zakończeniu wywołania prześlij odpowiedź z serwera, aby zweryfikować i uwierzytelnić użytkownika. Parametry żądania i odpowiedzi w tym wywołaniu interfejsu API są podobne do implementacji FIDO2, więc nie są wymagane żadne zmiany.
Dodatkowe materiały
- Przykładowe odwołanie do Credential Managera
- Ćwiczenie Codelab dotyczące Credential Managera
- Łatwe uwierzytelnianie w aplikacjach za pomocą kluczy dostępu i interfejsu CredentialManager API
- Ćwiczenie z programowania dotyczące FIDO2