앱 자체의 잠금 기능을 구현할 때, 비밀번호 말고도 사용자가 더욱더 빠르게 보안 접근을 해제할 수 있도록 지문 인증 처리를 제공할 수도 있습니다.
private var biometricPrompt: BiometricPrompt? = null
private var promptInfo: BiometricPrompt.PromptInfo? = null
먼저 위 두 개 변수를 선언해 줍니다.
BiometricPrompt
사용자가 지문, 얼굴 인식 등을 통해 인증을 수행할 때 해당 프롬프트를 표시하고, 인증이 성공하거나 실패했을 때의 콜백을 처리합니다.
BiometricPrompt.PromptInfo
BiometricPrompt.PromptInfo는 BiometricPrompt에 표시할 다이얼로그의 정보를 구성하는 데 사용됩니다. 이 객체는 생체 인증 프롬프트의 제목, 설명, 취소 버튼 등을 설정하는 데 사용됩니다.
BiometricPrompt로 인증 처리에 대한 콜백을 설정해 줍니다.
private fun setBiometricPrompt(): BiometricPrompt {
val executor = ContextCompat.getMainExecutor(this)
biometricPrompt =
BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
//인증 성공 이후의 처리
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Log.e("PassCodeActivity","authentication failed : $errorCode / $errString")
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
//인증 실패 시의 처리
}
})
return biometricPrompt as BiometricPrompt
}
인증 성공 이후에는 화면을 넘기거나 애니메이션 등 다양한 처리가 들어갈 수 있겠습니다.
실패 시에도 사용자에게 틀렸다는 진동 액션 또는 애니메이션 등의 효과 처리를 구현해 줄 수 있겠습니다.
PromptInfo에는 안내에 대한 ui 구성 처리를 진행합니다.
private fun setPromptInfo(): BiometricPrompt.PromptInfo {
val promptBuilder = BiometricPrompt.PromptInfo.Builder()
promptBuilder.setTitle("생체 인식을 통해 인증해주세요")
promptBuilder.setNegativeButtonText("비밀번호 인증하기")
promptBuilder.setConfirmationRequired(false)
promptInfo = promptBuilder.build()
return promptInfo as BiometricPrompt.PromptInfo
}
// val promptInfo = BiometricPrompt.PromptInfo.Builder()
// .setTitle("생체 인증")
// .setSubtitle("안전한 로그인을 위해 지문을 사용하세요")
// .setDescription("앱을 사용하려면 지문 인증이 필요합니다.")
// .setNegativeButtonText("취소")
// .build()
이제 실제로 인증 요청을 할 때는 아래 함수를 활용합니다.
promptInfo?.let {
biometricPrompt?.authenticate(it) //인증 실행
}
하지만 인증 요청을 할 때, 현재 생체 인증이 가능한 상황인지를 확인해야 합니다.
디바이스가 모종의 이유로 지원이 불가한 상태일 수도 있고, 생체 인증 정보를 사용자가 아직 디바이스에 등록하지 않은 경우일 수도 있습니다. 해당 로직은 Activity의 onResume에서 호출합니다.
private fun authenticateToEncrypt() {
val biometricManager = BiometricManager.from(this@PasscodeActivity)
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) {
BiometricManager.BIOMETRIC_SUCCESS -> Log.i("BiometricManager", "App can authenticate using biometrics.")
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> showNeedBiometricSetting()
else -> //지원 불가
}
startAuthenticate()
}
사용자 인증 정보가 등록되지 않은 BIOMETRIC_ERROR_NONE_ENROLLED의 경우에는 디바이스 설정에서 인증 정보를 등록할 수 있도록 안내가 필요합니다.
저 같은 경우에는 임의의 커스텀 다이얼로그를 팝업 하여 생체 인증 설정 화면으로 넘어갈 수 있도록 구성했습니다.
object : CustomAlertDialog.ButtonInterface {
override fun onConfirm(dialog: CustomAlertDialog) {
dialog.dismiss()
val settingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Intent(Settings.ACTION_BIOMETRIC_ENROLL)
} else {
Intent(Settings.ACTION_SECURITY_SETTINGS)
}
launcher.launch(settingIntent)
}
override fun onCancel(dialog: CustomAlertDialog) {
dialog.dismiss()
}
})
다이얼로그로 생체 인증 정보 등록을 맞춘 이후에는 Activity Launcher Callback에 다시 생체 인증 정보 활용 여부를 체크하는 함수를 호출하여 다시 인증 처리를 진행합니다.
private val fingerPrintEnrollLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { authenticateToEncrypt() }
생체 인증 정보만 등록하면 얼굴 인식도 같이 제공됩니다.
참고로 인증 정보 과정 화면은 시스템 상 캡처가 불가능합니다.