Dengan dukungan untuk kunci sandi, login gabungan, dan penyedia autentikasi pihak ketiga, Pengelola Kredensial adalah API yang direkomendasikan untuk autentikasi di Android yang menyediakan lingkungan yang aman dan nyaman, yang memungkinkan pengguna menyinkronkan dan mengelola kredensial mereka. Untuk developer yang menggunakan kredensial FIDO2 lokal, Anda harus mengupdate aplikasi untuk mendukung autentikasi kunci sandi melalui integrasi dengan Credential Manager API. Dokumen ini menjelaskan cara memigrasikan project Anda dari FIDO2 ke Pengelola Kredensial.
Alasan bermigrasi dari FIDO2 ke Pengelola Kredensial
Dalam sebagian besar kasus, Anda harus memigrasikan penyedia autentikasi aplikasi Android ke Pengelola Kredensial. Alasan untuk bermigrasi ke Pengelola Kredensial mencakup:
- Dukungan kunci sandi: Pengelola Kredensial mendukung kunci sandi, yaitu mekanisme autentikasi baru tanpa sandi yang lebih aman dan lebih mudah digunakan daripada sandi.
- Metode multi-login: Pengelola Kredensial mendukung metode multi-login, termasuk sandi, kunci sandi, dan metode login gabungan. Hal ini memudahkan pengguna untuk melakukan autentikasi ke aplikasi Anda, apa pun metode autentikasi pilihan mereka.
- Dukungan penyedia kredensial pihak ketiga: Di Android 14 dan yang lebih baru, Pengelola Kredensial mendukung beberapa penyedia kredensial pihak ketiga. Artinya, pengguna Anda dapat menggunakan kredensial yang sudah ada dari penyedia lain untuk login ke aplikasi Anda.
- Pengalaman pengguna yang konsisten: Pengelola Kredensial memberikan pengalaman pengguna yang lebih konsisten untuk autentikasi di berbagai aplikasi dan mekanisme login. Hal ini memudahkan pengguna untuk memahami dan menggunakan alur autentikasi aplikasi Anda.
Untuk memulai migrasi dari FIDO2 ke Pengelola Kredensial, ikuti langkah-langkah di bawah ini.
Memperbarui dependensi
Update plugin Kotlin di build.gradle project Anda ke versi 1.8.10 atau yang lebih baru.
plugins { //… id 'org.jetbrains.kotlin.android' version '1.8.10' apply false //… }
Dalam build.gradle project, perbarui dependensi untuk menggunakan Pengelola Kredensial dan Autentikasi Layanan 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>' // ... }
Ganti inisialisasi FIDO dengan inisialisasi Pengelola Kredensial. Tambahkan deklarasi ini di class yang Anda gunakan untuk metode pembuatan kunci sandi dan login:
val credMan = CredentialManager.create(context)
Membuat kunci sandi
Anda harus membuat kunci sandi baru, mengaitkannya dengan akun pengguna, dan menyimpan kunci publik kunci sandi tersebut di server Anda sebelum pengguna dapat login dengan kunci tersebut. Siapkan aplikasi Anda dengan kemampuan ini dengan mengupdate panggilan fungsi register.
Untuk mendapatkan parameter yang diperlukan yang dikirim ke metode
createCredential()
selama pembuatan kunci sandi, tambahkanname("residentKey").value("required")
seperti yang dijelaskan dalam spesifikasi WebAuthn keregisterRequest()
panggilan server.suspend fun registerRequest(sessionId: String ... { // ... .method("POST", jsonRequestBody { name("attestation").value("none") name("authenticatorSelection").objectValue { name("residentKey").value("required") } }).build() // ... }
Tetapkan jenis
return
untukregisterRequest()
dan semua fungsi turunan keJSONObject
.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") ) } }
Hapus dengan aman metode apa pun yang menangani peluncur intent dan panggilan hasil aktivitas dari tampilan Anda.
Karena
registerRequest()
sekarang menampilkanJSONObject
, Anda tidak perlu membuatPendingIntent
. Ganti intent yang ditampilkan denganJSONObject
. Update panggilan peluncur intent Anda untuk memanggilcreateCredential()
dari Credential Manager API. Panggil metodecreateCredential()
API.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 }
Setelah panggilan berhasil, kirim respons kembali ke server. Permintaan dan respons untuk panggilan ini mirip dengan implementasi FIDO2, sehingga tidak diperlukan perubahan.
Mengautentikasi dengan kunci sandi
Setelah menyiapkan pembuatan kunci sandi, Anda dapat menyiapkan aplikasi untuk mengizinkan pengguna login dan melakukan autentikasi menggunakan kunci sandi mereka. Untuk melakukannya, perbarui kode autentikasi untuk menangani hasil Pengelola Kredensial dan terapkan fungsi untuk melakukan autentikasi melalui kunci sandi.
- Panggilan permintaan login ke server untuk mendapatkan informasi yang diperlukan agar
dikirim ke permintaan
getCredential()
sama dengan penerapan FIDO2. Perubahan tidak diperlukan. Mirip dengan panggilan permintaan pendaftaran, respons yang ditampilkan memiliki format 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") { } }
Hapus dengan aman metode apa pun yang menangani peluncur intent dan panggilan hasil aktivitas dari tampilan Anda.
Karena
signInRequest()
sekarang menampilkanJSONObject
, Anda tidak perlu membuatPendingIntent
. Ganti intent yang ditampilkan denganJSONObject
, dan panggilgetCredential()
dari metode API Anda.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 }
Setelah panggilan berhasil, kirim respons kembali ke server untuk memvalidasi dan mengautentikasi pengguna. Parameter permintaan dan respons untuk panggilan API ini mirip dengan implementasi FIDO2, sehingga tidak diperlukan perubahan.
Referensi lainnya
- Referensi Contoh Pengelola Kredensial
- Codelab Pengelola Kredensial
- Menghadirkan autentikasi lancar ke aplikasi Anda dengan kunci sandi menggunakan Credential Manager API
- Codelab FIDO2