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

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 ライブラリで定義されているエラーコードと同じです(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.authenticationError
    // Process the error
}

// Other logic...