Overview

Biometric authentication provides a secure and convenient way for users to authenticate without remembering PINs or passwords. The Chipi SDK supports fingerprint and face recognition on compatible devices.

Prerequisites

  • Expo SDK 49 or later
  • Chipi SDK installed and configured
  • Device with biometric capabilities (fingerprint sensor or face recognition)

Installation

First, install the required dependencies:
npx expo install expo-local-authentication

Basic Implementation

This is the minimal configuration required to enroll in and use biometrics instead of a PIN to sign transactions.
1

Register biometrics along with the PIN

await SecureStore.setItemAsync("wallet_pin", pin, {
 requireAuthentication: true,     
});
2

Read the PIN to trigger the biometric prompt

    const storedWallet = await SecureStore.getItemAsync("wallet");
    const pin = await SecureStore.getItemAsync("wallet_pin",{
      requireAuthentication: true,
    });

    const token = await getToken({ template: "chipi-sdk-expo" });
    if (!token) {
      setError("No bearer token found");
      return;
    }


    const transferResponse = await transferAsync({
      params: {
        encryptKey: pin,
        wallet: JSON.parse(storedWallet),
        contractAddress: USDC_CONTRACT,
        recipient: recipientAddress,
        amount: amount,
        decimals: 6,
      },
      bearerToken: token,
      },
    );

Example

Here’s a simple example of how to implement a secure transfer flow using biometric authentication:
To use biometrics for authentication, you first need to create and securely store a wallet with a PIN (which can be protected by biometrics). Here’s an example wallet creation screen using the useCreateWallet hook:
// screens/CreateWalletScreen.tsx
import { useCreateWallet } from "@chipi-pay/chipi-expo";
import { useAuth } from "@clerk/clerk-expo";
import * as SecureStore from "expo-secure-store";
import { useState } from "react";
import { StyleSheet, Text, View, Alert, Linking } from "react-native";
import { PrimaryButton } from "@/components/ui/PrimaryButton";
import { SimpleInput } from "@/components/ui/SimpleInput";

export const CreateWalletView = () => {
  const { getToken } = useAuth();
  const [pin, setPin] = useState<string>("");
  const [error, setError] = useState("");
  const { createWalletAsync, isLoading } = useCreateWallet();
  const [walletData, setWalletData] = useState<any>(null);

  const handleCreateWallet = async () => {
    try {
      setError("");
      const token = await getToken({ template: "chipi-sdk-expo" });
      if (!token) {
        setError("No bearer token found");
        return;
      }

      const result = await createWalletAsync({
        encryptKey: pin,
        bearerToken: token,
      });

      // Save wallet data to local storage, protected by biometrics
      await SecureStore.setItemAsync(
        "wallet",
        JSON.stringify(result.wallet)
      );
      await SecureStore.setItemAsync("wallet_pin", pin, {
        requireAuthentication: true,
      });

      setWalletData(result);
      Alert.alert("Success", "Wallet successfully created!");
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      setError("Error creating wallet: " + errorMessage);
    }
  };

  const openStarkscan = (address: string) => {
    const url = `https://starkscan.co/contract/${address}`;
    Linking.openURL(url);
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Create New Wallet</Text>
      <Text style={styles.subtitle}>Set a PIN to secure your wallet</Text>

      <SimpleInput
        placeholder="Enter your PIN (min 4 digits)"
        value={pin}
        onChangeText={setPin}
        keyboardType="numeric"
        maxLength={6}
      />

      {error ? <Text style={styles.errorText}>{error}</Text> : null}

      <PrimaryButton
        title={isLoading ? "Creating..." : "Create Wallet"}
        active={pin.length >= 4 && !isLoading}
        onPress={handleCreateWallet}
      />

      {walletData && (
        <View style={styles.walletDetails}>
          <View style={styles.detailHeader}>
            <Text style={styles.detailTitle}>Wallet Details</Text>
            <Text
              style={styles.viewContract}
              onPress={() => openStarkscan(walletData.wallet.accountAddress)}
            >
              View Contract
            </Text>
          </View>

          <View style={styles.detailItem}>
            <Text style={styles.detailLabel}>Address:</Text>
            <Text style={styles.detailValue} numberOfLines={2}>
              {walletData.wallet.accountAddress}
            </Text>
          </View>

          {walletData.wallet.txHash && (
            <View style={styles.detailItem}>
              <Text style={styles.detailLabel}>TX Hash:</Text>
              <Text style={styles.detailValue} numberOfLines={2}>
                {walletData.wallet.txHash}
              </Text>
            </View>
          )}
        </View>
      )}
    </View>
  );
};

Useful resources

  • Making an EAS Build in Expo

Next Steps

Now that you have biometric authentication working, you can:
  • Integrate it with other Chipi SDK features
  • Add biometric authentication to wallet operations
  • Implement multi-factor authentication combining biometrics and PINs
  • Add biometric authentication to transaction signing
For more information, check out the Expo Local Authentication documentation.