better_local_auth

Enhanced Flutter plugin for local authentication with required cryptoObject support for Android security compliance.

Overview

better_local_auth is a fork and enhancement of the local_auth-3.0.1 Flutter package. It addresses security concerns identified by static analysis tools like Ostorlab by making the Android cryptoObject parameter required for biometric authentication. This change ensures that apps using biometric authentication on Android are not flagged as insecure.

Key Features

  • Security First: Requires cryptoObject on Android to prevent security warnings from static analysis tools
  • Simplified Maintenance: Consolidates multiple packages into one, removes unused platform support
  • Backward Compatibility: Maintains API compatibility with local_auth-3.0.1 where possible
  • Enhanced Features: Adds optional keychain support for iOS secure storage
  • Clear Migration Path: Provides comprehensive migration guidance for developers

Platform Support

Platform Supported Changes from local_auth
Android ✅ Yes cryptoObject required
iOS ✅ Yes keychainOptions required
Windows ❌ No Removed
Web ❌ No Removed
Linux ❌ No Removed
macOS ❌ No Removed

Installation

Add better_local_auth to your pubspec.yaml:

dependencies:
  better_local_auth: ^1.0.0

iOS Setup

For iOS, you need to add the NSFaceIDUsageDescription key to your app's Info.plist file to use Face ID:

<key>NSFaceIDUsageDescription</key>
<string>Your reason for using Face ID (e.g., "App needs to authenticate using faces.")</string>

This description will be shown to users when they are asked for Face ID permission.

Usage

Basic Authentication

import 'package:better_local_auth/better_local_auth.dart';
import 'package:better_local_auth/src/types/types.dart';

final auth = LocalAuthentication();

// Create required cryptoObject and keychainOptions
final cryptoObject = CryptoObject.forSignature(
  keyAlias: 'my_app_key',
  data: Uint8List.fromList('data_to_sign'.codeUnits),
);

final keychainOptions = KeychainOptions(
  serviceName: 'com.example.app',
  accountName: 'user_token',
);

try {
  final bool didAuthenticate = await auth.authenticate(
    localizedReason: 'Please authenticate to access secure data',
    cryptoObject: cryptoObject,
    keychainOptions: keychainOptions,
  );
  
  if (didAuthenticate) {
    // User authenticated successfully
  }
} on ArgumentError catch (e) {
  // Handle missing cryptoObject or keychainOptions
  print('Security requirement missing: ${e.message}');
} on LocalAuthException catch (e) {
  // Handle authentication errors
  print('Authentication failed: ${e.code}');
}

Enhanced API

For additional features, use BetterLocalAuthentication:

import 'package:better_local_auth/better_local_auth.dart';

final betterAuth = BetterLocalAuthentication();

// Use factory methods for common crypto objects
final signatureCrypto = BetterLocalAuthentication.createSignatureCryptoObject(
  keyAlias: 'signing_key',
  dataToSign: Uint8List.fromList('important_data'.codeUnits),
);

// Enhanced authentication with detailed results
final result = await betterAuth.authenticateEnhanced(
  localizedReason: 'Authenticate to sign transaction',
  cryptoObject: signatureCrypto,
  keychainOptions: KeychainOptions(
    serviceName: 'com.example.app',
    accountName: 'transaction_signing',
  ),
);

if (result.authenticated) {
  print('Authentication successful');
  print('Crypto result: ${result.cryptoResult}');
  print('Keychain key: ${result.keychainStorageKey}');
}

Device Capabilities

final LocalAuthentication auth = LocalAuthentication();

// Check if device supports biometrics
final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics;

// Check if device supports any authentication
final bool canAuthenticate = await auth.isDeviceSupported();

// Get enrolled biometrics
final List<BiometricType> availableBiometrics = await auth.getAvailableBiometrics();

Migration from local_auth

Breaking Changes

  1. Android cryptoObject Requirement: The cryptoObject parameter is now required for Android authentication
  2. iOS keychainOptions Requirement: The keychainOptions parameter is now required for iOS authentication
  3. Platform Support: Windows, Web, Linux, and macOS platforms are no longer supported

