petitspas/frontend/lib/services/auth_service.dart
Julien Martin fe71fdf28e feat(#47): Ajout de la modale de changement de mot de passe obligatoire
Implémentation complète du ticket #47 :
- Mise à jour de l'URL API vers app.ptits-pas.fr
- Ajout du champ changement_mdp_obligatoire au modèle AppUser
- Ajout des endpoints /auth/me et /auth/change-password-required
- Implémentation de la vraie logique de connexion dans AuthService
- Création de la modale ChangePasswordDialog non-dismissible
- Connexion du bouton de connexion avec gestion de la modale
- Ajout des routes admin-dashboard et parent-dashboard

La modale s'affiche automatiquement après connexion si
changement_mdp_obligatoire = true et bloque l'utilisateur jusqu'au
changement de mot de passe.
2026-01-27 16:30:15 +01:00

146 lines
4.6 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);
// Stocker les tokens
await TokenService.saveToken(data['accessToken']);
await TokenService.saveRefreshToken(data['refreshToken']);
// Récupérer le profil utilisateur pour avoir toutes les infos
final user = await _fetchUserProfile(data['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;
throw Exception('Erreur réseau: impossible de se connecter au serveur');
}
}
/// 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 data = jsonDecode(response.body);
return AppUser.fromJson(data);
} else {
throw Exception('Erreur lors de la récupération du profil');
}
} catch (e) {
if (e is Exception) rethrow;
throw Exception('Erreur réseau: impossible de récupérer le profil');
}
}
/// 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({
'currentPassword': currentPassword,
'newPassword': 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;
throw Exception('Erreur réseau: impossible de changer le mot de passe');
}
}
/// 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;
}
}
}