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.
146 lines
4.6 KiB
Dart
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;
|
|
}
|
|
}
|
|
} |