Como implementar biometria e autenticação local em apps mobile

1. Introdução à autenticação local e biometria em dispositivos móveis

1.1. Conceitos fundamentais: o que é autenticação local e por que usá-la

Autenticação local é o processo de verificar a identidade do usuário diretamente no dispositivo, sem depender de servidores remotos. Ela utiliza recursos nativos do hardware — como sensores biométricos ou armazenamento seguro — para validar que a pessoa que está acessando o app é realmente o proprietário do dispositivo. A principal vantagem é a velocidade: o usuário não precisa digitar senhas longas ou esperar por conexão de rede. Além disso, a autenticação local reduz a superfície de ataque, já que as credenciais nunca saem do aparelho.

1.2. Diferença entre autenticação biométrica e métodos tradicionais

Métodos tradicionais (PIN, padrão, senha alfanumérica) dependem de algo que o usuário sabe. Eles são vulneráveis a ataques de observação (shoulder surfing) e podem ser esquecidos. Já a autenticação biométrica usa algo que o usuário é: impressão digital, mapa facial ou íris. No iOS, o Face ID cria um modelo matemático do rosto usando o sensor TrueDepth; no Android, o BiometricPrompt gerencia sensores de impressão digital (capacitivos, ópticos ou ultrassônicos) e reconhecimento facial. A biometria oferece melhor experiência do usuário, mas exige fallbacks para quando o sensor falha.

1.3. Cenários de uso comuns

  • Desbloqueio de app: abrir um banco ou cofre digital sem digitar senha.
  • Confirmação de pagamentos: autorizar transações no Apple Pay ou Google Pay.
  • Acesso a dados sensíveis: exibir histórico médico ou documentos fiscais.
  • Reautenticação em sessões longas: exigir nova verificação após 5 minutos de inatividade.

2. Plataformas suportadas e APIs nativas

2.1. Android: BiometricPrompt e Android Keystore

A partir da API nível 28 (Android 9), o BiometricPrompt unifica todos os métodos biométricos. Ele interage com o Android Keystore, que armazena chaves criptográficas em hardware isolado (TEE ou StrongBox). O fluxo básico:

1. Criar um BiometricPrompt com callback de sucesso/erro.
2. Configurar BiometricPrompt.PromptInfo com título, subtítulo e descrição.
3. Chamar biometricPrompt.authenticate().

2.2. iOS: LocalAuthentication e Secure Enclave

O framework LocalAuthentication fornece o LAContext, que verifica a presença de biometria cadastrada e executa a autenticação. O Secure Enclave é um coprocessador que gerencia chaves e nunca expõe os dados biométricos ao sistema operacional.

let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
    context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
                           localizedReason: "Desbloqueie o app") { success, error in
        // tratar resultado
    }
}

2.3. Limitações e fallbacks

  • Biometria não disponível: dispositivo sem sensor ou com sensor danificado.
  • Biometria não cadastrada: usuário não configurou impressão digital ou Face ID.
  • Falha na leitura: dedo molhado, mudança facial (barba, óculos).
  • Fallback obrigatório: oferecer PIN ou senha do dispositivo como alternativa.

3. Implementação prática com React Native (Expo e CLI)

3.1. Usando expo-local-authentication

O pacote expo-local-authentication abstrai as APIs nativas. Instale com:

expo install expo-local-authentication

3.2. Exemplo de código

import * as LocalAuthentication from 'expo-local-authentication';

async function authenticateWithBiometrics() {
  // 1. Verificar disponibilidade
  const hasHardware = await LocalAuthentication.hasHardwareAsync();
  const isEnrolled = await LocalAuthentication.isEnrolledAsync();

  if (!hasHardware || !isEnrolled) {
    Alert.alert('Biometria não disponível');
    return false;
  }

  // 2. Solicitar autenticação
  const result = await LocalAuthentication.authenticateAsync({
    promptMessage: 'Autentique-se para acessar o app',
    fallbackLabel: 'Usar senha',
    cancelLabel: 'Cancelar',
  });

  // 3. Tratar resultado
  if (result.success) {
    console.log('Autenticado com sucesso');
    return true;
  } else {
    console.log('Falha na autenticação:', result.error);
    return false;
  }
}

3.3. Tratamento de erros

// Possíveis erros retornados por result.error:
// 'user_cancel' - usuário cancelou
// 'not_enrolled' - nenhuma biometria cadastrada
// 'not_available' - hardware ausente
// 'lockout' - muitas tentativas, aguardar 30s

4. Implementação prática com Flutter

4.1. Pacote local_auth

Adicione ao pubspec.yaml:

dependencies:
  local_auth: ^2.1.0

Configure permissões no Android (AndroidManifest.xml):

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

No iOS, adicione ao Info.plist:

<key>NSFaceIDUsageDescription</key>
<string>Este app usa Face ID para autenticação segura</string>

4.2. Exemplo de código

import 'package:local_auth/local_auth.dart';

final LocalAuthentication auth = LocalAuthentication();

