Pengelola Kredensial adalah API Jetpack yang mendukung beberapa metode login, seperti nama pengguna dan sandi, kunci sandi, serta solusi login gabungan (seperti Login dengan Google) dalam satu API sehingga menyederhanakan integrasi bagi developer.
Selain itu, untuk pengguna, Pengelola Kredensial menyatukan antarmuka login di seluruh metode autentikasi sehingga lebih jelas dan lebih mudah bagi pengguna untuk login ke aplikasi, terlepas dari metode yang mereka pilih.
Halaman ini menjelaskan konsep kunci sandi dan langkah-langkah dalam menerapkan dukungan sisi klien untuk solusi autentikasi, termasuk kunci sandi, menggunakan Credential Manager API. Ada juga halaman FAQ terpisah yang menyediakan jawaban untuk pertanyaan spesifik yang lebih mendetail.
Masukan Anda adalah bagian penting untuk meningkatkan Credential Manager API. Sampaikan masalah apa pun yang Anda temukan atau ide untuk meningkatkan API menggunakan link berikut:
Tentang kunci sandi
Kunci sandi adalah pengganti sandi yang lebih aman dan lebih mudah. Dengan kunci sandi, pengguna dapat login ke aplikasi dan situs menggunakan sensor biometrik (seperti sidik jari atau pengenalan wajah), PIN, atau pola. Hal ini memberikan pengalaman login yang lancar, sehingga pengguna tidak perlu mengingat nama pengguna atau sandi.
Kunci sandi mengandalkan WebAuthn (Web Authentication), sebuah standar yang dikembangkan bersama oleh FIDO Alliance dan World Wide Web Consortium (W3C). WebAuthn menggunakan kriptografi kunci publik untuk mengautentikasi pengguna. Situs atau aplikasi tempat pengguna login dapat melihat dan menyimpan kunci publik, tetapi tidak pernah bisa mengakses kunci pribadi. Kunci pribadi dirahasiakan dan aman. Dan karena kunci ini unik dan terikat dengan situs atau aplikasi, kunci sandi tidak dapat disalahgunakan sehingga menambah keamanan lebih lanjut.
Dengan Pengelola Kredensial, pengguna dapat membuat kunci sandi dan menyimpannya di Pengelola Sandi Google.
Baca Autentikasi pengguna dengan kunci sandi untuk mendapatkan panduan tentang cara menerapkannya alur autentikasi kunci sandi yang lancar dengan Pengelola Kredensial.
Prasyarat
Untuk menggunakan Pengelola Kredensial, selesaikan langkah-langkah di bagian ini.
Menggunakan versi platform terbaru
Pengelola Kredensial didukung di Android 4.4 (Level API 19) dan yang lebih tinggi.
Menambahkan dependensi ke aplikasi
Tambahkan dependensi berikut ke skrip build modul aplikasi Anda:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.5.0-alpha05") // 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-alpha05") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.5.0-alpha05" // 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-alpha05" }
Mempertahankan class dalam file ProGuard
Dalam file proguard-rules.pro
modul Anda, tambahkan perintah berikut:
-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
*;
}
Pelajari lebih lanjut cara menyingkat, meng-obfuscate, dan mengoptimalkan aplikasi Anda.
Menambahkan dukungan untuk Digital Asset Links
Guna mengaktifkan dukungan kunci sandi untuk aplikasi Android, kaitkan aplikasi Anda dengan situs yang dimiliki aplikasi Anda. Anda dapat mendeklarasikan pengaitan ini dengan menyelesaikan langkah-langkah berikut:
Membuat file JSON Digital Asset Links. Misalnya, untuk mendeklarasikan bahwa situs
https://signin.example.com
dan aplikasi Android dengan nama paketcom.example
dapat berbagi kredensial login, buat file bernamaassetlinks.json
dengan konten berikut:[ { "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 ] } } ]
Kolom
relation
adalah array dari satu atau beberapa string yang menjelaskan hubungan yang dideklarasikan. Untuk mendeklarasikan bahwa aplikasi dan situs berbagi kredensial login, tentukan hubungan sebagaidelegate_permission/handle_all_urls
dandelegate_permission/common.get_login_creds
.Kolom
target
adalah objek yang menentukan aset tempat deklarasi berlaku. Kolom berikut mengidentifikasi situs:namespace
web
site
URL situs, dalam format
https://domain[:optional_port]
; misalnya,https://www.example.com
.domain harus sepenuhnya memenuhi syarat, dan optional_port harus dihilangkan saat menggunakan port 443 untuk HTTPS.
Target
site
hanya dapat berupa domain root: Anda tidak dapat membatasi pengaitan aplikasi ke subdirektori tertentu. Jangan sertakan jalur dalam URL, seperti garis miring.Subdomain tidak dianggap cocok: yaitu, jika Anda menentukan domain sebagai
www.example.com
, domainwww.counter.example.com
tidak akan dikaitkan dengan aplikasi Anda.Kolom berikut mengidentifikasi aplikasi Android:
namespace
android_app
package_name
Nama paket yang dideklarasikan dalam manifes aplikasi. Misalnya, com.example.android
sha256_cert_fingerprints
Sidik jari SHA256 dari sertifikat penandatanganan aplikasi Anda. Menghosting file JSON Digital Assets Link di lokasi berikut pada domain login:
https://domain[:optional_port]/.well-known/assetlinks.json
Misalnya, jika domain login Anda adalah
signin.example.com
, hosting file JSON dihttps://signin.example.com/.well-known/assetlinks.json
.Jenis MIME untuk file Digital Assets Link harus berupa JSON. Pastikan server mengirimkan header
Content-Type: application/json
dalam respons.Pastikan host Anda mengizinkan Google untuk mengambil file Digital Asset Link. Jika Anda memiliki file
robots.txt
, file tersebut harus mengizinkan agen Googlebot untuk mengambil/.well-known/assetlinks.json
. Sebagian besar situs mengizinkan agen otomatis untuk mengambil file di jalur/.well-known/
sehingga layanan lain dapat mengakses' metadata dalam file tersebut:User-agent: * Allow: /.well-known/
Tambahkan baris berikut ke file manifes di bagian
<application>
:<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
Jika Anda menggunakan login sandi melalui Pengelola Kredensial, ikuti langkah ini untuk mengonfigurasi penautan aset digital dalam manifes. Langkah ini tidak diperlukan jika Anda hanya menggunakan kunci sandi.
Mendeklarasikan pengaitan di aplikasi Android. Tambahkan objek yang menentukan file
assetlinks.json
untuk dimuat. Anda harus meng-escape apostrof dan tanda kutip yang Anda gunakan dalam string. Misalnya:<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
Mengonfigurasi Pengelola Kredensial
Untuk mengonfigurasi dan melakukan inisialisasi objek CredentialManager
, tambahkan logika yang mirip dengan berikut ini:
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)
Menunjukkan kolom kredensial
Pada Android 14 dan yang lebih tinggi, atribut isCredential
dapat digunakan untuk
menunjukkan kolom kredensial, seperti kolom nama pengguna atau sandi. Atribut ini
menunjukkan bahwa tampilan ini adalah kolom kredensial yang dimaksudkan untuk berfungsi dengan
Pengelola Kredensial dan penyedia kredensial pihak ketiga, sekaligus membantu layanan isi otomatis
memberikan saran isi otomatis yang lebih baik. Saat aplikasi menggunakan Credential
Manager API, sheet bawah Pengelola Kredensial yang berisi kredensial yang tersedia
akan ditampilkan dan tidak perlu lagi menampilkan dialog pengisian isi otomatis untuk
nama pengguna atau sandi. Dengan cara yang sama, Anda tidak perlu
menampilkan tombol {i>autofill.<i}
dialog simpan untuk sandi, karena aplikasi akan meminta Credential Manager API
untuk menyimpan kredensial.
Untuk menggunakan atribut isCredential
, tambahkan atribut ke Tampilan yang relevan:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isCredential="true"
...
/>
Memproses login pengguna
Untuk mengambil semua opsi kunci sandi dan sandi yang dikaitkan dengan akun pengguna, selesaikan langkah-langkah berikut:
Lakukan inisialisasi opsi autentikasi sandi dan kunci sandi:
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);
Gunakan opsi yang diambil dari langkah sebelumnya untuk membangun permintaan login.
Kotlin
val getCredRequest = GetCredentialRequest( listOf(getPasswordOption, getPublicKeyCredentialOption) )
Java
GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder() .addCredentialOption(getPasswordOption) .addCredentialOption(getPublicKeyCredentialOption) .build();
Meluncurkan alur login:
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"); } }
Contoh berikut menunjukkan cara memformat permintaan JSON saat Anda mendapatkan kunci sandi:
{
"challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
"allowCredentials": [],
"timeout": 1800000,
"userVerification": "required",
"rpId": "credential-manager-app-test.glitch.me"
}
Contoh berikut menunjukkan tampilan respons JSON setelah Anda mendapatkan kredensial kunci publik:
{
"id": "KEDetxZcUfinhVi6Za5nZQ",
"type": "public-key",
"rawId": "KEDetxZcUfinhVi6Za5nZQ",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
"authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
"signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
"userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
}
}
Menangani pengecualian saat tidak ada kredensial yang tersedia
Dalam beberapa kasus, pengguna mungkin tidak memiliki kredensial apa pun yang tersedia, atau pengguna mungkin
tidak memberikan izin untuk menggunakan kredensial yang tersedia. Jika getCredential()
dipanggil dan tidak ada kredensial yang tidak ditemukan, NoCredentialException
akan ditampilkan. Jika hal ini terjadi, kode Anda harus menangani instance 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);
}
Di Android 14 atau yang lebih baru, Anda dapat mengurangi latensi saat menampilkan pemilih
akun dengan menggunakan metode prepareGetCredential()
sebelum memanggil
getCredential()
.
Kotlin
val response = credentialManager.prepareGetCredential(
GetCredentialRequest(
listOf(
<getPublicKeyCredentialOption>,
<getPasswordOption>
)
)
}
Java
GetCredentialResponse response = credentialManager.prepareGetCredential(
new GetCredentialRequest(
Arrays.asList(
new PublicKeyCredentialOption(),
new PasswordOption()
)
)
);
Metode prepareGetCredential()
tidak memanggil elemen UI. Langkah ini hanya membantu
Anda melakukan tugas persiapan sehingga nantinya Anda dapat meluncurkan operasi
get-credential yang tersisa (yang melibatkan UI) melalui getCredential()
API.
Data yang di-cache ditampilkan dalam objek PrepareGetCredentialResponse
. Jika
sudah ada kredensial, hasilnya akan disimpan dalam cache dan nantinya Anda dapat
meluncurkan API getCredential()
yang tersisa untuk memunculkan pemilih
akun dengan data yang di-cache.
Alur pendaftaran
Anda dapat mendaftarkan pengguna untuk autentikasi menggunakan kunci sandi atau sandi.
Membuat kunci sandi
Guna memberi pengguna pilihan untuk mendaftarkan kunci sandi dan menggunakannya untuk autentikasi ulang,
daftarkan kredensial pengguna menggunakan objek 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()); } } } ); }
Memformat permintaan JSON
Setelah membuat kunci sandi, Anda harus mengaitkannya dengan akun pengguna, dan menyimpan kunci publik kunci sandi tersebut di server Anda. Contoh kode berikut menunjukkan cara memformat permintaan JSON saat Anda membuat kunci sandi.
Postingan blog tentang cara menghadirkan autentikasi yang lancar ke aplikasi Anda menunjukkan cara memformat permintaan JSON saat membuat kunci sandi dan saat melakukan autentikasi menggunakan kunci sandi. Bagian ini juga menjelaskan alasan sandi tidak menjadi solusi autentikasi yang efektif, cara memanfaatkan kredensial biometrik yang ada, cara mengaitkan aplikasi dengan situs yang Anda miliki, cara membuat kunci sandi, dan cara mengautentikasi menggunakan kunci sandi.
{
"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"
}
}
Menyetel nilai untuk authenticatorAttachment
Parameter authenticatorAttachment
hanya dapat disetel pada waktu pembuatan
kredensial. Anda dapat menentukan platform
, cross-platform
, atau tanpa nilai. Pada sebagian besar kasus,
tidak ada nilai yang direkomendasikan.
platform
: Untuk mendaftarkan perangkat pengguna saat ini atau meminta pengguna sandi untuk mengupgrade ke kunci sandi setelah login, setelauthenticatorAttachment
keplatform
.cross-platform
: Nilai ini biasanya digunakan saat mendaftarkan kredensial multi-faktor dan tidak digunakan dalam konteks kunci sandi.- Tidak ada nilai: Untuk memberi pengguna fleksibilitas dalam membuat
kunci sandi di perangkat pilihan mereka (seperti di setelan akun), parameter
authenticatorAttachment
tidak boleh ditentukan jika pengguna memilih untuk menambahkan kunci sandi. Dalam sebagian besar kasus, tidak menentukan parameter adalah opsi terbaik.
Mencegah pembuatan kunci sandi duplikat
Mencantumkan ID kredensial dalam array excludeCredentials
opsional untuk mencegah
pembuatan kunci sandi baru jika sudah ada yang memiliki penyedia kunci sandi
yang sama.
Menangani respons JSON
Cuplikan kode berikut menunjukkan contoh respons JSON untuk membuat kredensial kunci publik. Pelajari lebih lanjut cara menangani kredensial kunci publik yang ditampilkan.
{
"id": "KEDetxZcUfinhVi6Za5nZQ",
"type": "public-key",
"rawId": "KEDetxZcUfinhVi6Za5nZQ",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
}
}
Memverifikasi origin dari JSON data klien
origin
mewakili aplikasi atau situs tempat
asal permintaan, dan digunakan oleh kunci sandi untuk melindungi dari serangan phishing.
Server aplikasi Anda diwajibkan untuk memeriksa origin
data klien terhadap daftar aplikasi dan situs yang disetujui. Jika server
menerima permintaan dari aplikasi atau situs dari origin yang tidak dikenal, permintaan
tersebut harus ditolak.
Dalam kasus Web, origin
mencerminkan origin situs yang sama tempat login kredensial. Misalnya, dengan URL
https://www.example.com:8443/store?category=shoes#athletic
, origin
adalah
https://www.example.com:8443
.
Untuk aplikasi Android, agen pengguna akan otomatis menetapkan origin
ke tanda tangan
aplikasi panggilan. Tanda tangan ini harus diverifikasi sebagai kecocokan di server Anda untuk
memvalidasi pemanggil API kunci sandi. origin
Android adalah URI yang berasal
dari hash SHA-256 sertifikat penandatanganan APK, seperti:
android:apk-key-hash:<sha256_hash-of-apk-signing-cert>
Hash SHA-256 sertifikat penandatanganan dari keystore dapat ditemukan dengan menjalankan perintah terminal berikut:
keytool -list -keystore <path-to-apk-signing-keystore>
Hash SHA-256 memiliki format heksadesimal yang dipisahkan titik dua
(91:F7:CB:F9:D6:81…
), dan nilai origin
Android dienkode menggunakan base64url.
Contoh Python ini menunjukkan cara mengonversi format hash ke format heksadesimal
kompatibel yang dipisahkan titik dua:
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('=', ''))
Ganti nilai fingerprint
dengan nilai Anda sendiri. Berikut contoh
hasilnya:
android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU
Anda kemudian dapat mencocokkan string tersebut sebagai origin yang diizinkan di server Anda. Jika Anda memiliki beberapa sertifikat penandatanganan, seperti sertifikat untuk proses debug dan rilis, atau beberapa aplikasi, ulangi proses dan setujui semua origin tersebut sebagai valid di server.
Menyimpan sandi pengguna
Jika pengguna memberikan nama pengguna dan sandi untuk alur autentikasi di
aplikasi, Anda dapat mendaftarkan kredensial pengguna yang dapat digunakan untuk mengautentikasi
pengguna. Untuk melakukannya, buat objek 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); } } ); }
Mendukung pemulihan kredensial
Jika pengguna tidak lagi memiliki akses ke perangkat tempat mereka menyimpan kredensial, mereka mungkin perlu melakukan pemulihan dari cadangan online yang aman. Untuk mempelajari lebih lanjut cara mendukung proses pemulihan kredensial ini, baca bagian yang berjudul "Memulihkan akses atau menambahkan perangkat baru" di postingan blog ini: Keamanan Kunci Sandi di Pengelola Sandi Google.
Menambahkan dukungan untuk alat pengelolaan sandi dengan URL populer endpoint kunci sandi
Untuk mendapatkan integrasi yang lancar dan kompatibilitas pada masa mendatang dengan alat pengelolaan sandi dan kredensial, sebaiknya tambahkan dukungan untuk URL populer endpoint kunci sandi. Hal ini merupakan protokol terbuka bagi pihak terkait untuk secara resmi mengiklankan dukungan mereka terhadap kunci sandi dan memberikan link langsung untuk pendaftaran dan pengelolaan kunci sandi.
- Untuk pihak tepercaya di
https://example.com
, yang memiliki situs plus aplikasi Android dan iOS, URL populernya adalahhttps://example.com/.well-known/passkey-endpoints
. Saat URL dikueri, responsnya harus menggunakan skema berikut
{ "enroll": "https://example.com/account/manage/passkeys/create" "manage": "https://example.com/account/manage/passkeys" }
Agar link ini terbuka langsung di aplikasi dan bukan di web, gunakan link aplikasi Android.
Detail selengkapnya dapat ditemukan di penjelasan URL populer endpoint kunci sandi di GitHub.
Membantu pengguna mengelola kunci sandi mereka dengan menampilkan penyedia yang membuatnya
Satu tantangan yang dihadapi pengguna saat mengelola beberapa kunci sandi yang terkait dengan aplikasi mengidentifikasi kunci sandi yang benar untuk diedit atau dihapus. Untuk membantu masalah ini, sebaiknya aplikasi dan situs web menyertakan informasi seperti penyedia yang membuat kredensial, tanggal pembuatan dan tanggal terakhir digunakan dalam daftar kunci sandi di layar setelan aplikasi Anda.Penyedia diperoleh dengan memeriksa AAGUID yang terkait dengan kunci sandi yang sesuai. AAGUID dapat ditemukan sebagai bagian dari data pengautentikasi kunci sandi.
Misalnya, jika pengguna membuat kunci sandi di perangkat yang menjalankan Android menggunakan Pengelola Sandi Google, RP kemudian akan menerima AAGUID yang terlihat seperti ini: "ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4". Pihak tepercaya dapat menambahkan anotasi pada kunci sandi dalam daftar kunci sandi untuk menunjukkan bahwa kunci sandi tersebut dibuat menggunakan Pengelola Sandi Google.
Untuk memetakan AAGUID ke penyedia kunci sandi, RP dapat menggunakan repositori AAGUID yang bersumber dari komunitas. Cari AAGUID di daftar untuk menemukan nama dan ikon penyedia kunci sandi.
Baca selengkapnya tentang Integrasi AAGUID.
Memecahkan masalah error umum
Tabel berikut menampilkan beberapa kode dan deskripsi error umum, serta memberikan beberapa informasi terkait penyebabnya:
Kode dan deskripsi error | Penyebab |
---|---|
Kegagalan Login Awal: 16: Pemanggil diblokir untuk sementara karena terlalu banyak perintah login yang dibatalkan. | Jika mengalami periode tunggu 24 jam ini selama pengembangan, Anda dapat meresetnya dengan menghapus penyimpanan aplikasi layanan Google Play. Atau, untuk mengganti waktu tunggu ini pada emulator atau perangkat pengujian, buka
aplikasi Telepon dan masukkan kode berikut:
|
Kegagalan Login Awal: 8: Error internal tidak diketahui. |
|
CreatePublicKeyCredentialDomException: Permintaan masuk tidak dapat divalidasi | ID paket aplikasi tidak terdaftar di server Anda. Validasi hal ini dalam integrasi sisi server Anda. |
CreateCredentialUnknownException: Selama proses penyimpanan sandi, ditemukan respons kegagalan sandi dari sekali ketuk 16: Melewatkan penyimpanan sandi karena pengguna kemungkinan diminta menggunakan fitur Isi Otomatis Android | Error ini hanya terjadi di Android 13 dan yang lebih rendah, dan hanya jika Google adalah penyedia Isi Otomatis. Dalam hal ini, pengguna akan melihat perintah simpan dari Isi Otomatis dan sandi akan disimpan ke Pengelola Sandi Google. Perlu diketahui bahwa kredensial yang disimpan menggunakan Isi Otomatis dengan Google dibagikan secara dua arah dengan Credential Manager API. Oleh karena itu, error ini dapat diabaikan dengan aman. |
Referensi lainnya
Untuk mempelajari lebih lanjut Credential Manager API dan kunci sandi, lihat referensi berikut:
- Panduan UX Kunci Sandi
- Video: Cara mengurangi ketergantungan pada sandi di aplikasi Android dengan dukungan kunci sandi
- Codelab: Pelajari cara menyederhanakan perjalanan autentikasi menggunakan Credential Manager API di aplikasi Android
- Aplikasi contoh: CredentialManager