Skip to content

Setting Up the Biometric Auth SDK

What you'll learn

  • Generating the Biometric Auth SDK with API/Impl split
  • Configuring platform permissions on iOS and Android
  • Using biometric as an unlock gate, not a primary credential
  • Restoring a session after successful biometric verification

Prerequisites

Pro Feature

biometric_auth_sdk requires an active Pro subscription. Run archipelago auth status to confirm your tier.

Step 1: Generate the Biometric Auth SDK

bash
archipelago generate biometric_auth_sdk

No variables are prompted — the brick generates a fixed structure with sane defaults.

Step 2: Understand the Generated Structure

The brick uses the API/Impl split because other features (e.g. a locked settings screen) may need to depend on the biometric contract without pulling in the implementation:

features/biometric_auth/
├── biometric_auth_api/
│   └── lib/src/
│       ├── biometric_auth_sdk.dart       # Abstract SDK contract
│       └── models/
│           └── biometric_result.dart     # Success/failure sealed class
└── biometric_auth_impl/
    └── lib/src/
        ├── biometric_auth_sdk_impl.dart  # local_auth wrapper
        └── di/                           # GetIt module

Step 3: Configure iOS Permissions

Add the Face ID usage description to ios/Runner/Info.plist:

xml
<key>NSFaceIDUsageDescription</key>
<string>We use Face ID to quickly verify your identity.</string>

Touch ID does not require a separate plist key, but the string above is shown for both Face ID and Touch ID prompts on devices that support them.

Step 4: Configure Android Permissions

For devices running API 23–27, add the fingerprint permission to android/app/src/main/AndroidManifest.xml:

xml
<uses-permission android:name="android.permission.USE_FINGERPRINT" />

Devices on API 28+ use USE_BIOMETRIC which the underlying local_auth package declares automatically. You only need this explicit declaration for older API levels.

Step 5: Register the Feature

In your shell app's bootstrap.dart, register the biometric SDK alongside your other features:

dart
import 'package:biometric_auth_impl/biometric_auth_impl.dart';

FeatureRegistry.register(BiometricAuthImpl());

Features that gate behaviour behind biometric verification depend on biometric_auth_api only:

yaml
# In a dependent feature's pubspec.yaml
dependencies:
  biometric_auth_api:
    path: ../../features/biometric_auth/biometric_auth_api

Step 6: Check Availability

Before presenting a biometric prompt, always check whether the device supports it:

dart
final biometricSDK = getIt<BiometricAuthSDK>();
final isSupported = await biometricSDK.isAvailable();

if (!isSupported) {
  // Fall back to PIN / password flow
  return;
}

Android Emulators

isAvailable() returns false on Android emulators that have no enrolled biometrics. Enroll a fingerprint via Settings > Security > Fingerprint inside the emulator, or test on a physical device.

Step 7: Trigger Authentication and Restore the Session

Biometric is an unlock gate, not a primary credential. The SDK verifies the user's identity locally — it does not issue a token. After a successful result, your code is responsible for restoring the app session by calling AuthSDK.loginSuccess():

dart
final result = await biometricSDK.authenticate(
  reason: 'Verify your identity to continue',
);

if (result.isSuccess) {
  // Biometric passed — restore the cached session
  await getIt<AuthSDK>().loginSuccess(userId: cachedUserId);
} else {
  // result.errorMessage contains a localised failure reason
  showErrorSnackbar(result.errorMessage);
}

The cachedUserId should come from your local secure storage (written during the initial password-based login), not from the biometric result itself.

Step 8: Typical Integration — App Resume Guard

A common pattern is to show a biometric prompt whenever the app resumes after being backgrounded:

dart
class AppLifecycleGuard extends StatefulWidget {
  const AppLifecycleGuard({required this.child, super.key});
  final Widget child;

  @override
  State<AppLifecycleGuard> createState() => _AppLifecycleGuardState();
}

class _AppLifecycleGuardState extends State<AppLifecycleGuard>
    with WidgetsBindingObserver {
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      _promptBiometric();
    }
  }

  Future<void> _promptBiometric() async {
    final sdk = getIt<BiometricAuthSDK>();
    if (!await sdk.isAvailable()) return;

    final result = await sdk.authenticate(
      reason: 'Confirm it\'s you to continue',
    );
    if (result.isSuccess) {
      await getIt<AuthSDK>().loginSuccess(userId: cachedUserId);
    }
  }

  @override
  Widget build(BuildContext context) => widget.child;
}

Common Customizations

CustomizationWhere to Change
Custom prompt copyPass a different reason string to authenticate()
Disable on tabletsWrap isAvailable() with a device-type check
Skip on debug buildsGuard with kDebugMode in your resume guard
Biometric + PIN fallbackShow PIN screen when result.isFallback is true

Next Steps

Built by Banua Coder