import 'dart:convert'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import '../api/api_client.dart' show apiDio, ApiException, onUnauthorized; import '../services/fcm_service.dart'; import 'auth_state.dart'; class AuthNotifier extends StateNotifier { final FlutterSecureStorage _storage = const FlutterSecureStorage(); AuthNotifier() : super(const AuthLoading()) { onUnauthorized = () => logout(); _init(); } Future _init() async { final token = await _storage.read(key: 'jwt_token'); final userJson = await _storage.read(key: 'auth_user'); if (token != null && userJson != null) { try { final user = AuthUser.fromMap(jsonDecode(userJson)); if (user.isAdmin) { state = AuthAuthenticated(user); return; } } catch (_) {} } state = const AuthUnauthenticated(); } Future login(String username, String password) async { try { final resp = await apiDio.post('/auth/login', data: { 'username': username, 'password': password, }); final token = resp.data['access_token'] as String; // Decode JWT payload to get user info final parts = token.split('.'); if (parts.length < 2) throw ApiException(401, 'Token tidak valid.'); final payload = jsonDecode( utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))), ); final user = AuthUser( id: payload['sub'] is int ? payload['sub'] : int.parse(payload['sub'].toString()), username: payload['username'] ?? username, role: payload['role'] ?? 'user', ); if (!user.isAdmin) { throw ApiException(403, 'Akses ditolak. Hanya admin yang dapat menggunakan aplikasi ini.'); } await _storage.write(key: 'jwt_token', value: token); await _storage.write(key: 'auth_user', value: jsonEncode({ 'id': user.id, 'username': user.username, 'role': user.role, })); state = AuthAuthenticated(user); FcmService.init(); // fire-and-forget: register push token } on ApiException { rethrow; } catch (e) { if (e.toString().contains('403') || e.toString().contains('Akses')) rethrow; throw ApiException(401, 'Username atau password salah.'); } } Future logout() async { await FcmService.clearToken(); await _storage.deleteAll(); state = const AuthUnauthenticated(); } Future changePassword(String currentPassword, String newPassword) async { try { await apiDio.put('/auth/change-password', data: { 'current_password': currentPassword, 'new_password': newPassword, }); } catch (e) { throw ApiException(400, 'Gagal mengganti password. Periksa password lama Anda.'); } } } final authProvider = StateNotifierProvider( (_) => AuthNotifier(), );