Eliminar creche_app/lib/core/auth_provider.dart
This commit is contained in:
parent
9c238482e4
commit
9f584a5874
|
|
@ -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';
|
||||
}
|
||||
Loading…
Reference in New Issue