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

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

認証情報プロバイダは、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.BiometricPrompt.NO_SPACEandroidx.biometric.BiometricPrompt.UNABLE_TO_PROCESSandroidx.biometric.BiometricPrompt.ERROR_TIMEOUT など、androidx.biometric ライブラリで定義されているエラーコードと同じです。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.authenticationError     // Process the error }  // Other logic...