Credential Manager เป็น API ที่แนะนําสําหรับการตรวจสอบสิทธิ์ใน Android เนื่องจากรองรับพาสคีย์ การลงชื่อเข้าใช้แบบรวมศูนย์ และผู้ให้บริการตรวจสอบสิทธิ์บุคคลที่สาม ซึ่งจะมอบสภาพแวดล้อมที่ปลอดภัยและสะดวกสําหรับให้ผู้ใช้ซิงค์และจัดการข้อมูลเข้าสู่ระบบ สําหรับนักพัฒนาซอฟต์แวร์ที่ใช้ข้อมูลเข้าสู่ระบบ FIDO2 ในเครื่อง คุณควรอัปเดตแอปให้รองรับการตรวจสอบสิทธิ์ด้วยพาสคีย์โดยการผสานรวมกับ Credential Manager API เอกสารนี้อธิบายวิธีย้ายข้อมูลโปรเจ็กต์จาก FIDO2 ไปยัง Credential Manager
เหตุผลที่ย้ายข้อมูลจาก FIDO2 ไปยังเครื่องมือจัดการข้อมูลเข้าสู่ระบบ
ในกรณีส่วนใหญ่ คุณควรย้ายข้อมูลผู้ให้บริการการตรวจสอบสิทธิ์ของแอป Android ไปยังเครื่องมือจัดการข้อมูลเข้าสู่ระบบ เหตุผลที่ควรย้ายข้อมูลไปยังเครื่องมือจัดการข้อมูลเข้าสู่ระบบมีดังนี้
- การรองรับพาสคีย์: เครื่องมือจัดการข้อมูลเข้าสู่ระบบรองรับพาสคีย์ ซึ่งเป็นกลไกการตรวจสอบสิทธิ์แบบใหม่ที่ไม่มีรหัสผ่านที่ปลอดภัยและใช้งานง่ายกว่ารหัสผ่าน
- วิธีการลงชื่อเข้าใช้หลายบัญชี: เครื่องมือจัดการข้อมูลเข้าสู่ระบบรองรับการลงชื่อเข้าใช้หลายวิธี ซึ่งรวมถึงรหัสผ่าน พาสคีย์ และวิธีการลงชื่อเข้าใช้แบบรวมศูนย์ ซึ่งช่วยให้ผู้ใช้ตรวจสอบสิทธิ์แอปของคุณได้ง่ายขึ้นไม่ว่าจะใช้วิธีการตรวจสอบสิทธิ์ใด
- การรองรับผู้ให้บริการข้อมูลเข้าสู่ระบบของบุคคลที่สาม: ใน Android 14 ขึ้นไป เครื่องมือจัดการข้อมูลเข้าสู่ระบบจะรองรับผู้ให้บริการข้อมูลเข้าสู่ระบบของบุคคลที่สามหลายราย ซึ่งหมายความว่าผู้ใช้จะใช้ข้อมูลเข้าสู่ระบบที่มีอยู่จากผู้ให้บริการรายอื่นเพื่อลงชื่อเข้าใช้แอปของคุณได้
- ประสบการณ์ของผู้ใช้ที่สอดคล้องกัน: เครื่องมือจัดการข้อมูลเข้าสู่ระบบช่วยให้ผู้ใช้ได้รับประสบการณ์การตรวจสอบสิทธิ์ที่สอดคล้องกันมากขึ้นในแอปและกลไกการลงชื่อเข้าใช้ต่างๆ วิธีนี้จะช่วยให้ผู้ใช้เข้าใจและใช้ขั้นตอนการตรวจสอบสิทธิ์ของแอปได้ง่ายขึ้น
หากต้องการเริ่มย้ายข้อมูลจาก FIDO2 ไปยังเครื่องมือจัดการข้อมูลเข้าสู่ระบบ ให้ทำตามขั้นตอนด้านล่าง
อัปเดตทรัพยากร Dependency
อัปเดตปลั๊กอิน Kotlin ใน build.gradle ของโปรเจ็กต์เป็นเวอร์ชัน 1.8.10 ขึ้นไป
plugins { //… id 'org.jetbrains.kotlin.android' version '1.8.10' apply false //… }
ใน build.gradle ของโปรเจ็กต์ ให้อัปเดต Dependency เพื่อใช้เครื่องมือจัดการข้อมูลเข้าสู่ระบบและการตรวจสอบสิทธิ์บริการ 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>' // ... }
แทนที่การเริ่มต้น FIDO ด้วยการเริ่มต้นใช้งานเครื่องมือจัดการข้อมูลเข้าสู่ระบบ เพิ่มการประกาศนี้ในคลาสที่คุณใช้สำหรับการสร้างพาสคีย์และวิธีการลงชื่อเข้าใช้
val credMan = CredentialManager.create(context)
สร้างพาสคีย์
คุณจะต้องสร้างพาสคีย์ใหม่ เชื่อมโยงกับบัญชีของผู้ใช้ และจัดเก็บคีย์สาธารณะของพาสคีย์ไว้ในเซิร์ฟเวอร์ก่อน ผู้ใช้จึงจะลงชื่อเข้าใช้ด้วยพาสคีย์ได้ ตั้งค่าแอปของคุณด้วยความสามารถนี้โดยการอัปเดตการเรียกใช้ฟังก์ชันการลงทะเบียน
หากต้องการรับพารามิเตอร์ที่จำเป็นซึ่งส่งไปยัง
createCredential()
วิธีname("residentKey").value("required")
ระหว่างการสร้างพาสคีย์ ให้เพิ่มname("residentKey").value("required")
(ตามที่อธิบายไว้ในข้อกำหนด WebAuthn) ลงในregisterRequest()
การเรียกเซิร์ฟเวอร์suspend fun registerRequest(sessionId: String ... { // ... .method("POST", jsonRequestBody { name("attestation").value("none") name("authenticatorSelection").objectValue { name("residentKey").value("required") } }).build() // ... }
ตั้งค่าประเภท
return
สำหรับregisterRequest()
และฟังก์ชันย่อยทั้งหมดเป็นJSONObject
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") ) } }
นำวิธีการที่จัดการตัวเปิดใช้งาน Intent และการเรียกใช้ผลการค้นหากิจกรรมออกจากมุมมองของคุณอย่างปลอดภัย
เนื่องจากตอนนี้
registerRequest()
แสดงผลเป็นJSONObject
คุณจึงไม่จำเป็นต้องสร้างPendingIntent
แทนที่ Intent ที่แสดงผลด้วยJSONObject
อัปเดตการเรียกตัวเปิดใช้งาน Intent เพื่อเรียกcreateCredential()
จาก Credential Manager API เรียกเมธอดcreateCredential()
APIsuspend 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 }
เมื่อโทรสำเร็จแล้ว ให้ส่งการตอบกลับกลับไปที่เซิร์ฟเวอร์ คำขอและการตอบกลับสำหรับการเรียกนี้คล้ายกับการใช้งาน FIDO2 คุณจึงไม่ต้องเปลี่ยนแปลงใดๆ
ตรวจสอบสิทธิ์ด้วยพาสคีย์
หลังจากตั้งค่าการสร้างพาสคีย์แล้ว คุณสามารถตั้งค่าแอปให้อนุญาตให้ผู้ใช้ลงชื่อเข้าใช้และตรวจสอบสิทธิ์โดยใช้พาสคีย์ได้ โดยคุณจะต้องอัปเดตโค้ดการตรวจสอบสิทธิ์เพื่อจัดการผลลัพธ์ของเครื่องมือจัดการข้อมูลเข้าสู่ระบบ และใช้ฟังก์ชันเพื่อตรวจสอบสิทธิ์ผ่านพาสคีย์
- การเรียกคําขอลงชื่อเข้าใช้ไปยังเซิร์ฟเวอร์เพื่อรับข้อมูลที่จําเป็นเพื่อส่งไปยังคําขอ
getCredential()
จะเหมือนกับการติดตั้งใช้งาน FIDO2 คุณไม่จำเป็นต้องทำการเปลี่ยนแปลงใดๆ เช่นเดียวกับการเรียกคําขอลงทะเบียน คําตอบที่แสดงจะอยู่ในรูปแบบ 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") { } }
นำวิธีการที่จัดการตัวเปิด Intent และการเรียกใช้ผลการค้นหาของกิจกรรมออกจากมุมมองของคุณอย่างปลอดภัย
เนื่องจากตอนนี้
signInRequest()
แสดงผลJSONObject
แล้ว คุณจึงไม่จำเป็นต้องสร้างPendingIntent
แทนที่ Intent ที่แสดงผลด้วยJSONObject
แล้วเรียกใช้getCredential()
จากเมธอด APIsuspend 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 }
เมื่อการเรียกใช้สำเร็จแล้ว ให้ส่งการตอบกลับกลับไปยังเซิร์ฟเวอร์เพื่อตรวจสอบและตรวจสอบสิทธิ์ผู้ใช้ พารามิเตอร์คำขอและการตอบกลับสำหรับการเรียก API นี้คล้ายกับการใช้งาน FIDO2 จึงไม่ต้องทำการเปลี่ยนแปลงใดๆ
แหล่งข้อมูลเพิ่มเติม
- ข้อมูลอ้างอิงตัวอย่างเครื่องมือจัดการข้อมูลเข้าสู่ระบบ
- Codelab ของ Credential Manager
- การนำการตรวจสอบสิทธิ์ที่ราบรื่นไปใช้กับแอปด้วยพาสคีย์โดยใช้ API เครื่องมือจัดการข้อมูลเข้าสู่ระบบ
- FIDO2 Codelab