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

在 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
  )
)

凭据提供程序应明确设置 allowedAuthenticator 属性 在 BiometricPromptData 实例中。如果未设置此属性,则值默认为 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.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 关联的错误消息,该消息可显示在界面上。

同样,在 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.authenitcationError
    // Process the error
}

  // Other logic...