Menedżer danych logowania to interfejs Jetpack API, który obsługuje wiele metod logowania, takich jak nazwa użytkownika i hasło, klucze dostępu i rozwiązania logowania federacyjnego (np. Zaloguj się przez Google), w jednym interfejsie API, co upraszcza integrację dla programistów.
Ponadto Credential Manager ujednolica interfejs logowania niezależnie od metody uwierzytelniania, dzięki czemu użytkownicy mogą łatwiej i w bardziej przejrzysty sposób logować się w aplikacjach niezależnie od wybranej metody.
Na tej stronie przedstawiamy pojęcie kluczy dostępu i czynności, które należy wykonać, aby wdrożyć obsługę po stronie klienta w zakresie rozwiązań uwierzytelniania, w tym kluczy dostępu, przy użyciu interfejsu Credential Manager API. Jest też osobna strona z najczęstszymi pytaniami, na której znajdziesz odpowiedzi na bardziej szczegółowe pytania.
Twoja opinia jest kluczowa dla ulepszania interfejsu Credential Manager API. Udostępniaj znalezione problemy lub pomysły na ulepszenie interfejsu API za pomocą tego linku:
Informacje o kluczach dostępu
Klucze dostępu są bezpieczniejsze i łatwiejsze w zastępowaniu haseł. Dzięki kluczom dostępu użytkownicy mogą logować się w aplikacjach i na stronach internetowych za pomocą czujnika biometrycznego (np. odcisku palca lub rozpoznawania twarzy), kodu PIN lub wzoru. Dzięki temu logowanie jest płynne, a użytkownicy nie muszą pamiętać nazw użytkowników ani haseł.
Klucze dostępu opierają się na WebAuthn (uwierzytelnianiu w internecie), standardzie opracowanym wspólnie przez FIDO Alliance i World Wide Web Consortium (W3C). WebAuthn używa szyfrowania kluczem publicznym do uwierzytelniania użytkownika. Witryna lub aplikacja, w której loguje się użytkownik, może zobaczyć i zapisać klucz publiczny, ale nie klucz prywatny. Klucz prywatny jest przechowywany w prywatności i w bezpiecznym miejscu. Ponieważ klucz jest unikalny i powiązany z witryną lub aplikacją, nie można go przechwycić, co zwiększa bezpieczeństwo.
Menedżer danych logowania umożliwia użytkownikom tworzenie kluczy dostępu i zapisywanie ich w Menedżerze haseł Google.
Aby dowiedzieć się, jak wdrożyć płynne przepływy uwierzytelniania za pomocą kluczy dostępu za pomocą Menedżera danych logowania, przeczytaj artykuł Uwierzytelnianie użytkowników za pomocą kluczy dostępu.
Wymagania wstępne
Aby używać usługi Credential Manager, wykonaj czynności opisane w tej sekcji.
Używanie najnowszej wersji platformy
Menedżer danych logowania jest obsługiwany na Androidzie 4.4 (poziom interfejsu API 19) i nowszych wersjach.
Dodawanie zależności do aplikacji
Dodaj te zależności do skryptu kompilacji modułu aplikacji:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.5.0-beta01") // optional - needed for credentials support from play services, for devices running // Android 13 and below. implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.5.0-beta01" // optional - needed for credentials support from play services, for devices running // Android 13 and below. implementation "androidx.credentials:credentials-play-services-auth:1.5.0-beta01" }
Zachowanie klas w pliku ProGuard
W pliku proguard-rules.pro
modułu dodaj te dyrektywy:
-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
*;
}
Dowiedz się więcej o skompresowaniu, zaciemnianiu i optymalizowaniu aplikacji.
Dodanie obsługi Digital Asset Links
Aby włączyć obsługę kluczy dostępu w aplikacji na Androida, powiąż ją z witryną, do której należy Twoja aplikacja. Aby zadeklarować to powiązanie, wykonaj te czynności:
Utwórz plik JSON protokołu Digital Asset Links. Aby na przykład zadeklarować, że strona
https://signin.example.com
i aplikacja na Androida o nazwie pakietucom.example
mogą udostępniać dane logowania, utwórz plik o nazwieassetlinks.json
z treścią:[ { "relation" : [ "delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds" ], "target" : { "namespace" : "android_app", "package_name" : "com.example.android", "sha256_cert_fingerprints" : [ SHA_HEX_VALUE ] } } ]
Pole
relation
to tablica co najmniej 1 ciągu znaków opisujących deklarowaną relację. Aby zadeklarować, że aplikacje i witryny współdzielą dane logowania, określ relacje jakodelegate_permission/handle_all_urls
idelegate_permission/common.get_login_creds
.Pole
target
to obiekt określający zasób, do którego odnosi się deklaracja. Te pola identyfikują witrynę:namespace
web
site
Adres URL witryny w formacie
https://domain[:optional_port]
, na przykładhttps://www.example.com
.Wartość domain musi być w pełni kwalifikowana, a optional_port musi być pominięty, jeśli używasz portu 443 w przypadku HTTPS.
Elementem docelowym
site
może być tylko domena główna, więc nie możesz ograniczyć powiązania aplikacji do konkretnego podkatalogu. Nie umieszczaj w adresie URL ścieżki, np. ukośnika na końcu.Subdomeny nie są uważane za dopasowanie: jeśli jako
www.example.com
podasz domain, domenawww.counter.example.com
nie będzie powiązana z Twoją aplikacją.Te pola identyfikują aplikację na Androida:
namespace
android_app
package_name
Nazwa pakietu zadeklarowana w manifeście aplikacji. Na przykład: com.example.android
sha256_cert_fingerprints
Odciski cyfrowe SHA-256 certyfikatu podpisywania aplikacji. Umieść plik JSON protokołu Digital Asset Links w tej lokalizacji w domenie logowania:
https://domain[:optional_port]/.well-known/assetlinks.json
Jeśli na przykład Twoja domena logowania to
signin.example.com
, umieść plik JSON pod adresemhttps://signin.example.com/.well-known/assetlinks.json
.Typ MIME pliku Digital Asset Link musi być JSON. Upewnij się, że serwer wysyła w odpowiedzi nagłówek
Content-Type: application/json
.Upewnij się, że Twój dostawca hostingu pozwala Google na pobranie Twojego pliku Digital Asset Link. Jeśli masz plik
robots.txt
, musi on zezwalać agentowi Googlebot na pobieranie informacji z pliku/.well-known/assetlinks.json
. Większość witryn może zezwolić dowolnemu automatycznemu agentowi na pobieranie plików z ścieżki/.well-known/
, aby inne usługi mogły uzyskać dostęp do metadanych w tych plikach:User-agent: * Allow: /.well-known/
Dodaj do pliku manifestu w sekcji
<application>
ten wiersz:<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
Jeśli korzystasz z logowania za pomocą hasła w Menedżerze danych logowania, wykonaj ten krok, aby skonfigurować łączenie zasobów cyfrowych w pliku manifestu. Ten krok nie jest wymagany, jeśli używasz tylko kluczy dostępu.
Zadeklaruj powiązanie w aplikacji na Androida. Dodaj obiekt, który określa pliki
assetlinks.json
, które mają być wczytane. Musisz zmienić znaczenie apostrofu i cudzysłowów użytych w tym ciągu. Na przykład:<string name="asset_statements" translatable="false"> [{ \"include\": \"https://signin.example.com/.well-known/assetlinks.json\" }] </string>
> GET /.well-known/assetlinks.json HTTP/1.1 > User-Agent: curl/7.35.0 > Host: signin.example.com < HTTP/1.1 200 OK < Content-Type: application/json
Konfigurowanie Menedżera danych logowania
Aby skonfigurować i zainicjować obiekt CredentialManager
, dodaj logikę podobną do tej:
Kotlin
// Use your app or activity context to instantiate a client instance of // CredentialManager. val credentialManager = CredentialManager.create(context)
Java
// Use your app or activity context to instantiate a client instance of // CredentialManager. CredentialManager credentialManager = CredentialManager.create(context)
Wskazać pola danych logowania
W Androidzie 14 i nowszych można używać atrybutu isCredential
do wskazywania pól danych logowania, takich jak pola nazwy użytkownika lub hasła. Ten atrybut wskazuje, że ten widok jest polem danych logowania, które ma działać z Menedżerem danych logowania i zewnętrznymi dostawcami danych uwierzytelniających, jednocześnie pomagając usługom autouzupełniania w tworzeniu lepszych sugestii autouzupełniania. Gdy aplikacja korzysta z interfejsu API menedżera danych logowania, wyświetla się dolna karta menedżera danych logowania z dostępnymi danymi logowania. Nie trzeba już wyświetlać okna dialogowego autouzupełniania w przypadku nazwy użytkownika ani hasła. Podobnie nie musisz wyświetlać w przypadku haseł okna zapisywania danych autouzupełniania, ponieważ aplikacja poprosi o zapisanie danych za pomocą interfejsu Credential Manager API.
Aby używać atrybutu isCredential
, dodaj go do odpowiednich widoków:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isCredential="true"
...
/>
Logowanie użytkownika
Aby pobrać wszystkie opcje kluczy dostępu i haseł powiązane z kontem użytkownika:
Aby skonfigurować opcje uwierzytelniania za pomocą hasła i klucza:
Kotlin
// Retrieves the user's saved password for your app from their // password provider. val getPasswordOption = GetPasswordOption() // Get passkey from the user's public key credential provider. val getPublicKeyCredentialOption = GetPublicKeyCredentialOption( requestJson = requestJson )
Java
// Retrieves the user's saved password for your app from their // password provider. GetPasswordOption getPasswordOption = new GetPasswordOption(); // Get passkey from the user's public key credential provider. GetPublicKeyCredentialOption getPublicKeyCredentialOption = new GetPublicKeyCredentialOption(requestJson);
Użyj opcji pobranych z poprzedniego kroku, aby utworzyć żądanie logowania.
Kotlin
val getCredRequest = GetCredentialRequest( listOf(getPasswordOption, getPublicKeyCredentialOption) )
Java
GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder() .addCredentialOption(getPasswordOption) .addCredentialOption(getPublicKeyCredentialOption) .build();
Uruchom proces logowania:
Kotlin
coroutineScope.launch { try { val result = credentialManager.getCredential( // Use an activity-based context to avoid undefined system UI // launching behavior. context = activityContext, request = getCredRequest ) handleSignIn(result) } catch (e : GetCredentialException) { handleFailure(e) } } 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") } } }
Java
credentialManager.getCredentialAsync( // Use activity based context to avoid undefined // system UI launching behavior activity, getCredRequest, cancellationSignal, <executor>, new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() { @Override public void onResult(GetCredentialResponse result) { handleSignIn(result); } @Override public void onError(GetCredentialException e) { handleFailure(e); } } ); public void handleSignIn(GetCredentialResponse result) { // Handle the successfully returned credential. Credential credential = result.getCredential(); if (credential instanceof PublicKeyCredential) { String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson(); // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate } else if (credential instanceof PasswordCredential) { String username = ((PasswordCredential) credential).getId(); String password = ((PasswordCredential) credential).getPassword(); // Use id and password to send to your server to validate and authenticate } else if (credential instanceof CustomCredential) { if (ExampleCustomCredential.TYPE.equals(credential.getType())) { try { ExampleCustomCredential customCred = ExampleCustomCredential.createFrom(customCredential.getData()); // Extract the required credentials and complete the // authentication as per the federated sign in or any external // sign in library flow } catch (ExampleCustomCredential.ExampleCustomCredentialParsingException e) { // 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"); } }
Ten przykład pokazuje, jak sformatować żądanie JSON, gdy otrzymasz klucz dostępu:
{
"challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
"allowCredentials": [],
"timeout": 1800000,
"userVerification": "required",
"rpId": "credential-manager-app-test.glitch.me"
}
Poniższy przykład pokazuje, jak może wyglądać odpowiedź JSON po uzyskaniu danych logowania z kluczem publicznym:
{
"id": "KEDetxZcUfinhVi6Za5nZQ",
"type": "public-key",
"rawId": "KEDetxZcUfinhVi6Za5nZQ",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
"authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
"signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
"userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
}
}
Obsługa wyjątków, gdy nie ma dostępnych danych logowania
W niektórych przypadkach użytkownik może nie mieć żadnych danych uwierzytelniających lub nie wyrazić zgody na użycie dostępnych danych. Jeśli wywołana zostanie funkcja getCredential()
i nie zostanie znaleziony żaden identyfikator, zwracany jest parametr NoCredentialException
. W takim przypadku Twój kod powinien obsługiwać instancje NoCredentialException
.
Kotlin
try {
val credential = credentialManager.getCredential(credentialRequest)
} catch (e: NoCredentialException) {
Log.e("CredentialManager", "No credential available", e)
}
Java
try {
Credential credential = credentialManager.getCredential(credentialRequest);
} catch (NoCredentialException e) {
Log.e("CredentialManager", "No credential available", e);
}
W Androidzie 14 lub nowszym możesz skrócić opóźnienie wyświetlania selektora konta, używając metody prepareGetCredential()
przed wywołaniem metody getCredential()
.
Kotlin
val response = credentialManager.prepareGetCredential(
GetCredentialRequest(
listOf(
<getPublicKeyCredentialOption>,
<getPasswordOption>
)
)
}
Java
GetCredentialResponse response = credentialManager.prepareGetCredential(
new GetCredentialRequest(
Arrays.asList(
new PublicKeyCredentialOption(),
new PasswordOption()
)
)
);
Metoda prepareGetCredential()
nie wywołuje elementów interfejsu. Ułatwia tylko wykonywanie prac przygotowawczych, dzięki którym można później uruchomić pozostałą operację pobierania danych logowania (która obejmuje interfejsy użytkownika) za pomocą interfejsu API getCredential()
.
Dane z pamięci podręcznej są zwracane w obiekcie PrepareGetCredentialResponse
. Jeśli dane logowania są już dostępne, wyniki zostaną zapisane w pamięci podręcznej. Później możesz uruchomić pozostałą część interfejsu API getCredential()
, aby wyświetlić selektor kont z danymi z pamięci podręcznej.
Procesy rejestracji
Użytkownik może zarejestrować się do uwierzytelniania za pomocą klucza dostępu lub hasła.
Utwórz klucz dostępu
Aby umożliwić użytkownikom rejestrowanie kluczy dostępu i używanie ich do ponownego uwierzytelniania, zarejestruj dane uwierzytelniające użytkownika za pomocą obiektu CreatePublicKeyCredentialRequest
.
Kotlin
fun createPasskey(requestJson: String, preferImmediatelyAvailableCredentials: Boolean) { val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest( // Contains the request in JSON format. Uses the standard WebAuthn // web JSON spec. requestJson = requestJson, // Defines whether you prefer to use only immediately available // credentials, not hybrid credentials, to fulfill this request. // This value is false by default. preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials, ) // Execute CreateCredentialRequest asynchronously to register credentials // for a user account. Handle success and failure cases with the result and // exceptions, respectively. coroutineScope.launch { try { val result = credentialManager.createCredential( // Use an activity-based context to avoid undefined system // UI launching behavior context = activityContext, request = createPublicKeyCredentialRequest, ) handlePasskeyRegistrationResult(result) } catch (e : CreateCredentialException){ handleFailure(e) } } } fun handleFailure(e: CreateCredentialException) { when (e) { is CreatePublicKeyCredentialDomException -> { // Handle the passkey DOM errors thrown according to the // WebAuthn spec. handlePasskeyError(e.domError) } 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 CreateCredentialUnknownException -> ... 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}") } }
Java
public void createPasskey(String requestJson, boolean preferImmediatelyAvailableCredentials) { CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest = // `requestJson` contains the request in JSON format. Uses the standard // WebAuthn web JSON spec. // `preferImmediatelyAvailableCredentials` defines whether you prefer // to only use immediately available credentials, not hybrid credentials, // to fulfill this request. This value is false by default. new CreatePublicKeyCredentialRequest( requestJson, preferImmediatelyAvailableCredentials); // Execute CreateCredentialRequest asynchronously to register credentials // for a user account. Handle success and failure cases with the result and // exceptions, respectively. credentialManager.createCredentialAsync( // Use an activity-based context to avoid undefined system // UI launching behavior requireActivity(), createPublicKeyCredentialRequest, cancellationSignal, executor, new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() { @Override public void onResult(CreateCredentialResponse result) { handleSuccessfulCreatePasskeyResult(result); } @Override public void onError(CreateCredentialException e) { if (e instanceof CreatePublicKeyCredentialDomException) { // Handle the passkey DOM errors thrown according to the // WebAuthn spec. handlePasskeyError(((CreatePublicKeyCredentialDomException)e).getDomError()); } else if (e instanceof CreateCredentialCancellationException) { // The user intentionally canceled the operation and chose not // to register the credential. } else if (e instanceof CreateCredentialInterruptedException) { // Retry-able error. Consider retrying the call. } else if (e instanceof CreateCredentialProviderConfigurationException) { // Your app is missing the provider configuration dependency. // Most likely, you're missing the // "credentials-play-services-auth" module. } else if (e instanceof CreateCredentialUnknownException) { } else if (e instanceof 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.getClass().getName()); } } } ); }
Formatowanie żądania JSON
Po utworzeniu klucza dostępu musisz powiązać go z kontem użytkownika i zapisać klucz publiczny klucza dostępu na swoim serwerze. Poniższy przykład kodu pokazuje, jak sformatować żądanie JSON podczas tworzenia klucza dostępu.
W tym poście na blogu o wprowadzaniu płynnego uwierzytelniania w aplikacjach znajdziesz informacje o sformatowaniu żądania JSON podczas tworzenia kluczy dostępu i uwierzytelniania za ich pomocą. Dowiesz się też, dlaczego hasła nie są skutecznym rozwiązaniem do uwierzytelniania. Dowiesz się z niego też, jak wykorzystać istniejące dane uwierzytelniające biometryczne, jak powiązać aplikację z należącą do Ciebie stroną internetową, jak tworzyć klucze dostępu i jak uwierzytelniać się z wykorzystaniem kluczy dostępu.
{
"challenge": "abc123",
"rp": {
"name": "Credential Manager example",
"id": "credential-manager-test.example.com"
},
"user": {
"id": "def456",
"name": "helloandroid@gmail.com",
"displayName": "helloandroid@gmail.com"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
}
],
"timeout": 1800000,
"attestation": "none",
"excludeCredentials": [
{"id": "ghi789", "type": "public-key"},
{"id": "jkl012", "type": "public-key"}
],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
Ustawianie wartości atrybutu authenticatorAttachment
Parametr authenticatorAttachment
można ustawić tylko podczas tworzenia danych logowania. Możesz podać wartość platform
, cross-platform
lub nie podawać żadnej wartości. W większości przypadków zalecana jest wartość 0.
platform
: aby zarejestrować aktualne urządzenie użytkownika lub poprosić użytkownika o przejście na klucze dostępu po zalogowaniu się, ustaw wartośćauthenticatorAttachment
naplatform
.cross-platform
: ta wartość jest często używana podczas rejestrowania danych logowania wieloskładnikowych i nie jest używana w kontekście klucza dostępu.- Brak wartości: aby zapewnić użytkownikom elastyczność w tworzeniu kluczy dostępu na preferowanych urządzeniach (np. w ustawieniach konta), parametr
authenticatorAttachment
nie powinien być określony, gdy użytkownik zdecyduje się dodać klucz dostępu. W większości przypadków najlepszym rozwiązaniem będzie pozostawienie parametru nieokreślonego.
Zapobiegaj tworzeniu zduplikowanych kluczy dostępu
Wyświetl identyfikatory danych logowania w opcjonalnym tablicy excludeCredentials
, aby zapobiec tworzeniu nowego klucza dostępu, jeśli istnieje już klucz dostępu tego samego dostawcy.
Obsługa odpowiedzi JSON
Ten fragment kodu przedstawia przykładową odpowiedź JSON dotyczącą utworzenia danych logowania z kluczem publicznym. Dowiedz się więcej o obsłudze zwróconych danych uwierzytelniających klucza publicznego.
{
"id": "KEDetxZcUfinhVi6Za5nZQ",
"type": "public-key",
"rawId": "KEDetxZcUfinhVi6Za5nZQ",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
}
}
Weryfikacja pochodzenia w pliku danych klienta JSON
Wartość origin
reprezentuje aplikację lub witrynę, z której pochodzi żądanie, i jest używana przez klucze dostępu do ochrony przed atakami phishingowymi.
Serwer aplikacji musi sprawdzić źródło danych klienta na liście dozwolonych aplikacji i witryn. Jeśli serwer otrzyma żądanie z nierozpoznanego źródła, powinien je odrzucić.
W przypadku sieci origin
odzwierciedla to samo źródło witryny, w której zalogowano się przy użyciu danych logowania. Na przykład dla adresu URL https://www.example.com:8443/store?category=shoes#athletic
wartość origin
to https://www.example.com:8443
.
W przypadku aplikacji na Androida agent użytkownika automatycznie ustawia parametr origin
na podpis aplikacji wywołującej. Ten podpis powinien zostać zweryfikowany jako pasujący na Twoim serwerze, aby zweryfikować wywołującego interfejs API klucza dostępu. Nazwa Androida origin
to identyfikator URI utworzony na podstawie hasza SHA-256 certyfikatu podpisywania pliku APK, np.:
android:apk-key-hash:<sha256_hash-of-apk-signing-cert>
Skrót SHA-256 certyfikatów podpisywania z magazynu kluczy możesz znaleźć, uruchamiając w terminalu to polecenie:
keytool -list -keystore <path-to-apk-signing-keystore>
Hashe SHA-256 są w formacie szesnastkowym rozdzielanym dwukropkiem (91:F7:CB:F9:D6:81…
), a wartości origin
na Androidzie są zakodowane w base64url.
Ten przykładowy kod w Pythonie pokazuje, jak przekonwertować format skrótu na zgodny format szesnastkowy rozdzielany dwukropkiem:
import binascii
import base64
fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5'
print("android:apk-key-hash:" + base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))
Zastąp wartość fingerprint
własną wartością. Oto przykładowy wynik:
android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU
Następnie możesz dopasować ten ciąg jako dozwolone źródło na serwerze. Jeśli masz wiele certyfikatów podpisywania, np. certyfikatów do debugowania i wydania, lub wiele aplikacji, powtórz proces i zaakceptuj wszystkie te źródła jako prawidłowe na serwerze.
Zapisywanie hasła użytkownika
Jeśli użytkownik poda nazwę użytkownika i hasło w ramach procesu uwierzytelniania w aplikacji, możesz zarejestrować dane logowania użytkownika, których można użyć do jego uwierzytelnienia. Aby to zrobić, utwórz obiekt CreatePasswordRequest
:
Kotlin
fun registerPassword(username: String, password: String) { // Initialize a CreatePasswordRequest object. val createPasswordRequest = CreatePasswordRequest(id = username, password = password) // Create credential and handle result. coroutineScope.launch { try { val result = credentialManager.createCredential( // Use an activity based context to avoid undefined // system UI launching behavior. activityContext, createPasswordRequest ) handleRegisterPasswordResult(result) } catch (e: CreateCredentialException) { handleFailure(e) } } }
Java
void registerPassword(String username, String password) { // Initialize a CreatePasswordRequest object. CreatePasswordRequest createPasswordRequest = new CreatePasswordRequest(username, password); // Register the username and password. credentialManager.createCredentialAsync( // Use an activity-based context to avoid undefined // system UI launching behavior requireActivity(), createPasswordRequest, cancellationSignal, executor, new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() { @Override public void onResult(CreateCredentialResponse result) { handleResult(result); } @Override public void onError(CreateCredentialException e) { handleFailure(e); } } ); }
Pomoc dotycząca odzyskiwania danych logowania
Jeśli użytkownik nie ma już dostępu do urządzenia, na którym były przechowywane jego dane logowania, może być konieczne przywrócenie ich z bezpiecznej kopii zapasowej online. Więcej informacji o tym, jak obsługiwać ten proces odzyskiwania danych logowania, znajdziesz w sekcji „Odzyskiwanie dostępu lub dodawanie nowych urządzeń” w tym poście na blogu: Bezpieczeństwo kluczy dostępu w Menedżerze haseł Google.
Dodanie obsługi narzędzi do zarządzania hasłami za pomocą znanych adresów URL punktów końcowych kluczy dostępu
Aby zapewnić płynną integrację i przyszłą zgodność z narzędziami do zarządzania hasłami i danymi logowania, zalecamy dodanie obsługi dobrze znanych adresów URL punktów końcowych kluczy. Jest to otwarty protokół dla współpracujących podmiotów, które chcą formalnie reklamować obsługę kluczy dostępu i udostępniać bezpośrednie linki do rejestracji i zarządzania kluczami.
- W przypadku strony
https://example.com
, która ma stronę internetową oraz aplikacje na Androida i iOS, znany adres URL tohttps://example.com/.well-known/passkey-endpoints
. Gdy zapytanie dotyczy adresu URL, odpowiedź powinna być zgodna z tym schematem:
{ "enroll": "https://example.com/account/manage/passkeys/create" "manage": "https://example.com/account/manage/passkeys" }
Aby link otwierał się bezpośrednio w aplikacji, a nie w przeglądarce, użyj linków do aplikacji na Androida.
Więcej informacji znajdziesz w artykule passkey endpoints well-known URL na GitHub.
Pomagaj użytkownikom w zarządzaniu kluczami dostępu, pokazując, który dostawca je utworzył
Jednym z wyzwań, przed którymi stoją użytkownicy podczas zarządzania wieloma kluczami dostępu powiązanymi z daną aplikacją, jest identyfikacja odpowiedniego klucza dostępu do edycji lub usunięcia. Aby pomóc w rozwiązaniu tego problemu, zalecamy, aby aplikacje i witryny zawierały dodatkowe informacje, takie jak nazwa dostawcy, data utworzenia i data ostatniego użycia na liście kluczy dostępu na ekranie ustawień aplikacji.Informacje o dostawcy można uzyskać, analizując identyfikator AAGUID powiązany z odpowiednim kluczem dostępu. Identyfikator AAGUID można znaleźć w danych uwierzytelniających klucza dostępu.
Jeśli na przykład użytkownik utworzy klucz dostępu na urządzeniu z Androidem za pomocą Menedżera haseł Google, RP otrzyma identyfikator AAGUID podobny do tego: „ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4”. Uwierzyciel może dodać adnotację do klucza dostępu na liście kluczy dostępu, aby wskazać, że został on utworzony za pomocą Menedżera haseł Google.
Aby zmapować identyfikator AAGUID do dostawcy kluczy dostępu, dostawcy kluczy dostępu mogą skorzystać z repozytorium identyfikatorów AAGUID udostępnionego przez społeczność. Aby znaleźć nazwę i ikonę dostawcy kluczy dostępu, wyszukaj identyfikator AAGUID na liście.
Dowiedz się więcej o integracji z AAGUID.
Rozwiązywanie typowych problemów
Zapoznaj się z przewodnikiem rozwiązywania problemów z Menedżerem danych logowania, aby poznać typowe kody błędów, opisy i informacje o ich przyczynach.
Dodatkowe materiały
Więcej informacji o interfejsie Credential Manager API i kluczach dostępu znajdziesz w tych materiałach:
- Przewodnik po UX kluczy dostępu
- Film: jak ograniczyć używanie haseł w aplikacjach na Androida dzięki obsługi kluczy dostępu
- Ćwiczenie z programowania: jak uprościć ścieżki uwierzytelniania za pomocą interfejsu Credential Manager API w aplikacji na Androida
- Przykładowa aplikacja: CredentialManager