ticktakclockの日記

技術ポエムを綴ったりします。GitHub idも同じです (@ticktakclock)

BiometricPromptで指紋認証機能を実装する

こんにちは、tkyです。

今回はAndroidBiometricPromptを使って指紋認証機能を試してみたいと思います。

developer.android.com

こんな感じで動いてます。 コードと動作確認gifはgithubを参照してください。

github.com

f:id:ticktakclock:20190314144934p:plain:w200

BiometricPrompt

API 28から使用できる新しい認証用のフレームワークです。以前はFingerprintManagerというものでしたが、こちらは 非推奨 となりAPI28からはBiometricPromptを使用するようにとの公式からのお達しが出ております。

パーミッション

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

コード

BiometricPromptのビルダーを使います。 ここにダイアログに使う情報をセットして、authenticateしますが、ダイアログの情報を先に入れないとインスタンス作れないのちょっとめんどくさい。

val cancellationSignal = CancellationSignal()
BiometricPrompt.Builder(context)
    .setTitle("生体認証")
    .setSubtitle("サブタイトルを添えて")
    .setDescription("詳細説明をここに記載します")
    .setNegativeButton("キャンセル", context.mainExecutor, DialogInterface.OnClickListener { dialog, which ->
        cancellationSignal.cancel()
    })
    .build()
    .authenticate(cancellationSignal, context.mainExecutor, object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
            super.onAuthenticationError(errorCode, errString)
        }
        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
        }

        override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) {
            super.onAuthenticationHelp(helpCode, helpString)
        }
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult?) {
            super.onAuthenticationSucceeded(result)
        }
    })

AndroidXのBiometricPrompt

前述でFingerprintManagerがAPI28から非推奨となりますが、P以上であればBiometricPrompt、未満であればFingerprintManagerという実装をすることになります。 BiometricPromptはsupportライブラリがないため、仮に実装の必要が出た場合、以下が必要になってくるというということです。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)

この課題を解決するために登場するのがAndroidX BiometricPromptです。 それぞれパッケージは以下のようになっています。

  • 通常のBiometricPrompt
    • android.hardware.biometrics.BiometricPrompt
  • androidxのBiometricPrompt
    • androidx.biometrics.BiometricsPrompt

コード

パーミッションは通常のBiometricPrompt同様で必要です。AndroidXにおいてはgradleに追記が必要です。

implementation 'androidx.biometric:biometric:1.0.0-alpha03'

AndroidXではダイアログを生成するBuilderと認証するためのクラスが分かれています。 BiometricPromptのコンストラクタに必要な情報を流して、authenticateする時にダイアログ情報をセットして認証させる流れです。

val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("AndroidXによる生体認証")
    .setSubtitle("サブタイトルを添えて")
    .setDescription("詳細説明をここに記載します")
    .setNegativeButtonText("Negativeボタン")
    .build()
// context.mainExecutorはAPI28からなので自分でmainExecutorを作成
BiometricPrompt(activity, mainExecutor, object : BiometricPrompt.AuthenticationCallback(){
    override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
        super.onAuthenticationError(errorCode, errString)
    }
    override fun onAuthenticationFailed() {
        super.onAuthenticationFailed()
    }
    override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
        super.onAuthenticationSucceeded(result)
    }
}).authenticate(promptInfo)

動作確認

いろいろな端末で動作確認してみます。

XperiaZ3 (Android 5.0.2 API21)

API21でBiometricPromptを使おうとすると当たり前ですがNoClassDefFoundErrorが発生します。 githubの実装では落ちないようにcatchしてToast出すだけしています。

java.lang.NoClassDefFoundError: Failed resolution of: Landroid/hardware/biometrics/BiometricPrompt$Builder;

AndroidXのBiometricPromptであれば、ちゃんとコールできています。 が、指紋認証センサーは搭載されていないので、BiometricPrompt側で認証エラーが発生します。

HUAWEI SHT-AL09(Android8.0.0 API26)

APi26なので同様にBiometricPromptを使おうとするとNoClassDefFoundErrorとなりますが、 この子は指紋認証センサーが搭載されているので、AndroidXのBiometricPromptが動作していることが確認できました。

Pixel 3 (Android 9.0.0 API28)

言わずもがな、どちらも動作できました。

最後に

指紋認証を試してみました。今の所「その端末がユーザのものであることを確認する」以上の使い方が思いつきませんでした・・・ この流れでWebAuthnとかFIDO2とか踏み込むかどうか・・・

また、AndroidX中の実装を気になって見てみたら、バージョンで切り分けてBiometricPromptとFingerprintManagerをゴリゴリしてました😅僕らでバージョン分岐しなくて良くなったのは事実です!

参考

Fingerprint Authentication using BiometricPrompt Compat