Eliminar creche_app/lib/core/auth_provider.dart

This commit is contained in:
Alberto 2026-03-11 19:19:09 +00:00
parent 9c238482e4
commit 9f584a5874
1 changed files with 0 additions and 162 deletions

View File

@ -1,162 +0,0 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:local_auth/local_auth.dart';
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart' as http;
import '/models/profile.dart';
import '/models/daily_access_approval.dart';
import '/models/creche_settings.dart';
part 'auth_provider.g.dart';
// Provider para a sessão atual
@riverpod
Future<Session?> currentSession(CurrentSessionRef ref) {
return Future.value(Supabase.instance.client.auth.currentSession);
}
// Provider para o perfil do utilizador logado
@riverpod
Future<Profile?> currentProfile(CurrentProfileRef ref) async {
final session = await ref.watch(currentSessionProvider.future);
if (session == null) return null;
final data = await Supabase.instance.client
.from('profiles')
.select()
.eq('user_id', session.user.id)
.maybeSingle();
if (data == null) return null;
return Profile.fromMap(data);
}
@riverpod
class AuthNotifier extends _$AuthNotifier {
final _storage = const FlutterSecureStorage();
final _localAuth = LocalAuthentication();
@override
Future<void> build() async {
final token = await _storage.read(key: 'access_token');
if (token != null) {
try {
await Supabase.instance.client.auth.setSession(token);
ref.invalidate(currentSessionProvider);
ref.invalidate(currentProfileProvider);
} catch (_) {
await _storage.delete(key: 'access_token');
}
}
}
Future<void> signIn(String email, String password) async {
final supabase = Supabase.instance.client;
final response = await supabase.auth.signInWithPassword(
email: email,
password: password,
);
await _storage.write(
key: 'access_token', value: response.session!.accessToken);
await _performSecurityChecks();
ref.invalidate(currentSessionProvider);
ref.invalidate(currentProfileProvider);
}
Future<void> biometricSignIn() async {
final canAuthenticate = await _localAuth.canCheckBiometrics;
if (!canAuthenticate) throw Exception('Biometria não disponível');
final didAuthenticate = await _localAuth.authenticate(
localizedReason: 'Autentique para entrar no Diário do Candengue',
);
if (!didAuthenticate) throw Exception('Falha na autenticação biométrica');
final token = await _storage.read(key: 'access_token');
if (token == null) throw Exception('Sem sessão guardada. Faça login primeiro.');
await Supabase.instance.client.auth.setSession(token);
await _performSecurityChecks();
ref.invalidate(currentSessionProvider);
ref.invalidate(currentProfileProvider);
}
Future<void> _performSecurityChecks() async {
final supabase = Supabase.instance.client;
final user = supabase.auth.currentUser;
if (user == null) throw Exception('Utilizador não autenticado');
// Verificar IP
final ipRes = await http.get(Uri.parse('https://api.ipify.org?format=text'));
final ip = ipRes.body.trim();
// Verificar localização
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
// Carregar configurações da creche
final settingsData =
await supabase.from('creche_settings').select().single();
final settings = CrecheSettings.fromMap(settingsData);
// Verificar IP (se lista não vazia)
if (settings.allowedIps.isNotEmpty && !settings.allowedIps.contains(ip)) {
throw Exception('Endereço IP não autorizado ($ip)');
}
// Verificar geofence (se coordenadas configuradas)
if (settings.geofenceLat != null && settings.geofenceLng != null) {
final distance = Geolocator.distanceBetween(
position.latitude,
position.longitude,
settings.geofenceLat!,
settings.geofenceLng!,
);
if (distance > settings.geofenceRadiusMeters) {
throw Exception(
'Fora da área permitida da creche (${distance.toInt()}m)');
}
}
// Verificar aprovação diária para teacher/staff
final profileData = await supabase
.from('profiles')
.select()
.eq('user_id', user.id)
.single();
final profile = Profile.fromMap(profileData);
if (profile.role == 'teacher' || profile.role == 'staff') {
final today = DateTime.now().toIso8601String().split('T')[0];
final approvalData = await supabase
.from('daily_access_approvals')
.select()
.eq('user_id', profile.id)
.eq('approval_date', today)
.maybeSingle();
if (approvalData == null ||
DailyAccessApproval.fromMap(approvalData).status != 'approved') {
// Lança exceção especial para redirecionar para sala de espera
throw WaitingApprovalException();
}
}
}
Future<void> signOut() async {
await Supabase.instance.client.auth.signOut();
await _storage.delete(key: 'access_token');
ref.invalidate(currentSessionProvider);
ref.invalidate(currentProfileProvider);
}
}
// Exceção especial para sala de espera
class WaitingApprovalException implements Exception {
@override
String toString() => 'Acesso pendente de aprovação da Diretora';
}