Android Biometric

Overview

Median's Android Biometric Authentication native plugin uses Median JavaScript Bridge commands to securely store and retrieve user credentials. The plugin supports both "Strong" and "Weak" biometrics. Strong incorporates biometric hardware to verify the user's identity, e.g fingerprint or IR facial scan. Weak includes less hardware level security, e.g. camera only. It is up to you as the web developer implementing this native plugin to specify the minimum level of biometrics to accept, and the resultant security of your app.

When designing your implementation the first step is to check for the presence and availability of biometric capabilities. If there are no biometric capabilities available a biometric login dialog should not be displayed to the end user. If biometrics are available, then the status check will also indicate the presence of a previously saved secret.

Once a user has successfully logged in you may save a secret to the device that can be used to login again in the future. We use the Android Keystore, which in turn leverages on-device cryptographic hardware, to ensure that the secret cannot be retrieved without biometric authentication from the user. Note that saving the secret does not require any specific user interaction on the device.

The next time the user needs to log in to your website, and the status check indicates a secret is available, the website should attempt to retrieve the secret from the device. At that point, the user will be shown a dialog prompting them to authenticate via biometrics. If the biometrics are provided successfully, the secret will be released by the Android Keystore and returned via a promist or JavaScript callback. If biometrics are unsuccessful, an error will be returned.

👍

Developer Demo

Display our demo page in your app with the Face ID / Touch ID Android Biometric Native Plugin active to test during development https://median.dev/auth/

Implementation Guide

Once the premium module has been added to your app, you may use the following Median JavaScript Bridge commands to access its functionality.

This guide assumes a working publicly-accessible website (or test site) with a username and password login system.

Save secret on successful login

After a user successfully logs in to your website with their username and password, check to see the device has biometric capabilities available. If biometrics are available then seamlessly save a secret for future retrieval. The secret must be a single string, and can be a combination of the username and password or an authentication token. You may specify a minimum biometric level to support, either strong or weak.

↔️Median JavaScript Bridge

For example, you may embed this JavaScript into your post-login page:

var username = 'andy'
var password = 'password';

median.auth.status({'callbackFunction': median_status_afterlogin}); // returns promise

function median_status_afterlogin(data) {
    if (data && data.hasTouchId) {
    	var secret = JSON.stringify({
            username: username,
            password: password
        });
        
        median.auth.save({
          'secret': secret, 
          'minimumAndroidBiometric': 'strong'
        });
    }
}

Tip: Some JS Bridge commands return promises. They are usually declared as such through a comment to help you identify them. Learn More.

In this example, we have saved the username and password as the secret. You may choose to save an authentication token instead. We have specified the minimumAndroidBiometric as strong which is the default biometric level.

Check for secret on login page

↔️Median JavaScript Bridge

On the login page, you will need to know whether or not to prompt for biometric login. Start by getting the status:

median.auth.status({'callbackFunction': median_status_beforelogin}); // returns promise

function median_status_beforelogin(data) {
   if (data && data.hasTouchId && data.hasSecret) {
       // Prompt the user to use biometrics to log in
       median.auth.get({'callbackFunction': median_secret_callback});
   }
}

function median_secret_callback(data) {
    if (data && data.success && data.secret) {
        var credentials = JSON.parse(data.secret);
        var username = credentials.username;
        var password = credentials.password;
        
        // Use username and password to do login here,
        // e.g. an http post or ajax request
    } else {
        // Allow manual entry
    }
}

Once the median_secret_callback function is called with the previously saved secret, it should perform a request to log in the user. If the credentials are incorrect, you should delete the secret and allow manual login by running the function:

// delete secret if credentials are incorrect
median.auth.delete({'callbackFunction': CALLBACK});

Median JavaScript Bridge Reference

Retrieving biometric availability

↔️Median JavaScript Bridge

Run the JavaScript function:

median.auth.status({
  'minimumAndroidBiometric': 'strong' | 'weak', // optional, default is 'strong'
  'callbackFunction': CALLBACK // optional if callback is used rather than promise
});

The app will return a promise, or if provided execute CALLBACK, with an object parameter containing the fields:

  • hasTouchId: true or false. Set to true on Android devices with fingerprints enrolled.
  • hasSecret: true or false

Saving a secret

Typically you will want to first check that biometrics are available via the status function above and then save the secret.

↔️Median JavaScript Bridge

Run the JavaScript function:

median.auth.save({
  'secret': secret,
  'minimumAndroidBiometric': 'strong' | 'weak', // optional, default is 'strong'
  'callbackFunction': CALLBACK // optional if callback is used rather than promise
});

The app will return a promise, or if provided execute CALLBACK, with an object with a success field

Getting a secret

↔️Median JavaScript Bridge

Run the JavaScript function:

median.auth.get({
  'callbackFunction': CALLBACK, // optional if callback is used rather than promise
  'minimumAndroidBiometric': 'strong' | 'weak', // optional, default is 'strong'
  'prompt': 'PROMPT',
  'callbackOnCancel': INTEGER // optional
});

Prompt is only available for iOS and it has been included above to ensure cross-platform compatibility. The app will return a promise, or if provided execute CALLBACK, with an object parameter containing the fields:

  • success: true or false
  • error: provided success is false (see error codes below)
  • secret: the previously stored secret

callbackOnCancel is optional. If set to 1 and a callbackFunction has been provided, when the user cancels the authentication the callback function will be invoked with success=false, error=userCanceled. If callbackOnCancel is not set (or set to 0), the callback will not be run on cancel.

Deleting a secret

↔️Median JavaScript Bridge

Run the JavaScript function:

median.auth.delete({
  'callbackFunction': CALLBACK // optional if callback is used rather than promise
});

The app will return a promise, or if provided execute CALLBACK, with an object parameter containing the fields:

  • success: true or false
  • error: provided if success is false

Possible error values

In general, you will only need to handle authenticationFailed in the "get secret" request.

  • duplicateItem
  • itemNotFound
  • authenticationFailed
  • genericError
  • userCanceled
  • unimplemented

Limiting access to allowed URLs

By default, any page loaded in your app will be able to use Median JavaScript Bridge to retrieve secrets. If you are allowing domains you do not control to be loaded within your app we strongly recommend restricting access to only specified Allowed URLs.

To do so, go to the Native Plugins page of your Median app, click Settings for the Face ID/Touch ID Android Biometric plugin and add your Allowed URLs (comma separated for multiple URLs) as shown below:

2634

Whitelisted URLs