Skip to content

Authentication

DWallet includes a user authentication system that manages user identity, session state, and profile information.

The authentication module handles:

  • User identity management
  • Session state persistence
  • User profile data (name, email, currency, balance)
  • Authentication state throughout the app

The core service managing user state:

class AuthUserService extends AsyncNotifier<User> {
@override
Future<User> build() async {
final prefs = await SharedPreferences.getInstance();
return _loadUser(prefs);
}
Future<void> updateUser(User newUser) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
await _saveUser(newUser);
return newUser;
});
}
Future<void> updateCurrency(Currency currency) async {
final currentUser = state.value!;
await updateUser(currentUser.copyWith(currency: currency));
}
Future<void> updateInitialBalance(double balance) async {
final currentUser = state.value!;
await updateUser(currentUser.copyWith(initialBalance: balance));
}
}

Located in: lib/app/di/services/_auth_user_service.dart

The User model contains:

class UserModel implements User {
final String id;
final String? fullName;
final String? email;
final double initialBalance;
final Currency currency;
final String? sessionToken;
final bool hasCompletedOnboarding;
const UserModel({
required this.id,
this.fullName,
this.email,
this.initialBalance = 0,
required this.currency,
this.sessionToken,
this.hasCompletedOnboarding = false,
});
}
class HomeView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userAsync = ref.watch(authUserServiceProvider);
return userAsync.when(
data: (user) => Text('Welcome, ${user.fullName ?? 'Guest'}'),
loading: () => CircularProgressIndicator(),
error: (err, _) => Text('Error: $err'),
);
}
}
final currency = ref.watch(userCurrencyProvider);
Text('${currency.symbol}100.00');
// Update currency
ref.read(authUserServiceProvider.notifier).updateCurrency(CurrencyMock.eur);
// Update balance
ref.read(authUserServiceProvider.notifier).updateInitialBalance(1000.00);

For development, mock users are available:

// Guest user
final guest = UserMock.guest();
// Default USD user
final usdUser = UserMock.withCurrency(CurrencyMock.usd);
// Custom user
final customUser = UserModel(
id: 'user_123',
fullName: 'John Doe',
email: 'john@example.com',
currency: CurrencyMock.eur,
initialBalance: 5000.00,
hasCompletedOnboarding: true,
);

The authentication system works alongside the security features:

  1. User state persists across app restarts
  2. App Lock provides additional security layer
  3. Biometric auth can be enabled per-user preference

Routes are protected using OnboardRouteGuard:

AutoRoute(
page: ClientRoute.page,
path: '/client',
guards: [OnboardRouteGuard],
children: [...],
)

Currently, DWallet uses a simplified session model suitable for a single-user offline app:

// Check if user has session
final hasSession = user.sessionToken != null;
// In a full implementation, you would:
// 1. Validate token on app start
// 2. Refresh tokens as needed
// 3. Handle session expiration

To connect to a real backend:

  1. Add API client to services:
class AuthApiService {
final Dio _dio;
Future<User> login(String email, String password) async {
final response = await _dio.post('/auth/login', data: {
'email': email,
'password': password,
});
return UserModel.fromJson(response.data);
}
Future<void> logout() async {
await _dio.post('/auth/logout');
}
}
  1. Update AuthUserService:
class AuthUserService extends AsyncNotifier<User> {
late final AuthApiService _api;
Future<void> login(String email, String password) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() =>
_api.login(email, password)
);
}
Future<void> logout() async {
await _api.logout();
state = AsyncValue.data(UserMock.guest());
}
}
  1. Add login/logout UI in auth pages.
  1. Always handle loading states with AsyncValue.when()
  2. Validate session on app start for real backends
  3. Secure storage for tokens (use flutter_secure_storage)
  4. Clear sensitive data on logout
  5. Handle errors gracefully with user-friendly messages