Androidで生体認証(指紋・顔認証)を実装するには BiometricPrompt APIを使う。

依存を追加する

app/build.gradle に依存を追加する。

dependencies {
    implementation "androidx.biometric:biometric:1.1.0"
}

androidx.biometric ライブラリはマニフェストマージにより USE_BIOMETRIC パーミッションを自動的に追加するため、手動での追加は不要だ。意図を明示したい場合は AndroidManifest.xml に明示的に記述する。

<uses-permission android:name="android.permission.USE_BIOMETRIC" />

生体認証が利用可能か確認する

BiometricManager を使って端末が生体認証に対応しているか確認する。

val biometricManager = BiometricManager.from(this)
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
    BiometricManager.BIOMETRIC_SUCCESS -> {
        // 生体認証が利用可能
    }
    BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
        // 生体認証ハードウェアなし
    }
    BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
        // ハードウェアが現在利用不可
    }
    BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
        // 生体情報が未登録
    }
}

canAuthenticate の引数には認証の強度を指定する。

定数説明
BIOMETRIC_STRONGClass 3 の強い生体認証(主に指紋認証)
BIOMETRIC_WEAKClass 2 の弱い生体認証(多くのデバイスの顔認証)
DEVICE_CREDENTIALPIN・パターン・パスワード

BiometricPrompt を実装する

認証コールバックを作成する

認証結果を受け取るコールバックを作成する。

val authenticationCallback = object : BiometricPrompt.AuthenticationCallback() {
    override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
        super.onAuthenticationSucceeded(result)
        // 認証成功時の処理
    }

    override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
        super.onAuthenticationError(errorCode, errString)
        // 認証エラー時の処理
    }

    override fun onAuthenticationFailed() {
        super.onAuthenticationFailed()
        // 認証失敗時の処理(認識できなかった場合など)
    }
}

onAuthenticationFailed は生体情報が登録済みの情報と一致しなかった場合に呼ばれる。認証をキャンセルした場合や試行の上限回数を超えた場合は onAuthenticationError が呼ばれる。

BiometricPrompt を作成する

FragmentActivity または Fragment から BiometricPrompt を作成する。

val biometricPrompt = BiometricPrompt(
    this, // FragmentActivity または Fragment
    ContextCompat.getMainExecutor(this),
    authenticationCallback
)

ダイアログの情報を設定する

PromptInfo で認証ダイアログに表示する情報を設定する。

val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("生体認証")
    .setSubtitle("アプリにログインするために認証してください")
    .setNegativeButtonText("キャンセル")
    .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
    .build()

setNegativeButtonText はキャンセルボタンのテキストを指定する。DEVICE_CREDENTIALsetAllowedAuthenticators に含める場合は setNegativeButtonText を設定できない。

認証を開始する

biometricPrompt.authenticate(promptInfo)

実際のアプリではボタンのクリックなど、ユーザーの操作をトリガーにして認証を開始する。

val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
    biometricPrompt.authenticate(promptInfo)
}

まとめ

全体のコードをまとめると以下になる。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val biometricManager = BiometricManager.from(this)
        if (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)
            != BiometricManager.BIOMETRIC_SUCCESS) {
            // 生体認証が利用不可
            return
        }

        val authenticationCallback = object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                super.onAuthenticationSucceeded(result)
                // 認証成功時の処理
            }

            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                super.onAuthenticationError(errorCode, errString)
                // 認証エラー時の処理
            }

            override fun onAuthenticationFailed() {
                super.onAuthenticationFailed()
                // 認証失敗時の処理
            }
        }

        val biometricPrompt = BiometricPrompt(
            this,
            ContextCompat.getMainExecutor(this),
            authenticationCallback
        )

        val promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle("生体認証")
            .setSubtitle("アプリにログインするために認証してください")
            .setNegativeButtonText("キャンセル")
            .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
            .build()

        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            biometricPrompt.authenticate(promptInfo)
        }
    }
}

参考