first commit

This commit is contained in:
DahoudG
2025-08-20 21:00:35 +00:00
commit b2a23bdf89
583 changed files with 243074 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
import 'dart:convert';
import '../models/login_response.dart';
import '../models/user_info.dart';
/// Service de stockage en mémoire des tokens (temporaire pour contourner Java 21)
class MemoryTokenStorage {
static final MemoryTokenStorage _instance = MemoryTokenStorage._internal();
factory MemoryTokenStorage() => _instance;
MemoryTokenStorage._internal();
// Stockage en mémoire
final Map<String, String> _storage = {};
static const String _accessTokenKey = 'access_token';
static const String _refreshTokenKey = 'refresh_token';
static const String _userInfoKey = 'user_info';
static const String _expiresAtKey = 'expires_at';
static const String _refreshExpiresAtKey = 'refresh_expires_at';
/// Sauvegarde les données d'authentification
Future<void> saveAuthData(LoginResponse loginResponse) async {
try {
_storage[_accessTokenKey] = loginResponse.accessToken;
_storage[_refreshTokenKey] = loginResponse.refreshToken;
_storage[_userInfoKey] = jsonEncode(loginResponse.user.toJson());
_storage[_expiresAtKey] = loginResponse.expiresAt.toIso8601String();
_storage[_refreshExpiresAtKey] = loginResponse.refreshExpiresAt.toIso8601String();
} catch (e) {
throw StorageException('Erreur lors de la sauvegarde des données d\'authentification: $e');
}
}
/// Récupère le token d'accès
Future<String?> getAccessToken() async {
return _storage[_accessTokenKey];
}
/// Récupère le refresh token
Future<String?> getRefreshToken() async {
return _storage[_refreshTokenKey];
}
/// Récupère les informations utilisateur
Future<UserInfo?> getUserInfo() async {
try {
final userJson = _storage[_userInfoKey];
if (userJson == null) return null;
final userMap = jsonDecode(userJson) as Map<String, dynamic>;
return UserInfo.fromJson(userMap);
} catch (e) {
throw StorageException('Erreur lors de la récupération des informations utilisateur: $e');
}
}
/// Récupère la date d'expiration du token d'accès
Future<DateTime?> getTokenExpirationDate() async {
try {
final expiresAtString = _storage[_expiresAtKey];
if (expiresAtString == null) return null;
return DateTime.parse(expiresAtString);
} catch (e) {
throw StorageException('Erreur lors de la récupération de la date d\'expiration: $e');
}
}
/// Récupère la date d'expiration du refresh token
Future<DateTime?> getRefreshTokenExpirationDate() async {
try {
final expiresAtString = _storage[_refreshExpiresAtKey];
if (expiresAtString == null) return null;
return DateTime.parse(expiresAtString);
} catch (e) {
throw StorageException('Erreur lors de la récupération de la date d\'expiration du refresh token: $e');
}
}
/// Vérifie si l'utilisateur est authentifié
Future<bool> hasValidToken() async {
final token = await getAccessToken();
if (token == null) return false;
final expirationDate = await getTokenExpirationDate();
if (expirationDate == null) return false;
return DateTime.now().isBefore(expirationDate);
}
/// Efface toutes les données d'authentification
Future<void> clearAll() async {
_storage.clear();
}
/// Met à jour uniquement les tokens
Future<void> updateTokens({
required String accessToken,
required String refreshToken,
required DateTime expiresAt,
required DateTime refreshExpiresAt,
}) async {
_storage[_accessTokenKey] = accessToken;
_storage[_refreshTokenKey] = refreshToken;
_storage[_expiresAtKey] = expiresAt.toIso8601String();
_storage[_refreshExpiresAtKey] = refreshExpiresAt.toIso8601String();
}
}
/// Exception personnalisée pour les erreurs de stockage
class StorageException implements Exception {
final String message;
StorageException(this.message);
@override
String toString() => 'StorageException: $message';
}

View File

