petitspas/frontend/lib/services/auth_service.dart
2026-02-17 15:54:24 +01:00

159 lines
5.4 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import '../models/user.dart';
import 'api/api_config.dart';
import 'api/tokenService.dart';
class AuthService {
static const String _currentUserKey = 'current_user';
/// Connexion de l'utilisateur
/// Retourne l'utilisateur connecté avec le flag changement_mdp_obligatoire
static Future<AppUser> login(String email, String password) async {
try {
final response = await http.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.login}'),
headers: ApiConfig.headers,
body: jsonEncode({
'email': email,
'password': password,
}),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final data = jsonDecode(response.body);
// API renvoie access_token / refresh_token (snake_case)
final accessToken = data['access_token'] as String? ?? data['accessToken'] as String?;
final refreshToken = data['refresh_token'] as String? ?? data['refreshToken'] as String?;
if (accessToken == null) throw Exception('Token absent dans la réponse serveur');
await TokenService.saveToken(accessToken);
await TokenService.saveRefreshToken(refreshToken ?? '');
final user = await _fetchUserProfile(accessToken);
// Stocker l'utilisateur en cache
await _saveCurrentUser(user);
return user;
} else {
final error = jsonDecode(response.body);
throw Exception(error['message'] ?? 'Erreur de connexion');
}
} catch (e) {
if (e is Exception) rethrow;
if (e is Error) throw Exception('Erreur interne: ${e.toString()}');
throw Exception('Erreur réseau: impossible de se connecter au serveur ($e)');
}
}
/// Récupère le profil utilisateur via /auth/me
static Future<AppUser> _fetchUserProfile(String token) async {
try {
final response = await http.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.authMe}'),
headers: ApiConfig.authHeaders(token),
);
if (response.statusCode == 200) {
final raw = jsonDecode(response.body);
if (raw is! Map<String, dynamic>) {
throw Exception('Profil invalide: réponse serveur inattendue');
}
// Accepter réponse directe ou wrapper { data: {...} }
final data = raw.containsKey('data') && raw['data'] is Map<String, dynamic>
? raw['data'] as Map<String, dynamic>
: raw;
return AppUser.fromJson(data);
} else {
throw Exception('Erreur lors de la récupération du profil');
}
} catch (e) {
if (e is Exception) rethrow;
if (e is Error) throw Exception('Erreur interne: ${e.toString()}');
throw Exception('Erreur réseau: impossible de récupérer le profil ($e)');
}
}
/// Changement de mot de passe obligatoire
static Future<void> changePasswordRequired({
required String currentPassword,
required String newPassword,
}) async {
final token = await TokenService.getToken();
if (token == null) {
throw Exception('Non authentifié');
}
try {
final response = await http.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.changePasswordRequired}'),
headers: ApiConfig.authHeaders(token),
body: jsonEncode({
'mot_de_passe_actuel': currentPassword,
'nouveau_mot_de_passe': newPassword,
'confirmation_mot_de_passe': newPassword,
}),
);
if (response.statusCode != 200 && response.statusCode != 201) {
final error = jsonDecode(response.body);
throw Exception(error['message'] ?? 'Erreur lors du changement de mot de passe');
}
// Après le changement de MDP, rafraîchir le profil utilisateur
final user = await _fetchUserProfile(token);
await _saveCurrentUser(user);
} catch (e) {
if (e is Exception) rethrow;
if (e is Error) throw Exception('Erreur interne: ${e.toString()}');
throw Exception('Erreur réseau: impossible de changer le mot de passe ($e)');
}
}
/// Déconnexion de l'utilisateur
static Future<void> logout() async {
await TokenService.clearAll();
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_currentUserKey);
}
/// Vérifie si l'utilisateur est connecté
static Future<bool> isLoggedIn() async {
final token = await TokenService.getToken();
return token != null;
}
/// Récupère l'utilisateur connecté depuis le cache
static Future<AppUser?> getCurrentUser() async {
final prefs = await SharedPreferences.getInstance();
final userJson = prefs.getString(_currentUserKey);
if (userJson != null) {
return AppUser.fromJson(jsonDecode(userJson));
}
return null;
}
/// Sauvegarde l'utilisateur actuel en cache
static Future<void> _saveCurrentUser(AppUser user) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_currentUserKey, jsonEncode(user.toJson()));
}
/// Rafraîchit le profil utilisateur depuis l'API
static Future<AppUser?> refreshCurrentUser() async {
final token = await TokenService.getToken();
if (token == null) return null;
try {
final user = await _fetchUserProfile(token);
await _saveCurrentUser(user);
return user;
} catch (e) {
return null;
}
}
}