生体認証プロンプトにシングルタップ パスキーの作成とログインを統合

Android 15 では、認証情報マネージャーが認証情報のシングルタップ フローをサポートしている 作成と取得が含まれます。このフローでは、構成する認証情報の情報が 生体認証プロンプトに直接表示されます。 エントリポイントを提供しますこの簡素化されたプロセスにより、認証情報の作成と取得プロセスがより効率的で合理化されます。

要件:

  • ユーザーのデバイスで生体認証が設定されており、ユーザーがアプリへの認証に生体認証を許可している。
  • ログインフローの場合、この機能は、そのアカウントで使用可能な認証情報(パスキーやパスワードなど)が複数ある場合でも、単一アカウントのシナリオでのみ有効になります。

パスキー作成フローのシングルタップを有効にする

このメソッドの作成手順は、既存の認証情報の作成手順と一致します。 プロセスをご覧ください。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
  )
)

認証情報プロバイダは、allowedAuthenticator プロパティを明示的に設定する必要があります。 BiometricPromptData インスタンス内。このプロパティが設定されていない場合、値はデフォルトで DEVICE_WEAK に設定されます。ユースケースで必要に応じて、オプションの cryptoObject プロパティを設定します。

ログイン パスキー フローでのシングルタップを有効にする

パスキーの作成フローと同様に、パスキーの既存の設定に従います。 ユーザーのログイン処理をご覧ください。BeginGetPublicKeyCredentialOptionpopulatePasskeyData() を使用して、認証リクエストに関する関連情報を収集します。

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 には、関連するエラー メッセージも記載されます。 UI に表示できる 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...