Skip to main content

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

    import { useTransfer } from "@chipi-stack/chipi-expo";
    import type { ChainToken } from "@chipi-stack/chipi-expo";
    
    const storedWallet = await SecureStore.getItemAsync("wallet");
    const pin = await SecureStore.getItemAsync("wallet_pin",{
      requireAuthentication: true,
    });

    const token = await getToken();
    if (!token) {
      setError("No bearer token found");
      return;
    }

    const transferResponse = await transferAsync({
      params: {
        encryptKey: pin,
        wallet: JSON.parse(storedWallet),
        token: "USDC" as ChainToken,
        recipient: recipientAddress,
        amount: Number(amount),
      },
      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:
// Create Wallet Screen with biometric authentication

import { useCreateWallet } from '@chipi-stack/chipi-expo';
import { useAuth, useUser } 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 { user } = useUser();
  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();
      if (!token) {
        setError('No bearer token found');
        return;
      }

      const result = await createWalletAsync({
        params: {
          encryptKey: pin,
          externalUserId: user?.id || '',
        },
        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>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#fff',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginBottom: 8,
    color: '#11181C',
  },
  subtitle: {
    fontSize: 16,
    color: '#687076',
    marginBottom: 24,
  },
  errorText: {
    color: '#ff6b6b',
    fontSize: 14,
    marginTop: 8,
  },
  walletDetails: {
    marginTop: 32,
    padding: 16,
    backgroundColor: '#f5f5f5',
    borderRadius: 8,
  },
  detailHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 16,
  },
  detailTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#11181C',
  },
  viewContract: {
    fontSize: 14,
    color: '#0a7ea4',
    fontWeight: '600',
  },
  detailItem: {
    marginBottom: 12,
  },
  detailLabel: {
    fontSize: 14,
    fontWeight: '600',
    color: '#687076',
    marginBottom: 4,
  },
  detailValue: {
    fontSize: 14,
    color: '#11181C',
    fontFamily: 'monospace',
  },
});

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.