생체 인식 메시지를 통해 원탭 패스키 생성 및 로그인을 통합합니다.

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_SPACE, androidx.biometric.BiometricPrompt.UNABLE_TO_PROCESS, androidx.biometric.BiometricPrompt.ERROR_TIMEOUT 등 androidx.biometric 라이브러리에 정의된 오류 코드와 동일합니다. authenticationError에는 UI에 표시할 수 있는 errorCode와 연결된 오류 메시지도 포함됩니다.

마찬가지로 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.authenticationError
    // Process the error
}

// Other logic...