ย้ายข้อมูลจาก FIDO2 ไปยังเครื่องมือจัดการข้อมูลเข้าสู่ระบบ

การรองรับพาสคีย์ การลงชื่อเข้าใช้แบบรวมศูนย์ และผู้ให้บริการตรวจสอบสิทธิ์บุคคลที่สาม ทำให้ Credential Manager เป็น API ที่แนะนำสำหรับการตรวจสอบสิทธิ์ใน Android ซึ่งมีสภาพแวดล้อมที่ปลอดภัยและสะดวกสบายที่ ช่วยให้ผู้ใช้ซิงค์และจัดการข้อมูลเข้าสู่ระบบได้ สําหรับนักพัฒนาแอปที่ใช้ข้อมูลเข้าสู่ระบบ FIDO2 ในเครื่อง คุณควรอัปเดตแอปให้รองรับการตรวจสอบสิทธิ์ด้วยพาสคีย์โดยผสานรวมกับ Credential Manager API เอกสารนี้ อธิบายวิธีย้ายข้อมูลโปรเจ็กต์จาก FIDO2 ไปยัง Credential Manager

เหตุผลที่ควรย้ายข้อมูลจาก FIDO2 ไปยังเครื่องมือจัดการข้อมูลเข้าสู่ระบบ

ในกรณีส่วนใหญ่ คุณควรย้ายข้อมูลผู้ให้บริการตรวจสอบสิทธิ์ของแอป Android ไปยัง Credential Manager เหตุผลที่ควรย้ายข้อมูลไปยังเครื่องมือจัดการข้อมูลเข้าสู่ระบบมีดังนี้

  • การรองรับพาสคีย์: เครื่องมือจัดการข้อมูลเข้าสู่ระบบรองรับพาสคีย์ ซึ่งเป็นกลไกการตรวจสอบสิทธิ์แบบใหม่ที่ไม่ต้องใช้รหัสผ่าน ที่ปลอดภัยและใช้งานง่ายกว่ารหัสผ่าน
  • วิธีการลงชื่อเข้าใช้หลายวิธี: เครื่องมือจัดการข้อมูลเข้าสู่ระบบรองรับวิธีการลงชื่อเข้าใช้หลายวิธี ซึ่งรวมถึงรหัสผ่าน พาสคีย์ และวิธีการลงชื่อเข้าใช้แบบรวม ซึ่งจะช่วยให้ผู้ใช้ตรวจสอบสิทธิ์แอปของคุณได้ง่ายขึ้น ไม่ว่าผู้ใช้จะชอบวิธีการตรวจสอบสิทธิ์ใดก็ตาม
  • การรองรับผู้ให้บริการข้อมูลเข้าสู่ระบบของบุคคลที่สาม: ใน Android 14 ขึ้นไป เครื่องมือจัดการข้อมูลเข้าสู่ระบบรองรับผู้ให้บริการข้อมูลเข้าสู่ระบบของบุคคลที่สามหลายราย ซึ่งหมายความว่าผู้ใช้จะใช้ข้อมูลเข้าสู่ระบบที่มีอยู่จากผู้ให้บริการรายอื่นเพื่อลงชื่อเข้าใช้แอปของคุณได้
  • ประสบการณ์ของผู้ใช้ที่สอดคล้องกัน: เครื่องมือจัดการข้อมูลเข้าสู่ระบบมอบประสบการณ์ของผู้ใช้ที่สอดคล้องกันมากขึ้นสำหรับการตรวจสอบสิทธิ์ในแอปและกลไกการลงชื่อเข้าใช้ ซึ่งจะช่วยให้ผู้ใช้เข้าใจและใช้ขั้นตอนการตรวจสอบสิทธิ์ของแอปได้ง่ายขึ้น

หากต้องการเริ่มย้ายข้อมูลจาก FIDO2 ไปยังเครื่องมือจัดการข้อมูลเข้าสู่ระบบ ให้ทำตามขั้นตอนด้านล่าง

อัปเดตทรัพยากร Dependency

  1. อัปเดตปลั๊กอิน Kotlin ใน build.gradle ของโปรเจ็กต์เป็นเวอร์ชัน 1.8.10 ขึ้นไป

      plugins {
        //…
          id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
        //…
      }
    
  2. ใน build.gradle ของโปรเจ็กต์ ให้อัปเดตทรัพยากร Dependency เพื่อใช้ เวอร์ชันล่าสุดของ Credential Manager และไลบรารีการตรวจสอบสิทธิ์ของ Play Services

      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>'
        // ...
      }
    
  3. แทนที่การเริ่มต้น FIDO ด้วยการเริ่มต้น Credential Manager เพิ่มการประกาศนี้ ในคลาสที่คุณใช้สำหรับวิธีการสร้างและลงชื่อเข้าใช้ด้วยพาสคีย์

    val credMan = CredentialManager.create(context)
    

สร้างพาสคีย์

คุณจะต้องสร้างพาสคีย์ใหม่ เชื่อมโยงกับบัญชีของผู้ใช้ และ จัดเก็บคีย์สาธารณะของพาสคีย์ไว้ในเซิร์ฟเวอร์ก่อนที่ผู้ใช้จะลงชื่อเข้าใช้ด้วยพาสคีย์ได้ ตั้งค่าแอปให้มีความสามารถนี้โดยอัปเดตการเรียกฟังก์ชัน register

