Using the Device API
Overview
When we created our other Identity Vault tutorial applications we implemented several pieces of functionality without taking the capabilities and current configuration of the device into account. We will address that now with the help of the Device API.
In this tutorial we will:
- Examine the methods available in the Device API.
- Disable options based on the current device capabilities and configurations.
- Show the privacy screen when the application is in the task switcher.
- Progressively enhance the vault type when the user logs in.
- Handle the iOS Face ID permissions before setting the vault to use biometric unlock.
Let's Code
This tutorial builds upon the application created when doing the startup strategies tutorial. If you have the code from when you performed that tutorial, then you are ready to go. If you need the code you can make a copy from our GitHub repository.
The Device API
The functions in the Device API can be separated into three different categories:
- Capabilities: The values returned by these functions are defined by the capabilities of the device.
- Configuration and Status: The values returned by these functions are determined by the device's current configuration and/or status.
- Procedures: These functions perform some kind of device related process such as setting a configuration value or displaying a prompt.
We will use the Tab2
page to demonstrate how these functions work.
Capabilities
The "capabilities" functions return information about the capabilities of the current device. The values returned by these functions are constant for a given device.
getAvailableHardware
: Resolves an array of the types of biometric hardware that a device supports.hasSecureHardware
: Resolvestrue
if the device has hardware dedicated to storing secure data. Most modern devices do.isBiometricsSupported
: Resolvestrue
if the device supports some form of biometrics regardless of whether or not biometrics is configured. Most modern devices support biometrics.
Update the Tab2
page to display the results of these methods.
Note that these value are consistent on a particular device, regardless of whether or not biometrics is currently configured on that device.
Configuration and Status
-
getBiometricStrengthLevel
: On iOS, this function always resolves tostrong
. On Android, the result depends on the Biometrics Classes of whichever biometrics are currently configured on the device. If at least one of the configured biometric options is Class 3, it will returnstrong
, otherwise it will returnweak
. -
isBiometricsAllowed
: On iOS Face ID based devices this method resolves to one of the following:Prompt
: The user needs to be asked for permission to use Face ID.Denied
: The user has declined permission.Granted
: The user has granted permission.
On all other devices, which do not require such permissions, this method always resolves to
Granted
. -
isBiometricsEnabled
: Resolvestrue
if biometrics have been configured for the current user,false
otherwise. -
isHideScreenOnBackgroundEnabled
: Resolvestrue
if the "hide screen" will be displayed when the app is placed in the background,false
otherwise. -
isLockedOutOfBiometrics
: Resolvestrue
if the user is locked out of biometrics after too many failed attempts. On iOS, this information may be known upon app launch. On Android, an attempt to unlock with the current app session needs to be performed before this can be known. -
isSystemPasscodeSet
: Resolvestrue
if the user has established a system passcode that can be used to unlock the app,false
otherwise.
Update the Tab2
page to display the results of these methods.
Build the application and install it on a variety of devices. Then modify the configuration of the biometrics on those devices to see how the values change.
Procedures
setHideScreenOnBackground
: Set whether or not the interface is obscured when the application is placed in the background.showBiometricPrompt
: Show a biometric prompt to the user. This method will resolve when the user dismisses the prompt by successfully providing biometrics, and will reject if they cancel. On iOS devices with Face ID, this method will also ask for permission as needed.
Add a couple of buttons to the Tab2
page to call these methods.
Build the code and install it on a variety of different types of devices to see how the procedures behave.
Common Uses of the Device API
Now that we have seen an overview of the Device API let's have a look at some common tasks that we can perform using the methods from this API. In this section, we look at how to perform the following tasks:
- Enabling or disabling various functionality based on the capabilities of the device.
- Setting the app to show the "privacy screen" when the app is being shown in the app switcher.
- Progressively enhance the vault to use the most secure options available on the device.
- Managing permissions on iOS devices that use Face ID.
Enable / Disable Functionality
We can use various methods within the Device API to enable or disable the setting of various preferences, settings,
or workflows based on whether or not the current device has biometrics enabled or not. For example, if the
application has a preference to toggle biometric authentication on and off, we could hide or disable the
toggle for that setting based on the value of isBiometricsEnabled()
.
Show the Privacy Screen
Many applications that use Identity Vault also display sensitive data that the user may not want shown if the application is currently displayed in the app switcher. Identity Vault has a "privacy screen" feature that will obscure the page contents in this situation. On Android, a gray or black page is shown instead. On iOS, the splash screen is displayed.
In the Tab2
page, we show how to use Device.setHideScreenOnBackground()
to toggle this feature on and off.
Most applications will either want this feature on or off without a toggle. In such cases, it is best to set
the feature however you wish upon application startup.
You could also put the Device.setHideScreenOnBackground(true)
call within the initializeVault().then()
callback
and then await
it before mounting the app. Doing so is fine, but not necessary.
You may also be tempted to include the Device.setHideScreenOnBackground(true)
call in the initializeVault()
function itself. It is a best-practice, however, to keep the code separate from the vault code to avoid confusion in
cases where the application needs to use multiple vaults.
Progressively Enhance the Vault
It is a best practice to initially create the vault with a configuration that all devices can support and then enhance the vault's security either based on user preferences or the capabilities of the device. In this section, we will examine how to automatically enhance the security based on the capabilities of the device.
For our application, we would like to use biometrics with a system passcode backup if possible. Otherwise, we will force the user to log in each time.
- If the vault is already storing a session, leave it alone.
- If the vault is not storing a session:
- Use biometrics with a system passcode backup if both are set up.
- Use a system passcode if biometrics is not set up.
- If neither biometrics nor a system passcode are set, force the user to login in each time.
Our app is written to start with a Secure Storage type vault.
Import the Device
class so we can use the API.
Change the type
to be InMemory
and increase the background lock time from 2 seconds to 30 seconds.
Add an enhanceVault()
function. Per our first requirement, the vault should not be changed if it is already in use.
Enhance the vault as part of the initialization process.
Use the proper vault type based on whether or not a system passcode is set.
Modify updateUnlockMode()
to adjust the lockAfterBackgrounded
time based on the type of vault used.
Our app is written to start with a Secure Storage type vault.
Import the Device
class so we can use the API.
Change the type
to be InMemory
and increase the background lock time from 2 seconds to 30 seconds.
Add an enhanceVault()
function. Per our first requirement, the vault should not be changed if it is already in use.
Enhance the vault as part of the initialization process.
Use the proper vault type based on whether or not a system passcode is set.
Modify updateUnlockMode()
to adjust the lockAfterBackgrounded
time based on the type of vault used.
Build the application and test it on a variety of devices using a variety of screen unlock settings. You should see the following behaviors:
- On devices without a system passcode or biometrics the user should be forced to login any time the application is
restarted or is put in the background for more than 30 seconds. We have made this timeout longer than our standard
2 second timeout because the
InMemory
vault will destroy the authentication result after the timeout rather than storing it. - On devices with a system passcode but without biometrics set up, the session is stored between invocations and is unlocked via the system passcode. The application locks after 2 seconds in the background.
- On devices with a system passcode and with biometrics set up, the session is stored between invocations and is unlocked via biometrics using the system passcode as backup. The application locks after 2 seconds in the background.
- On all devices, if "Use Secure Storage" is selected, then a secure storage vault will be used. This can be tested by pressing "Use Secure Storage" and closing the app. When it comes back the user should still be logged in. This should repeat until the user logs out prior to closing the app.
We are taking advantage of the fact that DeviceSecurityType.Both
uses the system passcode not only when
biometrics fails, but also if biometrics is not set up at all. Perhaps we want our app to behave in a more
specific manner such as any of the following:
- Use only Biometrics with a system passcode backup only if biometrics is set up.
- Use Biometrics without a system passcode backup, but use system passcode if biometrics is not set up.
- Use Biometrics with a system passcode backup only if biometrics is set up. Allow the user to set a custom passcode in other circumstances.
Identity Vault and the Device API give you the flexibility to support any of those scenarios and more. Please contact us if you would like to discuss your specific scenario.
Handle iOS Permissions
On iOS devices that use Face ID, the user needs to give permission in order for the application to use their face to unlock the app. The prompt for this permission is not provided to the user until your app tries to use Face ID for the first time. As a result, our application is not asking for permission to use the user's face until the first time it tries to unlock the vault. It would be better if it asked for permission before setting the vault to use biometrics. Let's do that now.
The requirements we have from our design team are;
- Keep the vault type logic as it is now. That is:
- If there is a system passcode and biometrics, use biometrics with a system passcode backup.
- If there is a system passcode but no biometrics, use the system passcode.
- If there no system passcode force the user to log in each time.
- In addition, if biometrics is enabled:
- Determine if the user needs to be prompted for permission.
- If the user needs to be prompted for permission, show a biometric prompt. This will also trigger the permissions prompt in cases where permission is required.
- If the user allows Face ID to be used, set the vault type to
DeviceSecurity
. - If the user denies permission to use Face ID, set the vault type to
InMemory
.
Add BiometricPermissionState
to the items being imported from the Identity Vault package.
Add a provisionBiometrics()
function but do not export it. Display the biometric prompt if permissions need to be prompted.
Determining the proper vault mode to use is going to get more complex, so let's start by abstracting that logic into its own method.
Biometrics will be special, so let's give it its own section.
Provision the Biometrics.
If biometrics has been set up, return the mode based on whether the user has granted or denied access to Face ID.
Add BiometricPermissionState
to the items being imported from the Identity Vault package.
Add a provisionBiometrics()
function but do not export it. Display the biometric prompt if permissions need to be prompted.
Determining the proper vault mode to use is going to get more complex, so let's start by abstracting that logic into its own method.
Biometrics will be special, so let's give it its own section.
Provision the Biometrics.
If biometrics has been set up, return the mode based on whether the user has granted or denied access to Face ID.
Perform a fresh install on several different devices. On iOS devices that use Face ID, the app should ask permission to use the Face ID upon the first start after application install. If you allow the use of Face ID, the application will use Biometrics with a System Passcode backup. If you disallow the use of Face ID, the application will use an "In Memory" vault.
On all other devices, you should not receive any permission requests and application will use Biometrics with a System Passcode backup by default.
Notice that the permission request comes during application startup. This may be jarring to some users. For this application it may be better to ask for the Face ID permission right after the user logs in. Since the provisioning is tied to the enhancement of the vault to use biometrics, this means delaying the enhancement of the vault until after login.
Export the enhanceVault()
function.
enhanceVault()
currently does not enhance a vault that is in use.
Preventing this is no longer necessary. Remove the isEmpty()
check leaving the rest of the code in place.
enhanceVault()
is currently called from the initialize()
function.
Remove the call leaving the rest of initialize()
in place.
In the LoginPage
, the login handler currently logs the user in and navigates to the main page.
Enhance the vault as part of a successful login.
Export the enhanceVault()
function.
enhanceVault()
currently does not enhance a vault that is in use.
Preventing this is no longer necessary. Remove the isEmpty()
check leaving the rest of the code in place.
enhanceVault()
is currently called from the initialize()
function.
Remove the call leaving the rest of initialize()
in place.
In the LoginPage
, the login handler currently logs the user in and navigates to the main page.
Enhance the vault as part of a successful login.
The application now asks for Face ID permission only if needed and only after a successful login rather than doing so as part of the startup process. In your application you may want to tie this to something else such as setting a "Use Biometrics" preference toggle. The choice is up to you.
On the Tab1
page, the user can currently click the "Use Biometrics" button even if the user has rejected
Face ID. According to the rules we are enforcing, though, we will end up using an "In Memory" vault in that
case. We can enhance the "disable biometrics" code that we added earlier.
Next Steps
We have examined the Device
API and several potential uses for it. Continue to explore how this API can be used
to enhance the user experience within your application.