AgendaTasks/lib/core/network/authenticated_client.dart
m3mo d8164be49a Add user authentication to Flutter frontend
- Create auth feature with Clean Architecture (domain/data/presentation)
- Add login and register pages with form validation
- Implement secure token storage with flutter_secure_storage
- Create AuthenticatedClient for automatic Bearer token headers
- Add AuthViewModel for global auth state management
- Update router with auth guards (redirect to login if not authenticated)
- Add logout option to settings page
- Update TaskRemoteDataSource to use authenticated client
- Add auth-related localization strings (EN/DE)
2026-02-02 22:58:07 +01:00

69 lines
2.0 KiB
Dart

import 'package:http/http.dart' as http;
import '../../features/auth/data/datasources/auth_local_datasource.dart';
import '../../features/auth/domain/repositories/auth_repository.dart';
import '../errors/exceptions.dart';
class AuthenticatedClient extends http.BaseClient {
final http.Client _inner;
final AuthLocalDataSource _localDataSource;
final AuthRepository _authRepository;
AuthenticatedClient({
required AuthLocalDataSource localDataSource,
required AuthRepository authRepository,
http.Client? inner,
}) : _localDataSource = localDataSource,
_authRepository = authRepository,
_inner = inner ?? http.Client();
@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
final tokens = await _localDataSource.getTokens();
if (tokens != null) {
request.headers['Authorization'] = 'Bearer ${tokens.accessToken}';
}
var response = await _inner.send(request);
if (response.statusCode == 401 && tokens != null) {
final refreshResult = await _authRepository.refreshToken();
final refreshed = refreshResult.when(
success: (_) => true,
error: (_) => false,
);
if (refreshed) {
final newTokens = await _localDataSource.getTokens();
if (newTokens != null) {
final newRequest = _copyRequest(request, newTokens.accessToken);
response = await _inner.send(newRequest);
}
} else {
await _localDataSource.clearAll();
throw const UnauthorizedException(message: 'Session expired');
}
}
return response;
}
http.BaseRequest _copyRequest(http.BaseRequest original, String token) {
final http.Request newRequest = http.Request(original.method, original.url)
..headers.addAll(original.headers)
..headers['Authorization'] = 'Bearer $token';
if (original is http.Request && original.body.isNotEmpty) {
newRequest.body = original.body;
}
return newRequest;
}
@override
void close() {
_inner.close();
super.close();
}
}