รูปที่ 1 รูปนี้แสดงวิธีแลกเปลี่ยนข้อมูลระหว่างแอปและเซิร์ฟเวอร์เมื่อสร้างพาสคีย์โดยใช้เครื่องมือจัดการข้อมูลเข้าสู่ระบบ
  1. หากต้องการรับพารามิเตอร์ที่จำเป็นซึ่งส่งไปยังเมธอด createCredential() ระหว่างการสร้างพาสคีย์ ให้เพิ่ม name("residentKey").value("required") ตามที่อธิบายไว้ในข้อกำหนด WebAuthn) ลงในการเรียกเซิร์ฟเวอร์ registerRequest()

    suspend fun registerRequest() {
        // ...
        val call = client.newCall(
            Builder()
                .method("POST", jsonRequestBody {
                    name("attestation").value("none")
                    name("authenticatorSelection").objectValue {
                        name("residentKey").value("required")
                    }
            }).build()
        )
        // ...
    }
    
  2. ตั้งค่าreturnประเภทสำหรับregisterRequest()และฟังก์ชันย่อยทั้งหมดเป็น JSONObject

    suspend fun registerRequest(sessionId: String): ApiResult<JSONObject> {
        val call = client.newCall(
            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")
            )
        }
    }
    
  3. นำวิธีการที่จัดการการเรียกใช้ Intent และผลลัพธ์ของกิจกรรมออกจากมุมมองอย่างปลอดภัย

  4. เนื่องจากตอนนี้ registerRequest() จะแสดงผลเป็น JSONObject คุณจึงไม่จำเป็นต้อง สร้าง PendingIntent แทนที่ Intent ที่ส่งคืนด้วย JSONObject อัปเดตการเรียกตัวเรียกใช้ Intent เพื่อเรียก createCredential() จาก Credential Manager API เรียกใช้เมธอด createCredential() API

    suspend fun createPasskey(
        activity: Activity,
        requestResult: JSONObject
    ): CreatePublicKeyCredentialResponse? {
        val request = CreatePublicKeyCredentialRequest(requestResult.toString())
        var response: CreatePublicKeyCredentialResponse? = null
        try {
            response = credMan.createCredential(
                request = request as CreateCredentialRequest,
                context = activity
            ) as CreatePublicKeyCredentialResponse
        } catch (e: CreateCredentialException) {
    
            showErrorAlert(activity, e)
    
            return null
        }
        return response
    }
    
  5. เมื่อโทรสำเร็จแล้ว ให้ส่งการตอบกลับไปยังเซิร์ฟเวอร์ คำขอและการตอบกลับสำหรับการเรียกนี้จะคล้ายกับการติดตั้งใช้งาน FIDO2 จึงไม่จำเป็นต้องทำการเปลี่ยนแปลงใดๆ

ตรวจสอบสิทธิ์ด้วยพาสคีย์

หลังจากตั้งค่าการสร้างพาสคีย์แล้ว คุณสามารถตั้งค่าแอปเพื่อให้ผู้ใช้ ลงชื่อเข้าใช้และตรวจสอบสิทธิ์โดยใช้พาสคีย์ได้ โดยคุณจะต้องอัปเดต รหัสการตรวจสอบสิทธิ์เพื่อจัดการผลลัพธ์ของเครื่องมือจัดการข้อมูลเข้าสู่ระบบ และใช้ ฟังก์ชันเพื่อตรวจสอบสิทธิ์ผ่านพาสคีย์

รูปที่ 2 ขั้นตอนการตรวจสอบสิทธิ์ด้วยพาสคีย์ของเครื่องมือจัดการข้อมูลเข้าสู่ระบบ
  1. การเรียกคำขอลงชื่อเข้าใช้ไปยังเซิร์ฟเวอร์เพื่อรับข้อมูลที่จำเป็นในการส่งไปยังคำขอ getCredential() จะเหมือนกับการติดตั้งใช้งาน FIDO2 ไม่จำเป็นต้องทำการเปลี่ยนแปลงใดๆ
  2. การเรียกคำขอลงทะเบียนจะคล้ายกับการเรียกคำขอลงทะเบียน โดยการตอบกลับที่ส่งคืนจะอยู่ในรูปแบบ 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") {
        }
    }
    
  3. นำวิธีการที่จัดการการเรียกใช้ Intent และการเรียกผลลัพธ์ของกิจกรรมออกจากมุมมองอย่างปลอดภัย

  4. เนื่องจากตอนนี้ signInRequest() จะแสดงผล JSONObject คุณจึงไม่จำเป็นต้องสร้าง PendingIntent แทนที่ Intent ที่แสดงผลด้วย JSONObject แล้วเรียกใช้ getCredential() จากเมธอด API

    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
    }
    
  5. เมื่อการเรียกสำเร็จแล้ว ให้ส่งการตอบกลับไปยังเซิร์ฟเวอร์เพื่อ ตรวจสอบและยืนยันตัวตนผู้ใช้ พารามิเตอร์คำขอและการตอบกลับสำหรับการเรียก API นี้คล้ายกับการติดตั้งใช้งาน FIDO2 จึงไม่จำเป็นต้องทำการเปลี่ยนแปลงใดๆ

แหล่งข้อมูลเพิ่มเติม