import 'package:flutter/foundation.dart'; import '../models/user_credentials.dart'; import '../services/aufe_connection.dart'; /// Authentication state enum enum AuthState { initial, loading, authenticated, unauthenticated, error } /// Provider for managing authentication state and user sessions /// /// Handles login, logout, session checking, and credential management /// Uses ChangeNotifier to notify listeners of state changes /// /// Usage example: /// ```dart /// final authProvider = Provider.of(context); /// /// // Login /// await authProvider.login( /// userId: '学号', /// ecPassword: 'EC密码', /// password: 'UAAP密码', /// ); /// /// // Check session /// final isValid = await authProvider.checkSession(); /// /// // Logout /// await authProvider.logout(); /// ``` class AuthProvider extends ChangeNotifier { AUFEConnection? _connection; AuthState _state = AuthState.initial; String? _errorMessage; UserCredentials? _credentials; /// Get current authentication state AuthState get state => _state; /// Get current error message (if any) String? get errorMessage => _errorMessage; /// Get current connection instance AUFEConnection? get connection => _connection; /// Get current user credentials UserCredentials? get credentials => _credentials; /// Check if user is authenticated bool get isAuthenticated => _state == AuthState.authenticated; /// Login with user credentials /// /// Creates AUFEConnection and performs both EC and UAAP login /// Saves credentials securely on successful login /// /// [userId] - Student ID /// [ecPassword] - EC system password /// [password] - UAAP system password /// /// Returns true if login succeeds, false otherwise Future login({ required String userId, required String ecPassword, required String password, }) async { try { print('🔐 Starting login process...'); print('🔐 User ID: $userId'); _setState(AuthState.loading); _errorMessage = null; // Create credentials final credentials = UserCredentials( userId: userId, ecPassword: ecPassword, password: password, ); // Create connection print('🔐 Creating AUFEConnection...'); final connection = AUFEConnection( userId: userId, ecPassword: ecPassword, password: password, ); // Initialize HTTP client print('🔐 Starting HTTP client...'); connection.startClient(); // Perform EC login print('🔐 Performing EC login...'); final ecLoginStatus = await connection.ecLogin(); print('🔐 EC login result: ${ecLoginStatus.success}'); if (!ecLoginStatus.success) { _errorMessage = _getEcLoginErrorMessage(ecLoginStatus); print('❌ EC login failed: $_errorMessage'); _setState(AuthState.error); await connection.close(); return false; } // Perform UAAP login print('🔐 Performing UAAP login...'); final uaapLoginStatus = await connection.uaapLogin(); print('🔐 UAAP login result: ${uaapLoginStatus.success}'); if (!uaapLoginStatus.success) { _errorMessage = _getUaapLoginErrorMessage(uaapLoginStatus); print('❌ UAAP login failed: $_errorMessage'); _setState(AuthState.error); await connection.close(); return false; } // Save credentials securely print('🔐 Saving credentials...'); await credentials.saveSecurely(); // Update state _connection = connection; _credentials = credentials; _setState(AuthState.authenticated); print('✅ Login successful!'); return true; } catch (e, stackTrace) { _errorMessage = '登录过程出错: $e'; print('❌ Login error: $e'); print('❌ Stack trace: $stackTrace'); _setState(AuthState.error); return false; } } /// Logout and clear all session data /// /// Closes connection, clears credentials from secure storage, /// and resets authentication state Future logout() async { try { // Close connection if (_connection != null) { await _connection!.close(); _connection = null; } // Clear credentials from secure storage await UserCredentials.clearSecurely(); // Reset state _credentials = null; _errorMessage = null; _setState(AuthState.unauthenticated); } catch (e) { debugPrint('Error during logout: $e'); // Still reset state even if cleanup fails _connection = null; _credentials = null; _setState(AuthState.unauthenticated); } } /// Check if current session is still valid /// /// Performs health check on the connection /// If session is invalid, updates state to unauthenticated /// /// Returns true if session is valid, false otherwise Future checkSession() async { if (_connection == null || _state != AuthState.authenticated) { _setState(AuthState.unauthenticated); return false; } try { final isHealthy = await _connection!.healthCheck(); if (!isHealthy) { _errorMessage = '会话已过期,请重新登录'; _setState(AuthState.unauthenticated); return false; } return true; } catch (e) { _errorMessage = '检查会话状态失败: $e'; _setState(AuthState.error); return false; } } /// Attempt to restore session from saved credentials /// /// Loads credentials from secure storage and attempts to login /// Useful for auto-login on app startup /// /// Returns true if session restored successfully, false otherwise Future restoreSession() async { try { _setState(AuthState.loading); // Load saved credentials final credentials = await UserCredentials.loadSecurely(); if (credentials == null) { _setState(AuthState.unauthenticated); return false; } // Attempt login with saved credentials return await login( userId: credentials.userId, ecPassword: credentials.ecPassword, password: credentials.password, ); } catch (e) { _errorMessage = '恢复会话失败: $e'; _setState(AuthState.unauthenticated); return false; } } /// Update authentication state and notify listeners void _setState(AuthState newState) { _state = newState; notifyListeners(); } /// Get user-friendly error message for EC login status String _getEcLoginErrorMessage(dynamic status) { if (status.failInvalidCredentials) { return 'EC系统用户名或密码错误'; } else if (status.failNotFoundTwfid) { return '无法获取TwfID,请稍后重试'; } else if (status.failNotFoundRsaKey) { return '无法获取RSA密钥,请稍后重试'; } else if (status.failNotFoundRsaExp) { return '无法获取RSA指数,请稍后重试'; } else if (status.failNotFoundCsrfCode) { return '无法获取CSRF代码,请稍后重试'; } else if (status.failMaybeAttacked) { return '登录频繁,请稍后重试'; } else if (status.failNetworkError) { return 'EC系统网络连接失败'; } else { return 'EC系统登录失败'; } } /// Get user-friendly error message for UAAP login status String _getUaapLoginErrorMessage(dynamic status) { if (status.failInvalidCredentials) { return 'UAAP系统用户名或密码错误'; } else if (status.failNotFoundLt) { return '无法获取lt参数,请稍后重试'; } else if (status.failNotFoundExecution) { return '无法获取execution参数,请稍后重试'; } else if (status.failNetworkError) { return 'UAAP系统网络连接失败'; } else { return 'UAAP系统登录失败'; } } @override void dispose() { // Close connection when provider is disposed _connection?.close(); super.dispose(); } }