8

As we all know, from android 9.0, android introduced BiometricPrompt Api to provide standard authentication experience across growing range of biometric sensors (E.g Fingerprint,Face ID etc).

Now with this new BiometricPrompt Api user can get authenticated via fingerprint, face scanner or iris scanned (depend on their biometric preference). BiometricPrompt api will take care of this and it will notify us via various callbacks.

Below is my code to display Biometric Prompt.

 biometricPrompt = new BiometricPrompt.Builder(context)
            .setTitle("FingerPrint Authentication")
            .setSubtitle("Login via Fingerprint")
            .setDescription("Touch Fingerprint Sensor")
            .setNegativeButton("Cancel", context.getMainExecutor(),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            Log.d(TAG,"Cancelled");
                        }
                    })
            .build();

Now if you see my code, i am setting title as a Fingerprint Authentication. Now in device setting, if user had set a Biometric Preference as a Face ID instead of FingerPrint then this biometricPrompt will authenticate user via faceID and fingerprint sensor wont work even if user keep touching sensor. This would create confusion as Biometric title is saying that "Fingerprint authentication" and user is actually getting authenticated via faceID

Is there any way by which we can know what Biometric preference user has selected (e.g Fingerprint or FaceID)? So based upon that preference i can show appropriate message on BiometricPrompt so user wont get confused.

I already explored all api from BiometricPrompt but could find anything related to BiometricPreference.

Any help would be highly appreciated.

repitch
  • 1,618
  • 1
  • 11
  • 29
Nirav Bhandari
  • 4,200
  • 5
  • 29
  • 58
  • I've been looking at the BiometricPrompt implementation and have the same questions. I think we are suposed to not refer a specific type of biometric, since BiometricPrompt abstract us away from a specific implementation. I would also like to figure out how we check if the device has hardware to support biometrics without actually calling authenticate(). – Orgmir May 08 '19 at 04:30
  • There are sub sequence related to Android 9, 10 and Samsung devices, please refer [here](https://stackoverflow.com/q/59090310/2624806). Since it create lot of use case and it is necessary to limit the use case for end-client. – CoDe Dec 04 '19 at 13:26

4 Answers4

3

While not a perfect solution, you can use the PackageManager API to determine whether a device has the authenticator hardware, e.g.:

if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
{
}

I've created a helper class as follows:

class BiometricAuthenticator
{
 public enum BiometricType
 {
  FINGERPRINT,
  FACE, 
  IRIS,
  NONE
 }

 public static boolean hasBiometricAuthenticator(Context context)
 {
    int biometry = BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED;
    if (VERSION.SDK_INT >= 30)
        biometry = BiometricManager.from(context).canAuthenticate(Authenticators.BIOMETRIC_STRONG | Authenticators.BIOMETRIC_WEAK);
    else
        biometry = BiometricManager.from(context).canAuthenticate();

    switch (biometry)
    {
        case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
        case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
        case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
             return (false);
        case BiometricManager.BIOMETRIC_SUCCESS:
             return true;
    }
    return (false);
 }

 /**
  * biometricType()
  *
  * returns type of biometry supported
  */
 public static BiometricType biometricType(Context context)
 {
    if (VERSION.SDK_INT < 23)
        return BiometricType.NONE;

    PackageManager packageManager = context.getPackageManager();

    // SDK 29 adds FACE and IRIS authentication
    if (VERSION.SDK_INT >= 29)
    {
        if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
            return BiometricType.FACE;
        if (packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS))
            return BiometricType.IRIS;
        if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
            return BiometricType.FINGERPRINT;
        return BiometricType.NONE;
    }

    // SDK 23-28 offer FINGERPRINT only
    return (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT) ? BiometricType.FINGERPRINT : BiometricType.NONE);
 }
}

This allows you to determine if a biometric authenticator is present (hasBiometricAuthenticator), and if so, return the type of authenticator as a BiometricType enum.

A device could theoretically have multiple authenticators, and biometricType() will return FACE, IRIS, then FINGERPRINT in that order of preference on API30+ devices.

Hopefully Google will expose better API in the future, but these tricks will at least help get appropriate prompts on the dialog

CSmith
  • 12,524
  • 3
  • 36
  • 40
1

There is no mean of knowing this type of information for now, an issue had been opened last year to ask for it (https://issuetracker.google.com/issues/111315641). As Android tried to simplify the path for developer to implement authentication in their apps, there is a lack of options in the BiometricPrompt implementation (see the Android document for BiometricPrompt implementation).

Maxime R
  • 23
  • 4
0

In your case you can simply change your title String to "Biometric Authentication", and so with the other Strings. For an example see the blog posts pointed to below.

Your code might look as follows. But I'd also recommend you use the strings.xml resource file instead of hard-coding your Strings in the code. For example, in the future, you may want translation services.

biometricPrompt = new BiometricPrompt.Builder(context)
        .setTitle("Biometric Authentication")
        .setSubtitle("Login via biometrics")
        .setDescription("Use the Biometrics Sensor")
        .setNegativeButton("Cancel", context.getMainExecutor(),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        Log.d(TAG,"Cancelled");
                    }
                })
        .build();

More broadly, the privacy implications must be evaluated before the API team makes a decision whether developers should know a user's biometric preferences. It's really not clear why a developer should need this information. Two blog posts have been published that touch on the subject of Strong vs Weak biometrics and how to implement them (blog one, blog two). Beyond that distinction (i.e. Strong vs Weak), what form-factors a user prefers or ends up using doesn't seem pertinent.

Isai Damier
  • 788
  • 2
  • 7
  • Yes, people should adapt their strings before trying to ask for the APIs to change if really not needed. In our case I decided to go with the app's name as title, for the subtitle I would rather change it slightly to "Auhenticate to login" because the word "biometrics" may not be well known. Description is not really needed in my opinion since the API already explains with a text and an icon what is requested very well. – David May 13 '20 at 00:56
  • 1
    _"what form-factors a user prefers or ends up using doesn't seem pertinent"_ But in some cases it is. See e.g. https://issuetracker.google.com/issues/111315641#comment21 and https://issuetracker.google.com/issues/111315641#comment1 – Michael May 13 '20 at 08:41
-1

In Android R was added a method called setAllowedAuthenticators

public BiometricPrompt.Builder setAllowedAuthenticators (int authenticators)

Optional: Specifies the type(s) of authenticators that may be invoked by BiometricPrompt to authenticate the user. Available authenticator types are defined in Authenticators and can be combined via bitwise OR. Defaults to:

  • Authenticators#BIOMETRIC_WEAK for non-crypto authentication, or
  • Authenticators#BIOMETRIC_STRONG for crypto-based authentication.

If this method is used and no authenticator of any of the specified types is available at the time BiometricPrompt#authenticate(...) is called, authentication will be canceled and AuthenticationCallback#onAuthenticationError(int, CharSequence) will be invoked with an appropriate error code.


This method should be preferred over setDeviceCredentialAllowed(boolean) and overrides the latter if both are used. Using this method to enable device credential authentication (with Authenticators#DEVICE_CREDENTIAL) will replace the negative button on the prompt, making it an error to also call setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener).


authenticators

A bit field representing all valid authenticator types that may be invoked by the prompt. Value is either 0 or a combination of BiometricManager.Authenticators.BIOMETRIC_STRONG, BiometricManager.Authenticators.BIOMETRIC_WEAK, and BiometricManager.Authenticators.DEVICE_CREDENTIAL

Jorge Casariego
  • 20,634
  • 6
  • 84
  • 96