Intorduction
Embarking on the journey to integrate biometric authentication into our latest mobile React Native endeavor, NFT Reality, presented a unique set of challenges and learning experiences. My previous exposure to a colleague's work on a similar project in React Native offered a foundational understanding, but it was clear that the crux of this task extended beyond mere technical execution.
In pursuit of the optimal approach, I delved into the intricacies of React Native's ecosystem, particularly focusing on two renowned libraries that facilitate native biometric functionalities: react-native-keychain and react-native-biometrics. The former plays a pivotal role in safeguarding sensitive data within our application, with an added layer of biometric protection, if desired. The latter, however, is tailored specifically for server-side authentication using biometric verification.
The cornerstone of this endeavor was identifying our specific use case. This introspection was crucial as not all solutions seamlessly integrate into our authentication flow, and some necessitate extensive backend modifications. In this article, I aim to share the insights and challenges I encountered while navigating the realm of biometric authentication in React Native, offering a practical perspective that merges technical know-how with real-world application.
Use Case
Add biometric login to the app after the first login,
and add a biometry option inside the app to easily switch ON/OFF biometry.
Auth Environment
token with auto-refresh,
every request sent with the token expands his lifetime.
Local Authentication
What can we do if we build something that does not require changes on our backend side? First, according to best practices, we should not save user credentials on user devices. We should use secure storage for tokens or other forms of sensitive information but not user credentials such as passwords. react-native-keychain uses iOS Keychain and Android Keystore, which provides a high level of security but is not for all android devices. Android Keystore is used for API level 23+, and in lower versions, there is an increased risk of obtaining secret information. Of course, if the security of your application is not the highest priority and we want a simple and fast solution, we can still use this option. You can read more information about types of secure storage: React Native security.
Let’s look at our environment a second time. We have a token with auto refresh. Why can't we store our access token and obtain it with biometry anytime we want? We can, but is it a real biometry login implementation? Our token has a lifetime which will expand after every successful request. What if we stop using the app for a long time and later decide to log in? We will obtain a terminated token, and login will fail. There is a different approach using manual refresh tokens, which I will discuss in the next chapter of this article.
Let’s assume that we will save the username and password with react-native-keychain. Is it fit our use case? Well, after the first login, we can show a popup to the user, and we can save user credentials if the user says YES. But what if the user says NO? Our second task is to add biometry options to switch ON/OFF biometry in the app. How can we switch ON biometry in the settings screen? We don’t have user credentials. We had a chance to save them in the login form, but the user decided to cancel this action. I can’t find a good answer for that problem because it’s not a technical issue. The answer can be simple, right? We can save user credentials permanently with an additional variable IS_BIOMETRY_ENABLED, and using that boolean, and we can switch ON/OFF biometry in the settings screen. Is it fair to save something even if the user answers explicitly that he doesn't want to enable biometry for the app? I will strongly disagree that this is a good solution, and it doesn't testify to our transparency. It can also be a source of future problems with positive reviews before the publication of our app.
What is a workaround to that problem? We can ask the user to confirm his action with his password in the settings screen. We will save the username and password only after successful verification. Finally, we took a step in the backend direction. We can’t verify the user password locally! You can see that implementing local authentication flow with biometry is not simple.
There are a lot of concerns and tradeoffs we have to take, and still, you will find new problems on the horizon. What about resetting the password in the web application created next to the mobile app? If we change the password in the web app, we will break our authentication flow in the mobile app.
We can’t locally set up full biometric authentication for our app, at least not without some drawbacks.
Manual Refresh Token Using Biometry
Previously I mentioned that our application uses a token with auto refresh. The implementation I will describe in this section can not be used in my case. If sessions in your application also are pretty long and your token is auto-refreshing, you probably don’t need to use biometry for refreshing sessions. This approach is the best if your sessions are short and you want to authenticate the user every time he uses the application. Maybe you have already refreshed the token, and this solution fits your environment without any changes on the backend side.
Implementation is simple. We save the refresh token in our secure storage, and after our permit expires, we can refresh the session using biometry to get the previously saved refresh token. We can quickly implement biometry settings inside the app because we can keep both access and refresh tokens in secure storage without concern. Switching ON/OFF biometry will be handled by additional variables like IS_BIOMETRY_ENABLED.
A refresh token is used to get new access tokens with a short lifetime. Remember that the refresh token also has a lifetime, it can be very long, but if we don’t use the app for months, our biometric authentication can fail.
Let’s talk about the logout function in your app. If your backend gives you a logout method, you will probably be unable to use the refresh token after successful logout. The logout function triggered on a backend will clean the user session, and the refresh token will no longer work. As you can see, this implementation of biometry can not be considered a login implementation. We can only refresh a user session. It can be enough for your client, but If you want to create a separate method for login, you will have to go with a complete backend solution.
Backend Authentication
If you want to build an accurate biometry login method, you should use this solution. It requires work on both frontend and backend sides, but this is probably the safest solution you can go with. Using react-native biometrics, we can create a public-private key pair protected with biometry. A basic example can be described below:
When a user decides to enable biometry, we use the
create keys()
function to create a public-private key pair which will be stored in the Keystore. Additionally, a public key is returned by a process, and we can send it to our backend.When a user tries to log in, we call
createSignature()
, and the user is prompted to authenticate with biometry. Successful authentication will return a signature generated from our private key stored in the Keystore. Next, we will send a signature to our backend, and in response, we will get a new access token.When users want to disable or enable biometrics in settings screens, we can use
createKeys()
ordeleteKeys()
to trigger ON/OFF actions.
Comparing this flow to the previous chapters, we do not consume our existing authentication flow. This approach requires creating new login methods on the backend side. It’s worth mentioning that react-native-biometrics was recently updated, and now it supports passcode fallback when Face ID or Touch ID fails.
Another solution for that implementation will be to use separate libraries for biometric authentication and key pair generation.
Summary
This article helps you to find the best solution for your use case. You may want to test biometry with a simple approach or decide to use biometry only for refreshing tokens. Finally, if you want to implement solid biometric authentication you will go with server authentication using biometry. The NFT Reality app is now in a process of developing and is planned to be released in 2023.
References:
Mastering Mobile Development with React Native
In the fast-paced world of mobile app development, React Native stands out as a key player for building versatile and efficient applications. At Mobile Reality, we're not only experts in crafting complex UIs and integrating advanced features like biometrics authentication with React Native, but we're also adept at navigating the nuances between it and other frameworks like Flutter and Xamarin. Dive into our wealth of knowledge:
React Native vs. Flutter vs. Xamarin: A Comparative Analysis
React Native - Continuous Delivery in Bitrise [Part I - iOS]
React Native - Continuous Delivery in Bitrise [Part II - iOS]
React Native Select Pro Docs - self-made open-source library for React Native developers
For those considering the development of a mobile app and weighing the benefits of a web app against a native app, our sales team is available to provide expert advice tailored to your specific needs. Additionally, if you're passionate about crafting cutting-edge mobile applications and looking to take the next step in your career, explore our current job offerings on our careers page. Join us in leading the mobile development scene with React Native and beyond.