Halaman ini menjelaskan cara membuat, login dengan, dan menghapus kunci pemulihan.
Keterampilan Android
Lihat di GitHubRestore Credentials
android skills add restore-credentialsKompatibilitas versi
Restore Credentials dari Credential Manager berfungsi di perangkat yang menjalankan Android 9 (API
level 28) dan yang lebih tinggi, layanan Google Play (GMS) core versi 242200000
(v24.22) atau yang lebih tinggi, dan library androidx.credentials versi 1.5.0 atau yang lebih tinggi. Di perangkat tanpa layanan Google Play atau dengan versi GMS yang lebih lama,
tangkap CreateCredentialProviderConfigurationException dan
GetCredentialProviderConfigurationException agar aplikasi Anda beralih dengan lancar
ke login standar.
Prasyarat
Konfigurasikan server pihak tepercaya WebAuthn yang mirip dengan server untuk kunci sandi. Jika Anda sudah menyiapkan server untuk menangani autentikasi dengan kunci sandi, Anda dapat menggunakan kembali endpoint pendaftaran dan pernyataan WebAuthn yang sama untuk kunci pemulihan.
Dependensi
Tambahkan dependensi berikut ke file build.gradle modul aplikasi Anda:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha02") implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha02" implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02" }
Restore Credentials tersedia dari library androidx.credentials versi 1.5.0 dan yang lebih tinggi. Namun, sebaiknya gunakan versi stabil terbaru dari dependensi jika memungkinkan.
Pengaruh android:allowBackup terhadap Restore Credentials
Atribut manifes android:allowBackup mengontrol pencadangan data aplikasi,
yang berinteraksi dengan Restore Credentials dalam tiga cara berbeda:
- Transfer kunci pemulihan (independen
allowBackup): Layanan Google Play mentransfer pasangan kunciRestoreCredentialmenggunakan pencadangan cloud terenkripsi end-to-end atau transfer antarperangkat (D2D) lokal, terlepas dari apakahandroid:allowBackupadalahtrueataufalse. Untuk transfer D2D, kunci pemulihan selalu ditransfer; satu-satunya kondisi yang tidak memungkinkan transfer adalah selama pencadangan cloud saat pengguna memilih untuk tidak menggunakan pencadangan cloud di Setelan Android. - Pemulihan latar belakang di
BackupAgent(memerlukanandroid:allowBackup="true"): Login latar belakang otomatis sebelum pengguna membuka aplikasi di perangkat baru (BackupAgent.onRestoreFinished()) hanya berjalan saatandroid:allowBackup="true"danandroid:backupAgentdikonfigurasi dalam manifes Anda. Jikaandroid:allowBackup="false", pulihkan sesi saat pengguna pertama kali meluncurkanActivityutama Anda. Anda juga dapat menerapkan kedua pendekatan tersebut untuk membuat pemulihan lebih andal. - Pengujian Pencadangan Cloud Android Studio (memerlukan
android:allowBackup="true"): Menguji jenis pencadangan Cloud di alat Pencadangan dan Pemulihan Android Studio memerlukanandroid:allowBackup="true", sedangkan pengujian Perangkat ke Perangkat berfungsi terlepas dariallowBackup.
Ringkasan
Restore Credentials mengikuti siklus proses tiga tahap di seluruh perangkat lama dan baru pengguna:
- Membuat kunci pemulihan di perangkat sebelumnya: Setelah pengguna login (atau saat aplikasi dimulai jika pengguna sudah login dan tidak ada kunci pemulihan yang
dicatat di penyimpanan lokal), ambil opsi pembuatan dari server pihak tepercaya
Anda dan panggil
credentialManager.createCredential(). - Login dengan kunci pemulihan di perangkat baru: Ambil
RestoreCredentialyang ditransfer menggunakan strategi dua tingkat: pertama, secara otomatis tanpa gangguan diBackupAgent.onRestoreFinished()(saatandroid:allowBackup="true") sehingga sinkronisasi latar belakang dan notifikasi push segera dilanjutkan, dan kedua, sebagai penggantian di peluncurActivity.onCreate()saat aplikasi pertama kali diluncurkan. - Hapus kunci pemulihan saat logout: Saat pengguna logout atau
sesinya dibatalkan oleh server, panggil
credentialManager.clearCredentialState()denganClearCredentialStateRequest(ClearCredentialStateRequest.TYPE_CLEAR_RESTORE_CREDENTIAL)dan hapus flag sinkronisasi lokal Anda.
Membuat kunci pemulihan
Aplikasi Anda harus mencakup semua kasus login pengguna untuk memastikan pengguna aktif memiliki kunci pemulihan yang dibuat. Buat kunci pemulihan dalam skenario berikut:
- Jika pengguna login dan kunci pemulihan belum dibuat (seperti dalam
metode
onCreate()untukActivityutama). - Saat pengguna login atau menyelesaikan alur pendaftaran akun baru.
Untuk menghindari pemanggilan createCredential() pada setiap peluncuran aplikasi, simpan flag sinkronisasi lokal seperti has_synced_restore_credential di dalam penyimpanan lokal. Jangan simpan
flag ini di SharedPreferences atau DataStore yang dicadangkan secara default, kecuali jika file tersebut
dikecualikan di data_extraction_rules.xml; jika tidak, Pencadangan Otomatis akan menyalin
has_synced_restore_credential = true ke perangkat baru dan mencegah perangkat baru
membuat atau merotasi kunci pemulihannya sendiri.
Membuat instance Credential Manager
Gunakan konteks aktivitas aplikasi Anda untuk membuat instance objek CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
Mendapatkan opsi pembuatan kredensial dari server aplikasi Anda
Gunakan library yang mematuhi FIDO di server aplikasi Anda untuk mengirim JSON PublicKeyCredentialCreationOptions yang diperlukan untuk membuat kredensial pemulihan ke aplikasi klien Anda. Untuk mengetahui informasi selengkapnya tentang penerapan sisi server, lihat
Panduan sisi server.
JSON opsi pembuatan mengikuti struktur yang sama dengan pendaftaran kunci sandi:
{
"challenge": "<base64url-encoded challenge>",
"rp": {
"name": "<relying party name>",
"id": "<relying party host name>"
},
"user": {
"id": "<base64url-encoded user ID>",
"name": "<user name>",
"displayName": "<user display name>"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"attestation": "none",
"excludeCredentials": [
{
"id": "<base64url-encoded credential ID to exclude>",
"type": "public-key"
}
],
"authenticatorSelection": {
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
Pastikan JSON opsi pembuatan Anda memenuhi persyaratan berikut:
challengedanuser.id: Enkode kedua nilai sebagai string Base64URL tanpa padding (mengganti+dengan-,/dengan_, dan menghapus=di akhir). Karakter Base64 standar atau ID bilangan bulat mentah menyebabkanCreateRestoreCredentialDomExceptionatauIllegalArgumentException.rp.id: Tetapkan ke domain pihak tepercaya Anda (misalnya,example.com).pubKeyCredParams: Sertakan{"type": "public-key", "alg": -7}(ES256) dan{"type": "public-key", "alg": -257}(RS256).
Membuat kunci pemulihan
Setelah menerima JSON opsi pembuatan kunci publik dari server Anda, gabungkan
JSON dalam objek CreateRestoreCredentialRequest dan panggil
createCredential() pada objek CredentialManager.
// createRestoreRequest contains the details sent by the server
val response = credentialManager.createCredential(context, createRestoreRequest)
Poin penting tentang kode
Objek
CreateRestoreCredentialRequestberisi kolom-kolom berikut:requestJson: Opsi pembuatan kredensial yang dikirim oleh server aplikasi dalam format Web Authentication API untukPublicKeyCredentialCreationOptionsJSON.isCloudBackupEnabled: KolomBooleanuntuk menentukan apakah kunci pemulihan harus dicadangkan ke cloud. Secara default, flag ini adalahtrue. Kolom ini memiliki nilai berikut:true: (Direkomendasikan) Nilai ini memungkinkan pencadangan kunci pemulihan ke cloud jika pengguna telah mengaktifkan Pencadangan Google dan enkripsi end-to-end, seperti kunci layar.false: Nilai ini menyimpan kunci secara lokal dan bukan di cloud. Kunci tidak tersedia di perangkat baru jika pengguna memilih untuk memulihkan dari cloud, tetapi masih ditransfer selama penyiapan device-to-device (D2D) kabel lokal atau Wi-Fi.
Menangani respons pembuatan kredensial
Credential Manager API menampilkan respons jenis
CreateRestoreCredentialResponse. Respons ini menyimpan respons pendaftaran kredensial kunci publik dalam format JSON.
Kirim response.registrationResponseJson dari aplikasi Anda ke server pihak tepercaya
untuk memverifikasi dan menyimpan kunci publik bersama akun pengguna, lalu
catat penanda sinkronisasi lokal Anda di penyimpanan lokal. Untuk mengetahui informasi selengkapnya tentang
verifikasi sisi server, lihat panduan untuk kunci sandi.
Selama proses pembuatan kunci pemulihan, tangani pengecualian berikut:
CreateRestoreCredentialDomException: Pengecualian ini terjadi jikarequestJsontidak valid dan tidak mengikuti format WebAuthn untukPublicKeyCredentialCreationOptionsJSON.E2eeUnavailableException: Pengecualian ini terjadi jikaisCloudBackupEnabledadalahtrue, tetapi perangkat pengguna tidak memiliki pencadangan data atau enkripsi end-to-end, seperti kunci layar.
Untuk memastikan Kredensial Pemulihan dibuat untuk transfer perangkat ke perangkat (D2D) lokal meskipun E2EE cloud tidak tersedia, tangkapE2eeUnavailableExceptiondan segera coba lagicreateCredential()denganCreateRestoreCredentialRequest(requestJson, isCloudBackupEnabled = false).IllegalArgumentException: Pengecualian ini terjadi jikarequestJsonkosong, bukan JSON yang valid, atau tidak memilikiuser.idberenkode Base64URL yang valid dan sesuai dengan spesifikasi WebAuthn.
Login dengan kunci pemulihan
Gunakan Restore Credentials untuk otomatis login pengguna tanpa gangguan selama proses penyiapan perangkat.
Mendapatkan opsi pengambilan kredensial dari server aplikasi
Sebelum memanggil getCredential() di perangkat baru, minta verifikasi login JSON PublicKeyCredentialRequestOptions WebAuthn dari server aplikasi Anda. Karena
aplikasi di perangkat baru belum mengetahui pengguna mana yang login, endpoint
ini harus tidak diautentikasi (penemuan tanpa pengguna). Untuk panduan server terkait, lihat Login dengan kunci sandi dan panduan autentikasi sisi server.
JSON opsi pengambilan memiliki struktur berikut:
{
"challenge": "<your app challenge>",
"allowCredentials": [],
"rpId": "<your app server domain>"
}
Konfigurasikan JSON opsi pengambilan di server Anda sebagai berikut:
allowCredentials: Hapus kolom ini atau teruskan array kosong ([]) karena akun pengguna tidak diketahui sebelum pemulihan. SetelahgetCredential()menampilkan hasil, server Anda akan mengidentifikasi pengguna dari kolomresponse.userHandle(yang cocok denganuser.idyang didaftarkan selama pembuatan) dan mencari kunci publik berdasarkanid(credentialId).userVerification: Tetapkan ke"discouraged"(atau"preferred"). Karena Kredensial Pemulihan berjalan secara otomatis tanpa UI biometrik atau kunci layar, Android mengganti"required"menjadi"discouraged", dan data pengautentikasi yang ditampilkan ke server Anda memiliki flag Pengguna Terverifikasi (uv) yang ditetapkan kefalse. Konfigurasikan pemverifikasi server WebAuthn Anda untuk menerimauv = falseuntuk pernyataan kunci pemulihan.
Mendapatkan kunci pemulihan
Untuk mendapatkan kunci pemulihan di perangkat baru, gabungkan requestJson server dalam
GetRestoreCredentialOption di dalam GetCredentialRequest dan panggil
getCredential() pada objek CredentialManager.
Terapkan strategi pemulihan dua tingkat agar pengguna login, baik pemulihan data aplikasi latar belakang berjalan atau tidak:
- Tingkat 1 (Pemulihan latar belakang di
BackupAgent.onRestoreFinished()): Jika pencadangan data aplikasi diaktifkan (android:allowBackup="true"), buat subclassBackupAgentdan gantionRestoreFinished()(jangan gunakanonRestore(), yang hanya dipanggil untuk pencadangan nilai kunci lama). KarenaonRestoreFinished()bersifat sinkron dangetCredential()adalah fungsisuspendKotlin, hubungkan panggilan menggunakanrunBlocking(Dispatchers.IO). MenangkapNoCredentialExceptionsecara diam-diam saat tidak ada kunci pemulihan. - Tingkat 2 (Penggantian latar depan di peluncur
Activity.onCreate()): Saat aplikasi pertama kali diluncurkan dan tidak ada sesi lokal, panggilgetCredential()darilifecycleScopesebelum menampilkan layar login. Tindakan ini menangani aplikasi denganandroid:allowBackup="false"dan memulihkan jika perangkat offline saatonRestoreFinished()berjalan.
Jika Anda menambahkan BackupAgent baru ke aplikasi yang sudah menggunakan Pencadangan Otomatis dengan
android:allowBackup="true", tetapkan juga android:fullBackupOnly="true" pada
elemen <application> di AndroidManifest.xml agar Android terus menggunakan
Pencadangan Otomatis, bukan beralih ke pencadangan nilai kunci lama.
// Fetch the options required to get the restore key
val authenticationJson = fetchAuthenticationJson()
// Create the GetRestoreCredentialRequest object
val options = GetRestoreCredentialOption(authenticationJson)
val getRequest = GetCredentialRequest(listOf(options))
val response = credentialManager.getCredential(context, getRequest)
// Type-check and extract the restore credential
val credential = response.credential as RestoreCredential
Credential Manager API menampilkan GetCredentialResponse yang properti credential-nya adalah RestoreCredential (bukan PublicKeyCredential) yang berisi authenticationResponseJson.
Menangani respons login
Kirim restoreCredential.authenticationResponseJson dari aplikasi ke server pihak tepercaya Anda untuk memverifikasi pernyataan kriptografis dan menerbitkan token sesi baru untuk pengguna. Untuk mengetahui detail verifikasi sisi server, lihat Login dengan
kunci sandi.
Saat mendesain penyimpanan backend dan kebijakan siklus proses untuk kunci pemulihan, ikuti panduan berikut:
- Bedakan kunci pemulihan dari kunci sandi pengguna: Beri tag pada kunci pemulihan di database Anda agar disembunyikan dari layar pengelolaan kunci sandi yang ditampilkan kepada pengguna dan dikecualikan dari pemeriksaan verifikasi pengguna (
uv = true). - Mendukung beberapa perangkat aktif per pengguna: Seorang pengguna dapat memiliki beberapa perangkat (seperti ponsel dan tablet). Izinkan beberapa kunci pemulihan aktif per akun pengguna (misalnya, diberi kunci berdasarkan ID perangkat atau penginstalan), bukan menimpa satu baris.
- Tetapkan TTL yang sesuai dan bersihkan kunci tanpa induk: Meng-uninstal aplikasi atau menghapus data aplikasi akan menghapus kunci pemulihan lokal tanpa memberi tahu server Anda. Pertahankan kunci pemulihan cukup lama agar dapat bertahan selama periode upgrade perangkat (meskipun pengguna logout di perangkat lama setelah mentransfer), dan pangkas kunci yang tidak berlaku berdasarkan TTL tidak aktif atau batas kunci per pengguna.
Menghapus kunci pemulihan
Credential Manager bersifat stateless dan tidak mengetahui aktivitas pengguna, sehingga tidak
otomatis menghapus kunci pemulihan setelah digunakan. Untuk menghapus kunci pemulihan, panggil
metode clearCredentialState(). Demi keamanan, hapus kunci setiap kali
pengguna logout agar mereka tidak otomatis login kembali saat aplikasi diluncurkan berikutnya.
Kunci pemulihan hanya dihapus dalam situasi berikut:
- Tindakan tingkat sistem: Pengguna meng-uninstal aplikasi atau menghapus datanya.
- Panggilan tingkat aplikasi: Hapus kunci secara terprogram dengan memanggil
clearCredentialState()saat menangani logout pengguna dalam kode aplikasi Anda.
Karena ClearCredentialStateRequest() default hanya menghapus status sesi standar dan membiarkan kunci pemulihan tetap utuh, lakukan penghapusan ganda saat logout:
- Panggil
credentialManager.clearCredentialState(ClearCredentialStateRequest())untuk menghapus status sesi penyedia kredensial yang aktif. - Panggil
credentialManager.clearCredentialState()denganClearCredentialStateRequest(ClearCredentialStateRequest.TYPE_CLEAR_RESTORE_CREDENTIAL)untuk menghapusRestoreCredentialyang disimpan. - Hapus penanda
has_synced_restore_credentiallokal Anda dari penyimpanan lokal.
// Create a ClearCredentialStateRequest object
val clearRequest = ClearCredentialStateRequest(TYPE_CLEAR_RESTORE_CREDENTIAL)
// When the user logs out, delete the restore key
val response = credentialManager.clearCredentialState(clearRequest)
Langkah berikutnya
- Menguji Restore Credentials: Menguji alur pemulihan cloud dan perangkat ke perangkat (D2D) menggunakan Android Studio atau
adb shell bmgr. - Pengujian pencadangan dan pemulihan otomatis: Tulis pengujian host JUnit 5
dengan
BackupRestoreControlleruntuk memverifikasi pemulihanCLOUD_ENCRYPTEDdanDEVICE_TO_DEVICEdi CI.