整合「輕觸一下密碼金鑰建立流程」和生物特徵辨識提示登入功能

在 Android 15 中,Credential Manager 支援輕觸一下即可建立及擷取憑證的流程。在這個流程中,系統會直接在生物特徵辨識提示中,顯示正在建立或使用的憑證資訊,以及更多選項的進入點。這個簡化程序可讓您更有效率、更輕鬆地建立及擷取憑證。

必要操作:

  • 使用者已在裝置上設定生物辨識,並允許用於驗證應用程式。
  • 在登入流程中,這項功能僅適用於單一帳戶情境,即使該帳戶有多個憑證 (例如密碼金鑰和密碼) 也是如此。

在密碼金鑰建立流程中啟用輕觸功能

這個方法的建立步驟與現有的憑證建立程序相同。在 BeginCreatePublicKeyCredentialRequest 中,如果要求是密碼金鑰,請使用 handleCreatePasskeyQuery() 處理要求。

is BeginCreatePublicKeyCredentialRequest -> {
    Log.i(TAG, "Request is passkey type")
    return handleCreatePasskeyQuery(request, passwordCount, passkeyCount)
}

在 handleCreatePasskeyQuery() 中,加入 BiometricPromptData 和 CreateEntry 類別:

val createEntry = CreateEntry(
    // Additional properties...
    biometricPromptData = BiometricPromptData(
        allowedAuthenticators = allowedAuthenticator
    ),
)

憑證提供者應在 BiometricPromptData 執行個體中明確設定 allowedAuthenticator 屬性。如未設定這項屬性,預設值為 DEVICE_WEAK。視需要設定選用的 cryptoObject 屬性。

在登入密碼金鑰流程中啟用輕觸一下功能

與密碼金鑰建立流程類似,這會遵循處理使用者登入的現有設定。在 BeginGetPublicKeyCredentialOption 下,使用 populatePasskeyData() 收集驗證要求的相關資訊:

is BeginGetPublicKeyCredentialOption -> {
    // ... other logic

    populatePasskeyData(
        origin,
        option,
        responseBuilder,
        autoSelectEnabled,
        allowedAuthenticator
    )

    // ... other logic as needed
}

與 CreateEntry 類似,BiometricPromptData 執行個體會設為 PublicKeyCredentialEntry 執行個體。如未明確設定,allowedAuthenticator 預設為 BIOMETRIC_WEAK。

PublicKeyCredentialEntry(
    // other properties...

    biometricPromptData = BiometricPromptData(
        allowedAuthenticators = allowedAuthenticator
    )
)

處理憑證項目選取作業

處理建立密碼金鑰或登入時選取密碼金鑰的憑證項目選取作業時,請視情況呼叫 PendingIntentHandler's retrieveProviderCreateCredentialRequest 或 retrieveProviderGetCredentialRequest。這些物件會傳回包含供應商所需中繼資料的物件。舉例來說,處理密碼金鑰建立項目選取時,請按照下列方式更新程式碼:

val createRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
if (createRequest == null) {
    Log.i(TAG, "request is null")
    setUpFailureResponseAndFinish("Unable to extract request from intent")
    return
}
// Other logic...

val biometricPromptResult = createRequest.biometricPromptResult

// Add your logic based on what needs to be done
// after getting biometrics

if (createRequest.callingRequest is CreatePublicKeyCredentialRequest) {
    val publicKeyRequest: CreatePublicKeyCredentialRequest =
        createRequest.callingRequest as CreatePublicKeyCredentialRequest

    if (biometricPromptResult == null) {
        // Do your own authentication flow, if needed
    } else if (biometricPromptResult.isSuccessful) {
        createPasskey(
            publicKeyRequest.requestJson,
            createRequest.callingAppInfo,
            publicKeyRequest.clientDataHash,
            accountId
        )
    } else {
        val error = biometricPromptResult.authenticationError
        // Process the error
    }

    // Other logic...
}

這個範例包含生物辨識流程成功的相關資訊。其中也包含憑證的其他資訊。如果流程失敗,請根據 biometricPromptResult.authenticationError 下方的錯誤代碼做出決策。biometricPromptResult.authenticationError.errorCode 傳回的錯誤代碼與 androidx.biometric 程式庫中定義的錯誤代碼相同,例如 androidx.biometric.BiometricPrompt.ERROR_NO_SPACE、androidx.biometric.BiometricPrompt.ERROR_UNABLE_TO_PROCESS、androidx.biometric.BiometricPrompt.ERROR_TIMEOUT 等。authenticationError 也會包含與 errorCode 相關聯的錯誤訊息,可顯示在使用者介面上。

同樣地,請在 retrieveProviderGetCredentialRequest 期間擷取中繼資料。檢查生物辨識流程是否為 null。如果是,請設定自己的生物辨識資訊來進行驗證。這與 get 作業的檢測方式類似:

val getRequest =
    PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)

if (getRequest == null) {
    Log.i(TAG, "request is null")
    setUpFailureResponseAndFinish("Unable to extract request from intent")
    return
}

// Other logic...

val biometricPromptResult = getRequest.biometricPromptResult

// Add your logic based on what needs to be done
// after getting biometrics

if (biometricPromptResult == null) {
    // Do your own authentication flow, if necessary
} else if (biometricPromptResult.isSuccessful) {

    Log.i(TAG, "The response from the biometricPromptResult was ${biometricPromptResult.authenticationResult?.authenticationType}")

    validatePasskey(
        publicKeyRequest.requestJson,
        origin,
        packageName,
        uid,
        passkey.username,
        credId,
        privateKey
    )
} else {
    val error = biometricPromptResult.authenticationError
    // Process the error
}

// Other logic...