集成单点触控通行密钥创建和登录与生物识别提示

在 Android 15 中,Credential Manager 支持用于凭据创建和检索的单点触控流程。在此流程中,正在创建或正在使用的凭据的信息将直接显示在 Biometric Prompt 中,以及指向更多选项的入口点。此简化流程创建了更高效且简化的凭据创建和检索流程。

要求

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

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

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

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

在您的handleCreatePasskeyQuery()中,包含BiometricPromptDataCreateEntry类。

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.authenitcationError
    // 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关联的错误消息,该消息可以显示在UI上。

类似地,在retrieveProviderGetCredentialRequest期间提取元数据。检查您的生物识别流程是否为null。如果是,请配置您自己的生物识别信息进行身份验证。这类似于获取操作的检测方式。

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.authenitcationError
    // Process the error
}

  // Other logic...