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

Android 15에서 인증 관리자는 사용자 인증 정보 생성 및 검색을 위한 원탭 흐름을 지원합니다. 이 흐름에서는 생성 또는 사용 중인 모든 정보가 생체 인식 프롬프트에 더 많은 옵션으로 이동할 수 있는 진입점이 있습니다 이 간소화된 프로세스를 통해 사용자 인증 정보 생성 및 검색 프로세스가 더 효율적이고 간소화됩니다.

요구사항:

  • 생체 인식이 사용자 기기에 설정되어 있으며 사용자가 이를 허용합니다. 애플리케이션에 대한 인증을 제공합니다
  • 로그인 흐름의 경우 이 기능은 단일 계정 시나리오에서만 사용 설정됩니다. 여러 사용자 인증 정보 (예: 패스키 및 비밀번호)를 사용할 수 있는 경우에도 해당 계정에 적용됩니다.

패스키 생성 흐름에서 탭 한 번으로 사용 설정

이 메서드의 생성 단계는 기존 사용자 인증 정보 생성과 일치합니다. 절차에 따라 다릅니다. 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
  )
)

사용자 인증 정보 제공업체는 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.authenitcationError
    // Process the error
}

  // Other logic...
}

이 예에는 생체 인식 흐름의 성공에 관한 정보가 포함되어 있습니다. 또한 사용자 인증 정보에 대한 다른 정보가 포함됩니다. 흐름이 실패하면 biometricPromptResult.authenticationError 아래의 오류 코드를 사용하여 결정을 내리세요. 오류 코드는 biometricPromptResult.authenticationError.errorCode는 동일한 오류 코드입니다. androidx.biometric 라이브러리에 정의되어 있습니다. androidx.biometric.BiometricPrompt.NO_SPACE androidx.biometric.BiometricPrompt.UNABLE_TO_PROCESS androidx.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...