Kotlin 生体認証(指紋認証、顔認証)

こんにちはmessiです。
Androidエンジニアしてます。
Twitterはこちら -> https://twitter.com/messi_0601

今回はAndroidの生体認証の実装について紹介します。

## はじめに
AndroidXの生体認証コンポネントBiometricPromptが正式リリースされた。
サポート対象はAndroid6.0以上
これまで使われていたFingerPrintManagerは非推奨になりました。
指紋認証を事前に端末登録しておかないと動作しません。（端末の設定でアプリに指紋認証の使用の許可してないといけないこともあるそうです。）

今回顔認証も試してみたかったのですが、自分の所有端末が顔認証非対応だったので試すことができなかったのですが、このコードで顔認証も出来るようなので試してみてください。
pixel4なら出来るっぽいです。
[引用]
https://stackoverflow.com/questions/63629458/face-authentication-using-androidx-biometric-api-in-android

##gradle設定

```build.gradle
dependencies {
    //add
    implementation "androidx.biometric:biometric:1.0.1"
}
```

## 端末が生体認証に対応しているか確認
わかりやすくするためにLogを多めに配置してます。
```checkBiometric```この関数で端末が生体認証できるかどうかをBooleanで返します。

```MainActivity.kt
//端末が生体認証に対応しているか確認
private fun checkBiometric(): Boolean {
    val biometricManager = BiometricManager.from(this)
    when (biometricManager.canAuthenticate()) {
        BiometricManager.BIOMETRIC_SUCCESS -> {
            Log.d("TAG", "App can authenticate using biometrics.")
        }
        BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
            Log.e("TAG", "Hardware not available")
        }
        BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
            Log.e("TAG", "Biometric features are currently unavailable.")
        }
        BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
            Log.e( "TAG", "The user hasn't associated any biometric credentials with their account.")
        }
        else -> {
            Log.e("TAG", "Nothing supported")
        }
    }
    return biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS
    }
```

| method | 概要 | 
|:-----------|:------------|
| BIOMETRIC_SUCCESS|エラーは検出されませんでした。|
| BIOMETRIC_ERROR_HW_UNAVAILABLE |ハードウェアは利用できません。 あとでもう一度試してみてください。|
| BIOMETRIC_ERROR_NONE_ENROLLED |ユーザーには生体認証が登録されていません。|
| BIOMETRIC_ERROR_NO_HARDWARE | 生体認証ハードウェアはありません。 |

## 認証後の処理の設定
生体認証が行われた後に行われる処理の設定になります。(callbackの所）
Succeeded、Error、Failedでそれぞれ処理を書くことができます。
ここではToastを出すようにしてます。

```MainActivity.kt
private fun createBiometricPrompt(): BiometricPrompt {
    executor = ContextCompat.getMainExecutor(this)
    val callback = object:BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            super.onAuthenticationError(errorCode, errString)
            Toast.makeText(
                applicationContext,
                "Authentication error: $errString", Toast.LENGTH_SHORT
            ).show()
        }

        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
            Toast.makeText(
                applicationContext, "Authentication failed",
                Toast.LENGTH_SHORT
            ).show()
        }

        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            super.onAuthenticationSucceeded(result)
            Toast.makeText(
                applicationContext,
                "Authentication succeeded!", Toast.LENGTH_SHORT
            ).show()
        }
    }
    biometricPrompt = BiometricPrompt(this, executor, callback)
    return biometricPrompt
}
```

## 表示ダイアログの設定
buttonを押した際に認証のダイアログが表示する関数になってます。
```authenticate(promptInfo)```で呼び出します。

```MainActivity.kt
private fun showId() {
    promptInfo = BiometricPrompt.PromptInfo.Builder()
        .setTitle("指紋認証")
        .setSubtitle("サンプルです")
        .setConfirmationRequired(true)
        .setDeviceCredentialAllowed(true)
        .build()
    val biometricLoginButton =
        findViewById<Button>(R.id.button)
    biometricLoginButton.setOnClickListener {
        biometricPrompt.authenticate(promptInfo)
    }
}
```

BiometricPrompt.PromptInfo.Builder()でダイアログの実装を行います。

| method | 説明 | 
|:-----------|:------------|
| setTitle       |ダイアログのタイトルの設定 (必須)|
| setSubtitle |詳しい説明とかに使えるtext|
| setNegativeButtonText|*1左下に表示するtext (必須)|
| setDeviceCredentialAllowed| *1別の認証方法を提案するかどうか |
| setConfirmationRequired|認証後明示的なユーザー確認をするかどうか |
| setDescription|ダイアログの説明 |

*1 setNegativeButtonTextとsetDeviceCredentialAllowed(true)を共に使うことができません。setDeviceCredentialAllowedを指定しない場合はsetNegativeButtonTextが必須となります。

![475779.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188385/c7332d00-677f-ea03-a03a-7d890199ae07.jpeg)

## サンプル
必要最低限で作ったので見やすかと思います。
https://github.com/175atsu/BiometricSample

## 終わりに
指紋、顔、
次は声の認証かな？

顔認証できたら教えてください🙇‍♂️

では、また