将一键式通行密钥创建和登录与生物识别提示集成

在 Android 15 上,Credential Manager 支持用于凭据创建和检索的单点触控流程。在此流程中,系统会在生物识别提示中直接显示正在创建或使用的凭据的信息,以及指向更多选项的入口点。这种简化的流程可提高凭据创建和检索流程的效率并使其更加顺畅。

要求

  • 用户的设备上已设置生物识别,并且用户允许使用生物识别来验证应用登录。
  • 对于登录流程,此功能仅在单账号场景中启用,即使该账号有多个凭据(例如通行密钥和密码)可用也是如此。

在通行密钥创建流程中启用单点触控

此方法的创建步骤与现有凭据创建流程一致。在 BeginCreatePublicKeyCredentialRequest 中,如果请求是针对通行密钥的,请使用 handleCreatePasskeyQuery() 处理该请求。

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

handleCreatePasskeyQuery() 中,添加包含 CreateEntry 类的 BiometricPromptData

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 retrieveProviderCreateCredentialRequestretrieveProviderGetCredentialRequest。这些函数会返回包含提供程序所需元数据的对象。例如,在处理通行密钥创建条目选择时,请按如下所示更新代码:

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.NO_SPACEandroidx.biometric.BiometricPrompt.UNABLE_TO_PROCESSandroidx.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...