Future<bool> authenticate() async {
  // 1. Verificar disponibilidade
  final bool canAuthenticate = await auth.canCheckBiometrics || await auth.isDeviceSupported();
  if (!canAuthenticate) return false;

  // 2. Solicitar autenticação
  try {
    final bool authenticated = await auth.authenticate(
      localizedReason: 'Autentique-se para continuar',
      options: const AuthenticationOptions(
        biometricOnly: true, // apenas biometria, sem PIN
        stickyAuth: true,    // mantém sessão ativa
      ),
    );
    return authenticated;
  } catch (e) {
    print('Erro: $e');
    return false;
  }
}

4.3. Gerenciamento de fallback

Future<bool> authenticateWithFallback() async {
  bool authenticated = await authenticate();
  if (!authenticated) {
    // Fallback para PIN do dispositivo
    authenticated = await auth.authenticate(
      localizedReason: 'Use seu PIN ou padrão',
      options: const AuthenticationOptions(
        biometricOnly: false, // permite senha do dispositivo
      ),
    );
  }
  return authenticated;
}

5. Armazenamento seguro de credenciais após autenticação

5.1. Keychain (iOS) e EncryptedSharedPreferences (Android)

Após a biometria, você pode liberar uma chave criptográfica que descriptografa dados armazenados. No iOS, o Keychain armazena tokens com proteção kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. No Android, o EncryptedSharedPreferences usa AES-256 com chave derivada do Android Keystore.

5.2. Integração com React Native

// Instalar: npm install react-native-keychain
import * as Keychain from 'react-native-keychain';

async function saveToken(token: string) {
  // A biometria protege o acesso ao Keychain
  await Keychain.setGenericPassword('session_token', token, {
    service: 'com.meuapp.token',
    accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET,
    accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
  });
}

async function getToken() {
  const credentials = await Keychain.getGenericPassword({
    authenticationPrompt: { title: 'Autentique-se para recuperar o token' },
  });
  return credentials ? credentials.password : null;
}

5.3. Integração com Flutter

// Instalar: flutter pub add flutter_secure_storage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final storage = FlutterSecureStorage();

Future<void> saveSecureToken(String token) async {
  await storage.write(
    key: 'auth_token',
    value: token,
    aOptions: AndroidOptions(encryptedSharedPreferences: true),
    iOptions: IOSOptions(
      accessibility: KeychainAccessibility.first_unlock_this_device,
      authenticationType: AuthenticationType.biometrics,
    ),
  );
}

Future<String?> readSecureToken() async {
  return await storage.read(
    key: 'auth_token',
    iOptions: IOSOptions(
      authenticationType: AuthenticationType.biometrics,
    ),
  );
}

6. Boas práticas de segurança e experiência do usuário

6.1. Nunca armazenar a biometria em si

A biometria nunca deve ser salva no seu app. Use-a apenas como gatilho para liberar chaves criptográficas armazenadas no Keystore/Secure Enclave. O sistema operacional gerencia os templates biométricos.

6.2. Feedback visual e sonoro

  • No Android, o BiometricPrompt exibe animação nativa.
  • No iOS, o Face ID mostra um ícone de rosto animado.
  • Adicione feedback tátil (HapticFeedback) em apps customizados.

6.3. Política de timeout e reautenticação

Defina um tempo de sessão (ex: 5 minutos). Após esse período, exija nova autenticação biométrica. Use um timer no estado global do app:

// Exemplo conceitual
sessionTimer = Timer(Duration(minutes: 5), () {
  setState(() { isSessionValid = false; });
});

7. Testes e depuração em diferentes dispositivos

7.1. Simulando biometria

  • Android Emulator: AVD Manager → Configurações → Fingerprint → "Touch to simulate fingerprint".
  • iOS Simulator: Hardware → Face ID → Enrolled. Use xcrun simctl spawn booted para simular rosto válido/inválido.

7.2. Testes em dispositivos reais

  • Sensores ultrassônicos (Samsung Galaxy S21): funcionam com dedo molhado.
  • Sensores ópticos (muitos chineses): falham com luz solar direta.
  • Face ID: testar com óculos escuros, máscara cirúrgica (iOS 15.4+).

7.3. Logs e monitoramento

Use ferramentas como Sentry ou Crashlytics para capturar falhas de autenticação. Registre o tipo de erro sem expor dados sensíveis:

// Log seguro
analytics.logEvent('biometric_failure', {
  'error_code': result.error,
  'device_model': DeviceInfo.getModel(),
});

8. Considerações finais e próximos passos

8.1. Comparação com autenticação por terceiros

  • Apple ID / Google Sign-In: autenticação remota, útil para criar conta, mas não substitui a biometria local para reautenticação rápida.
  • Combinação ideal: use biometria local para desbloqueio de sessão e Apple/Google para login inicial.

8.2. Tendências futuras

  • Autenticação contínua: monitorar comportamento do usuário (digitação, marcha) para validar identidade durante toda a sessão.
  • Autenticação passiva: usar sensores (Wi-Fi, Bluetooth) para detectar ambiente confiável.

8.3. Checklist de implementação para produção

  • [ ] Fallback para PIN/senha do dispositivo.
  • [ ] Tratamento de lockout (muitas tentativas).
  • [ ] Armazenamento seguro de tokens com biometria.
  • [ ] Timeout de sessão configurável.
  • [ ] Testes em pelo menos 5 dispositivos reais diferentes.
  • [ ] Logs de falhas sem expor dados biométricos.

Referências