@@ -0,0 +1,246 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:injectable/injectable.dart';
import '../models/login_response.dart';
import '../models/user_info.dart';
/// Service de stockage sécurisé des tokens d'authentification
@singleton
class SecureTokenStorage {
static const String _accessTokenKey = 'access_token';
static const String _refreshTokenKey = 'refresh_token';
static const String _userInfoKey = 'user_info';
static const String _expiresAtKey = 'expires_at';
static const String _refreshExpiresAtKey = 'refresh_expires_at';
static const String _biometricEnabledKey = 'biometric_enabled';
// Utilise SharedPreferences temporairement pour Android
Future<SharedPreferences> get _prefs => SharedPreferences.getInstance();
/// Sauvegarde les données d'authentification
Future<void> saveAuthData(LoginResponse loginResponse) async {
try {
final prefs = await _prefs;
await Future.wait([
prefs.setString(_accessTokenKey, loginResponse.accessToken),
prefs.setString(_refreshTokenKey, loginResponse.refreshToken),
prefs.setString(_userInfoKey, jsonEncode(loginResponse.user.toJson())),
prefs.setString(_expiresAtKey, loginResponse.expiresAt.toIso8601String()),
prefs.setString(_refreshExpiresAtKey, loginResponse.refreshExpiresAt.toIso8601String()),
]);
} catch (e) {
throw StorageException('Erreur lors de la sauvegarde des données d\'authentification: $e');
}
}
/// Récupère le token d'accès
Future<String?> getAccessToken() async {
try {
final prefs = await _prefs;
return prefs.getString(_accessTokenKey);
} catch (e) {
throw StorageException('Erreur lors de la récupération du token d\'accès: $e');
}
}
/// Récupère le refresh token
Future<String?> getRefreshToken() async {
try {
final prefs = await _prefs;
return prefs.getString(_refreshTokenKey);
} catch (e) {
throw StorageException('Erreur lors de la récupération du refresh token: $e');
}
}
/// Récupère les informations utilisateur
Future<UserInfo?> getUserInfo() async {
try {
final prefs = await _prefs;
final userJson = prefs.getString(_userInfoKey);
if (userJson == null) return null;
final userMap = jsonDecode(userJson) as Map<String, dynamic>;
return UserInfo.fromJson(userMap);
} catch (e) {
throw StorageException('Erreur lors de la récupération des informations utilisateur: $e');
}
}
/// Récupère la date d'expiration du token d'accès
Future<DateTime?> getTokenExpirationDate() async {
try {
final prefs = await _prefs;
final expiresAtString = prefs.getString(_expiresAtKey);
if (expiresAtString == null) return null;
return DateTime.parse(expiresAtString);
} catch (e) {
throw StorageException('Erreur lors de la récupération de la date d\'expiration: $e');
}
}
/// Récupère la date d'expiration du refresh token
Future<DateTime?> getRefreshTokenExpirationDate() async {
try {
final expiresAtString = await _storage.read(key: _refreshExpiresAtKey);
if (expiresAtString == null) return null;
return DateTime.parse(expiresAtString);
} catch (e) {
throw StorageException('Erreur lors de la récupération de la date d\'expiration du refresh token: $e');
}
}
/// Récupère toutes les données d'authentification
Future<LoginResponse?> getAuthData() async {
try {
final results = await Future.wait([
getAccessToken(),
getRefreshToken(),
getUserInfo(),
getTokenExpirationDate(),
getRefreshTokenExpirationDate(),
]);
final accessToken = results[0] as String?;
final refreshToken = results[1] as String?;
final userInfo = results[2] as UserInfo?;
final expiresAt = results[3] as DateTime?;
final refreshExpiresAt = results[4] as DateTime?;
if (accessToken == null ||
refreshToken == null ||
userInfo == null ||
expiresAt == null ||
refreshExpiresAt == null) {
return null;
}
return LoginResponse(
accessToken: accessToken,
refreshToken: refreshToken,
tokenType: 'Bearer',
expiresAt: expiresAt,
refreshExpiresAt: refreshExpiresAt,
user: userInfo,
);
} catch (e) {
throw StorageException('Erreur lors de la récupération des données d\'authentification: $e');
}
}
/// Met à jour le token d'accès
Future<void> updateAccessToken(String accessToken, DateTime expiresAt) async {
try {
await Future.wait([
_storage.write(key: _accessTokenKey, value: accessToken),
_storage.write(key: _expiresAtKey, value: expiresAt.toIso8601String()),
]);
} catch (e) {
throw StorageException('Erreur lors de la mise à jour du token d\'accès: $e');
}
}
/// Vérifie si les données d'authentification existent
Future<bool> hasAuthData() async {
try {
final accessToken = await _storage.read(key: _accessTokenKey);
final refreshToken = await _storage.read(key: _refreshTokenKey);
return accessToken != null && refreshToken != null;
} catch (e) {
return false;
}
}
/// Vérifie si les tokens sont expirés
Future<bool> areTokensExpired() async {
try {
final expiresAt = await getTokenExpirationDate();
final refreshExpiresAt = await getRefreshTokenExpirationDate();
if (expiresAt == null || refreshExpiresAt == null) return true;
final now = DateTime.now();
return refreshExpiresAt.isBefore(now);
} catch (e) {
return true;
}
}
/// Vérifie si le token d'accès expire bientôt
Future<bool> isAccessTokenExpiringSoon({int minutes = 5}) async {
try {
final expiresAt = await getTokenExpirationDate();
if (expiresAt == null) return true;
final threshold = DateTime.now().add(Duration(minutes: minutes));
return expiresAt.isBefore(threshold);
} catch (e) {
return true;
}
}
/// Efface toutes les données d'authentification
Future<void> clearAuthData() async {
try {
await Future.wait([
_storage.delete(key: _accessTokenKey),
_storage.delete(key: _refreshTokenKey),
_storage.delete(key: _userInfoKey),
_storage.delete(key: _expiresAtKey),
_storage.delete(key: _refreshExpiresAtKey),
]);
} catch (e) {
throw StorageException('Erreur lors de l\'effacement des données d\'authentification: $e');
}
}
/// Active/désactive l'authentification biométrique
Future<void> setBiometricEnabled(bool enabled) async {
try {
await _storage.write(key: _biometricEnabledKey, value: enabled.toString());
} catch (e) {
throw StorageException('Erreur lors de la configuration biométrique: $e');
}
}
/// Vérifie si l'authentification biométrique est activée
Future<bool> isBiometricEnabled() async {
try {
final enabled = await _storage.read(key: _biometricEnabledKey);
return enabled == 'true';
} catch (e) {
return false;
}
}
/// Efface toutes les données stockées
Future<void> clearAll() async {
try {
await _storage.deleteAll();
} catch (e) {
throw StorageException('Erreur lors de l\'effacement de toutes les données: $e');
}
}
/// Vérifie si le stockage sécurisé est disponible
Future<bool> isAvailable() async {
try {
await _storage.containsKey(key: 'test');
return true;
} catch (e) {
return false;
}
}
}
/// Exception liée au stockage
class StorageException implements Exception {
final String message;
const StorageException(this.message);
@override
String toString() => 'StorageException: $message';
}