3

The documentation of BiometricPrompt.PromptInfo's setDeviceCredentialAllowed method states:

[...] Developers should first check KeyguardManager.isDeviceSecure() before enabling this. If the device is not secure, BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL will be returned in BiometricPrompt.AuthenticationCallback.onAuthenticationError(int, CharSequence).

https://developer.android.com/reference/androidx/biometric/BiometricPrompt.PromptInfo.Builder.html#setDeviceCredentialAllowed(boolean)

However, in order to enable biometric authentication in the first place, you have to set up a device PIN or password. Isn't this check (which is only available on API 23+) therefore superfluous when we already have BiometricManager.canAuthenticate?

Florian Walther
  • 6,237
  • 5
  • 46
  • 104

1 Answers1

4

This is a great question! It might be important to provide a step-wise response. First, let's separate what's happening on a particular device vs what's happening in your app. Then we'll address the more specific question.

Device

A device running API 23+ may or may not have device login credentials setup. The owner of a device is not required to setup device PIN, pattern, password, or biometric templates. It's a choice.

App

A user should be able to install your app whether or not device login credentials have been setup. Therefore your app must be written in a way that handles use cases where login credentials haven't been setup.

Answer to your more specific question

The recommendation is that you pass in a CryptoObject to authenticate() when implementing the biometrics API. If you follow that recommendation, then yes, you will check canAuthenticate() before calling authenticate(promptInfo, cryptoObject). There are a number of reasons for this, which you can get from reading the blog post pointed to above.

Since your question is specifically about setDeviceCredentialAllowed(true), it's important to recall what canAuthenticate() does. It checks for three things: whether there is biometric hardware available on the device, whether the user has enrolled templates, or whether the user has enabled biometric authentication.

Hence, you cannot use canAuthenticate() in your case since it's exclusively about biometrics whereas setDeviceCredentialAllowed(true) accepts device PIN, pattern or password.

Note that while the recommendation is that you use CryptoObject, setDeviceCredentialAllowed() is not compatible with CryptoObject nor with setNegativeButtonText().

P.S. You may also benefit from reading this blog post.

Isai Damier
  • 976
  • 6
  • 8
  • Thank you! So is this check for the case that someone has no biometrics enrolled but a device password set up? And what is the appropriate way to to the same check below API 23? Because with `.setDeviceCredentialAllowed(true)`, the BiometricPrompt can still fall back to the device's password there. So it would be nice to check if a password is set up there as well. – Florian Walther Jan 03 '20 at 09:32
  • 1
    You write "Hence, you cannot use canAuthenticate() in your case since it's exclusively about biometrics" But biometrics can't be enabled without setting a pin/password/pattern, right? So I still can't see the point of this check. If the device has biometrics, it has device credentials too. – Florian Walther Jan 04 '20 at 19:15
  • Let B represent biometrics. Let P represent PIN/pattern/password. **Then** Your statement that B implies P is correct. In other words, if biometrics is enabled then it's safe to assume PIN/password/pattern is also enabled. **BUT** The converse is not true. Just because PIN/pattern/password is enabled does not necessarily mean biometric is enabled; after all, PIN/password were in circulation way before biometrics. The doc is saying you need to make sure PIN/pattern/password are set on the device before trying to use them to login. Nothing to do with biometrics per se. – Isai Damier Jan 06 '20 at 23:36
  • you have to imagine a situation where a device does **not** have biometrics, but has PIN/pattern/password set. – Isai Damier Jan 06 '20 at 23:38
  • That makes sense. Now the only problem is that `KeyguardManager.isDeviceSecure` is only available on API level 23+. What about the devices on 21+ that also support PIN/pattern/password? – Florian Walther Jan 07 '20 at 09:09
  • The functionalities of the biometrics library only work as far back as API 23. The Gradle dependency will compile on devices running API 14+, but the functions/methods are supported only on 23+. – Isai Damier Jan 07 '20 at 16:46
  • Depending on the minSdkVersion level that your app is targeting, you may have to forego the convenient precheck and handle the error codes in `onAuthenticationError(errorCode: Int, errString: CharSequence)` yourself. – Isai Damier Jan 07 '20 at 17:52
  • So there is no appropriate check for pin/pattern/password on AP 21-23? What about `KeyguardManager.isKeyguardSecure`? – Florian Walther Jan 07 '20 at 18:03
  • Technically you can use `keyguardManager.isKeyguardSecure` all the way back to API 16 as long as SIM-locked is not an issue for you. – Isai Damier Jan 07 '20 at 20:35
  • Thank you, I'm trying to figure out if that would be an issue. Theoretically, the sim could be locked but my app could be running, right? As I understand that, this could cause a false positive when I check for pin/pattern/password (even tho this would be unlikely) – Florian Walther Jan 07 '20 at 20:52