Join us at TechEx 2024!

From June 5-6 we'll be at the event series for the tech ecosystem.

Learn more
Back to Insights

December 21, 2023

Exploring biometric login using digital signatures

Learn more about how to enhance security with biometric login using digital signatures with tips from our App Solutions Studio.

Why biometrics? The benefits of using biometric login in our applications may not always be apparent.

Most apps you’ve used probably never offered the option to configure this feature. This is often because these apps only require your credentials once and then keep your session open indefinitely, thus there’s less need to simplify the login process. However, some apps frequently request these credentials to enhance security, especially when dealing with sensitive financial or legal information. These apps need to verify the account owner each time the app is used.

This security measure can lead to a frustrating user experience, as users must input their email and password every time they use the application. By implementing biometrics, we can make the login process much faster and easier for the user, improving the experience. While biometrics are not foolproof, they are nearly impossible to replicate, which makes them a more secure option. 

How to implement biometrics

Despite being a native feature on our mobile phones, biometrics are not completely foolproof. The binary “true/false” check can be bypassed by skilled hackers. Therefore, in order to prevent this becoming a threat, we need to add an additional layer of security for our backend to confirm our identity during login. One option is to use a refresh token saved on our device’s keychain.

While useful in adding an additional layer of security, refresh tokens eventually expire, which could happen anywhere within hours, days, or months. Once this happens, the user must re-enter their credentials, negatively impacting the user experience. Additionally, refresh tokens are typically irrevocable. If these tokens have a long lifespan, we won’t be able to prevent that device from accessing the app, as it can create new auth tokens until expiration. The solution here lies in cryptography and digital signatures.

Before we dive into the implementation of biometric login, it’s important to understand the concepts of asymmetric keys and digital signing.

Asymmetric keys are pairs of alphanumeric keys consisting of a public key and a corresponding private key. Each key can encrypt a string that only the other key can decrypt. The security of asymmetric key cryptography hinges on the private key remaining secret, while the public key can be openly distributed without compromising security.

We can accomplish something known as digital signing using this pair of keys. Digital signing involves encrypting a string with our private key and sending it to someone who:

  1. Possesses our public key.
  2. Understands what the original string should look like.

The recipient will decrypt the message using our public key. If the decrypted message matches the original string, this confirms it was encrypted with the private key. This means it was sent by the owner of both keys, or at least someone with access to the private key. Hence, it’s crucial to keep the private key confidential.

It’s worth noting that digital signatures do not assure the confidentiality of the message. This is because anyone can decrypt the message using our public key. However, since the information is not sensitive, this is not a concern.

Understanding the basic flow

Now that we understand how digital signing works, we can use it to verify our identity when logging into our backend server. We can do this by requesting a “magic word” or challenge from the server, signing it, and sending it back for verification.

The following image provides an illustration of our process, although it is important to keep in mind it omits a crucial step.

  1. We create an asymmetric key pair when enabling biometrics on the front-end, storing the private key in the keychain.
  2. We send the public key to the server.
  3. When attempting a biometric login, we request a “challenge” from the server.
  4. We prompt the user for a biometric check (TouchID or FaceID).
  5. Once the user passes the check, we encrypt the challenge using our private key and send it back to the server for verification.
  6. The server decrypts the signature using our public key and confirms that it matches the challenge.
  7. The server responds with an authorization token.

The challenge

It’s important to note that the challenge alters every time we log in. This feature is important because encrypting a static challenge would always yield the same string, thus making it reusable for anyone looking to intercept our login attempts.

From the server’s perspective, it’s crucial to keep track of the last challenge sent and associate it with the user who requested it. This way, the server can verify the signature.

Session management

For each device, only one account can be accessed using biometrics. This is due to multiple reasons: First, the device cannot determine which account to access based on your face. Secondly, from a security perspective it makes little sense to have this feature on multiple accounts as apps with this feature are generally designed for single user accounts.

Typically a user identifier is sent together with the public key and signature. If this step is missed, the backend will not be able to link your public key with your user account. This identifier needs to be stored on the device once biometric login is activated.

Before enabling biometric login for an account, we must ensure no other account has it enabled, which means there’s no other user identifier stored. Maintaining the device ID and keys in sync with the public key stored in the backend is critical.

Using biometrics on multiple devices

Depending on your business logic, you may want to enable biometric login for a single user across multiple devices. However, the private key cannot be shared across devices, which poses a challenge. Here are two possible solutions:

  1. The backend could store a device identifier along with the public key. This would allow the system to determine which public key corresponds to each device. The device identifier should be included in all requests.
  2. The backend could store multiple public keys for each user, without identifiers. When a signature is received, the system would attempt to decrypt it using all of the user’s public keys. This method may become less efficient over time as the number of public keys increases with each device change.

Single device key coordination

If we allow only one device at a time, losing or changing the device requires creating a new pair of public/private keys. Because of this, we must inform the backend of our new public key. Overwriting the previous keys is sufficient for this task.