Migration Steps

  1. Update Dependencies: Replace local_auth with better_local_auth in your pubspec.yaml
  2. Add Required Parameters: Update all authenticate() calls to include cryptoObject and keychainOptions
  3. Remove Platform-Specific Code: Remove any code specific to Windows, Web, Linux, or macOS
  4. Test on Both Platforms: Verify authentication works correctly on Android and iOS

Migration Example

Before (local_auth):

import 'package:local_auth/local_auth.dart';

final auth = LocalAuthentication();

final bool didAuthenticate = await auth.authenticate(
  localizedReason: 'Please authenticate',
);

After (better_local_auth):

import 'package:better_local_auth/better_local_auth.dart';
import 'package:better_local_auth/src/types/types.dart';

final auth = LocalAuthentication();

// Create required security objects
final cryptoObject = CryptoObject.forSignature(
  keyAlias: 'my_app_key',
  data: Uint8List.fromList('auth_data'.codeUnits),
);

final keychainOptions = KeychainOptions(
  serviceName: 'com.example.app',
  accountName: 'authentication',
);

final bool didAuthenticate = await auth.authenticate(
  localizedReason: 'Please authenticate',
  cryptoObject: cryptoObject,
  keychainOptions: keychainOptions,
);

Common Migration Issues

  1. Missing cryptoObject on Android: Will throw ArgumentError with clear message
  2. Missing keychainOptions on iOS: Will throw ArgumentError with clear message
  3. Platform-specific code: Remove any conditional code for unsupported platforms

Security Considerations

Android CryptoObject Requirement

The cryptoObject requirement ensures that biometric authentication on Android performs cryptographic operations, which:

  1. Prevents security warnings from static analysis tools like Ostorlab
  2. Ensures authentication results can be cryptographically verified
  3. Provides secure key storage in Android's Keystore system
  4. Enables secure operations like data signing and encryption

iOS Keychain Integration

The keychainOptions requirement ensures secure storage of authentication results in iOS Keychain, which:

  1. Provides hardware-backed secure storage
  2. Enables secure token storage and retrieval
  3. Supports biometric-protected keychain items
  4. Maintains security even if device is lost or stolen

API Reference

LocalAuthentication Class

Maintains API compatibility with local_auth-3.0.1 but requires additional security parameters.

Methods:

  • authenticate() - Authenticate user with biometrics (requires cryptoObject and keychainOptions)
  • stopAuthentication() - Cancel in-progress authentication
  • canCheckBiometrics - Check if device supports biometrics
  • isDeviceSupported() - Check if device supports any authentication
  • getAvailableBiometrics() - Get list of enrolled biometrics

BetterLocalAuthentication Class

Enhanced API with additional features and factory methods.

Methods:

  • createSignatureCryptoObject() - Factory method for signature crypto objects
  • createCipherCryptoObject() - Factory method for encryption/decryption crypto objects
  • authenticateEnhanced() - Enhanced authentication with detailed results

Data Models

  • CryptoObject - Abstract class for cryptographic operations
  • KeychainOptions - Options for iOS keychain integration
  • AuthenticationOptions - Extended options including security parameters
  • AuthResult - Enhanced authentication result with crypto and keychain data

Troubleshooting

Common Issues

  1. ArgumentError: cryptoObject is required

    • Solution: Create and provide a CryptoObject using factory methods
  2. ArgumentError: keychainOptions is required

    • Solution: Create and provide KeychainOptions with service and account names
  3. Platform not supported

    • Solution: Remove code for Windows, Web, Linux, or macOS platforms
  4. Biometric hardware not available

    • Solution: Check device capabilities before attempting authentication

Debugging Tips

  1. Enable debug logging in development
  2. Check device biometric enrollment status
  3. Verify keychain permissions on iOS
  4. Test with different cryptoObject configurations

Contributing

Contributions are welcome! Please see the CONTRIBUTING.md file for details.

License

This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.

Acknowledgments

  • Based on the original local_auth package from the Flutter team
  • Security improvements inspired by static analysis tool findings
  • Community feedback and testing

Libraries

better_local_auth
Enhanced Flutter plugin for local authentication with required cryptoObject support for Android security compliance.
local_auth