import 'dart:developer' as developer; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'auth_state.dart'; part 'auth_provider.g.dart'; /// Provides authentication state and operations. /// /// Note: Full OIDC implementation with flutter_appauth requires /// native platform configuration. For now, this provides the /// state management infrastructure. @riverpod class AuthNotifier extends _$AuthNotifier { static const _accessTokenKey = 'auth_access_token'; static const _refreshTokenKey = 'auth_refresh_token'; static const _expiresAtKey = 'auth_expires_at'; static const _userIdKey = 'auth_user_id'; static const _userNameKey = 'auth_user_name'; static const _userEmailKey = 'auth_user_email'; @override Future build() async { return _loadStoredAuth(); } Future _loadStoredAuth() async { try { final prefs = await SharedPreferences.getInstance(); final accessToken = prefs.getString(_accessTokenKey); if (accessToken == null) { return const AuthState(); } final expiresAtMs = prefs.getInt(_expiresAtKey); final expiresAt = expiresAtMs != null ? DateTime.fromMillisecondsSinceEpoch(expiresAtMs) : null; final authState = AuthState( isAuthenticated: true, accessToken: accessToken, refreshToken: prefs.getString(_refreshTokenKey), expiresAt: expiresAt, userId: prefs.getString(_userIdKey), userName: prefs.getString(_userNameKey), userEmail: prefs.getString(_userEmailKey), ); // Check if token is expired if (authState.isTokenExpired) { developer.log('Stored token expired, clearing auth', name: 'auth'); await _clearStoredAuth(); return const AuthState(); } developer.log('Restored auth for ${authState.userName}', name: 'auth'); return authState; } catch (e) { developer.log('Failed to load stored auth: $e', name: 'auth'); return const AuthState(); } } /// Sign in with OIDC (placeholder for flutter_appauth integration). Future signIn() async { // TODO: Implement OIDC flow with flutter_appauth // For now, this is a placeholder that will be implemented // when native platform configuration is complete. developer.log('Sign in requested - OIDC not yet configured', name: 'auth'); } /// Sign out and clear stored credentials. Future signOut() async { await _clearStoredAuth(); state = const AsyncData(AuthState()); developer.log('Signed out', name: 'auth'); } /// Update auth state (called after successful OIDC flow). Future setAuthenticated({ required String accessToken, String? refreshToken, DateTime? expiresAt, String? userId, String? userName, String? userEmail, }) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString(_accessTokenKey, accessToken); if (refreshToken != null) { await prefs.setString(_refreshTokenKey, refreshToken); } if (expiresAt != null) { await prefs.setInt(_expiresAtKey, expiresAt.millisecondsSinceEpoch); } if (userId != null) await prefs.setString(_userIdKey, userId); if (userName != null) await prefs.setString(_userNameKey, userName); if (userEmail != null) await prefs.setString(_userEmailKey, userEmail); state = AsyncData(AuthState( isAuthenticated: true, accessToken: accessToken, refreshToken: refreshToken, expiresAt: expiresAt, userId: userId, userName: userName, userEmail: userEmail, )); developer.log('Authenticated as $userName', name: 'auth'); } Future _clearStoredAuth() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove(_accessTokenKey); await prefs.remove(_refreshTokenKey); await prefs.remove(_expiresAtKey); await prefs.remove(_userIdKey); await prefs.remove(_userNameKey); await prefs.remove(_userEmailKey); } }