Files
mileograph_flutter/lib/services/authservice.dart
Pete Gregory 11a5a42ad4
Some checks failed
Release / meta (push) Successful in 20s
Release / release-dev (push) Has been cancelled
Release / release-master (push) Has been cancelled
Release / android-build (push) Has been cancelled
Release / linux-build (push) Has been cancelled
add support for token validation on login page
2025-12-14 12:15:39 +00:00

158 lines
3.9 KiB
Dart

import 'package:flutter/foundation.dart';
import 'package:mileograph_flutter/objects/objects.dart';
import 'package:mileograph_flutter/services/apiService.dart';
import 'package:mileograph_flutter/services/tokenStorageService.dart';
class AuthService extends ChangeNotifier {
final ApiService api;
bool _restoring = false;
final TokenStorageService _tokenStorage = TokenStorageService();
AuthService({required this.api});
AuthenticatedUserData? _user;
bool get isLoggedIn => _user != null;
String? get token => _user?.access_token;
String? get userId => _user?.user_id;
String? get username => _user?.username;
String? get fullName => _user?.full_name;
void setLoginData({
required String userId,
required String username,
required String fullName,
required String accessToken,
required String email,
}) {
_user = AuthenticatedUserData(
user_id: userId,
username: username,
full_name: fullName,
access_token: accessToken,
email: email,
);
_persistToken(accessToken);
notifyListeners();
}
Future<void> login(String username, String password) async {
final formData = {
'grant_type': 'password',
'username': username,
'password': password,
'scope': '',
'client_id': 'string',
'client_secret': 'string',
};
// 1. Get token
final tokenResponse = await api.postForm('/token', formData);
final accessToken = tokenResponse['access_token'];
// 2. Get user details
final userResponse = await api.get(
'/users/me',
headers: {
'Authorization': 'Bearer $accessToken',
'accept': 'application/json',
},
);
// 3. Populate state
setLoginData(
userId: userResponse['user_id'],
username: userResponse['username'],
fullName: userResponse['full_name'],
accessToken: accessToken,
email: userResponse['email'],
);
}
Future<void> tryRestoreSession() async {
if (_restoring || _user != null) return;
_restoring = true;
try {
// read token from secure storage (with fallback)
final token = await _tokenStorage.getToken();
if (token == null || token.isEmpty) return;
final userResponse = await api.get(
'/users/me',
headers: {
'Authorization': 'Bearer $token',
'accept': 'application/json',
},
);
setLoginData(
userId: userResponse['user_id'],
username: userResponse['username'],
fullName: userResponse['full_name'],
accessToken: token,
email: userResponse['email'],
);
} catch (_) {
await _clearToken();
} finally {
_restoring = false;
}
}
Future<bool> validateStoredToken() async {
final token = await _tokenStorage.getToken();
if (token == null || token.isEmpty) return false;
try {
await api.get(
'/validate',
headers: {
'Authorization': 'Bearer $token',
'accept': 'application/json',
},
);
return true;
} catch (_) {
await _clearToken();
return false;
}
}
Future<void> _persistToken(String token) async {
await _tokenStorage.setToken(token);
}
Future<void> _clearToken() async {
await _tokenStorage.clearToken();
}
Future<void> register({
required String username,
required String email,
required String fullName,
required String password,
String inviteCode = '',
}) async {
final formData = {
'user_name': username,
'email': email,
'full_name': fullName,
'password': password,
'invitation_code': inviteCode,
'empty': '',
'empty2': '',
};
await api.postForm('/register', formData);
}
Future<void> handleTokenExpired() async {
_user = null;
await _clearToken();
notifyListeners();
}
void logout() {
handleTokenExpired(); // reuse
}
}