An issue can emerge when we use a device that still has a stored pair of keys and a user ID, which implies biometric access capability. However, this access attempt will become unsuccessful as the local keys do not align with the remote ones due to overwriting. To address this issue, this requires a mechanism to notify the device of the remote key change. This could be accomplished by having the backend respond with a status code, such as 401. When this error is received, the local keys can be deleted to prevent the login from being triggered again.

Declaration of Cryptography on the App Store

Since our application uses a digital signature, we need to declare the use of cryptography when attempting to publish it on the App Store. This declaration helps us adhere to App Store policies and maintain transparency about the application’s security features:

Export Compliance Information

Does your app use encryption? 

Select ‘Yes’ even if your app only utilizes the standard encryption within Apple’s operating system.

After selecting ‘Yes’, the following question will appear. If cryptography is only used for the digital signature, select ‘Yes’ because it aligns with option (c) on the exemption list:

Does your app qualify for any exemptions as per Category 5, Part 2 of the U.S. Export Administration Regulations?

Yes

No

Ensure that your app matches the criteria of the exemption listed below. You are accountable for the correct classification of your product. Misclassifying your app could result in violating U.S. export laws and may subject you to penalties, including the removal of your app from the App Store.

You can select ‘Yes’ for this question if your app’s encryption is:

(a) Specially designed for medical end-use

(b) Limited to intellectual property and copyright protection

(c) Limited to authentication, digital signature, or the decryption of data or files

(d) Specially designed and limited for banking use or “money transactions”

(e) Limited to “fixed” data compression or coding techniques

You can also select ‘Yes’ if your app aligns with the descriptions provided in Note 4 for Category 5, Part 2 of the U.S. Export Administration Regulations.

It is important to handle this information with care as it could potentially violate U.S. laws.

Additionally, consider adding the following entry to the info.plist file to avoid declaring this each time a new version is uploaded to the App Store.

Pseudocode

To implement biometric login, we only need three functions to begin with:

  • Enable Biometric Login
  • Disable Biometric Login
  • Attempt Biometric Login

Enable biometric login:

function enableBiometricLogin(){
  var publicKey = createAsymmetricKeys();
  
  var status = sendPublicKeyToServer(publicKey);

  if (status == success) {
    storeUserId(userId);
  }
}

Disable biometric login:

function disableBiometricLogin(){
  var status = deleteRemoteKeys();

  if (status == success) {
    deleteLocalkeys();
    deleteUserId();
  }
};

Attempt biometric login:

function attemptBiometricLogin(){
  var areKeysStored = checkStoredKeys();
  var storedUserId = checkUserId();

  if (areKeysStored && storedUserId) {
    var challenge = requestChallenge(storedUserId);

  	var signature = signChallenge(challenge);

    var loginData = sendSignatureToServer(signature, storedUserId);
    
    handleLogin(loginData);	
  }
};

React Native

When developing in React Native, we suggest using the “react-native-biometrics” library. This library handles everything related to cryptography: it creates the key pair, stores them in the device keychain, and creates a digital signature while prompting the user for the biometric check. For a better understanding, please refer to the official react-native-biometrics documentation.

Enable biometric login:

const enableBiometricLogin = async (userId) => {
  const { publicKey } = await rnBiometrics.createKeys();

  const { status } = await sendPublicKey(userId, publicKey);

  if (status === 'success') {
    storeUserId(userId);
  } else {
    await rnBiometrics.deleteKeys();
  }
};

Disable biometric login:

const disableBiometricLogin = async (userId) => {
  const { status } = await deleteRemotePublicKey(userId);

  if (status === 'success') {
    await rnBiometrics.deleteKeys();
    deleteStoredUserId();
  }
};

Attempt biometric login:

const attemptBiometricLogin = async (userId) => {
  const { keysExist } = await rnBiometrics.biometricKeysExist();
  
  if (keysExist) {
    const { challenge } = await requestChallenge(userId);
  
    const { success, signature } = await rnBiometrics.createSignature({
              	payload: challenge,
              });

    if (success) {
      const { status, authToken } = await sendSignature(userId, signature);
        if (status === 'success') {
          handleLogin(authToken):
		}
	}
};

Selecting a Device ID

For multi-device usage in React Native, consider using a unique device ID. This can be achieved with react-native-device-info, specifically the getUniqueId() method. This method provides an ID unique to each device, but note that the ID format varies between iOS and Android.

Conclusion

In conclusion, biometric login systems are a crucial tool for enhancing both security and user experience. They offer a fast, easy, and secure way to verify user identity, significantly reducing the risk of password leaks and unauthorized access. However, implementing such systems requires a deep understanding of cryptography, digital signatures, and session management. Despite these complexities, the increased security and improved user experience make biometric login systems a worthwhile investment for apps dealing with sensitive information.

Avatar photo

By Lorenzo Boga

Mobile Studio Developer

Lorenzo Boga is a Mobile Developer at Qubika. Alongside his professional work, he is pursuing a degree in Software Engineering. Lorenzo is passionate about creating impactful mobile applications and continuously expanding his skills in software development.

News and things that inspire us

Receive regular updates about our latest work

Let’s work together

Get in touch with our experts to review your idea or product, and discuss options for the best approach

Get in touch