Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 33 additions & 129 deletions mobile/lib/controllers/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,55 +35,33 @@ class AuthState extends ChangeNotifier {

try {
final res = await _authService.login(username, password);

dynamic dataMap = res.data;

if (dataMap is String) {
try {
dataMap = jsonDecode(dataMap);
} catch (_) {}
}

if (dataMap is Map) {
if (dataMap['success'] == true) {
String? extractedToken;

if (dataMap['data'] != null && dataMap['data'] is Map) {
final dataEnv = dataMap['data'];
if (dataEnv['tokens'] != null && dataEnv['tokens'] is Map) {
extractedToken = dataEnv['tokens']['access_token'];
}
}

if (extractedToken != null) {
_token = extractedToken;
await _storage.write(key: "access_token", value: _token);
_isLoading = false;
notifyListeners();
return true;
}
if (dataMap is Map && dataMap['success'] == true) {
Map<String, dynamic>? tokens = dataMap['data']?['tokens'];
if (tokens != null &&
tokens['access_token'] != null &&
tokens['refresh_token'] != null) {
_token = tokens['access_token'];
await _authService.saveTokens(
tokens['access_token'],
tokens['refresh_token'],
);
_isLoading = false;
notifyListeners();
return true;
}
_errorMessage =
dataMap['error'] ?? dataMap['message'] ?? "Authentication failed";
} else {
_errorMessage = "Unexpected server payload architecture structure.";
}
_errorMessage =
dataMap['error'] ?? dataMap['message'] ?? "Authentication failed";
} on DioException catch (e) {
dynamic responseData = e.response?.data;
if (responseData is String) {
try {
responseData = jsonDecode(responseData);
} catch (_) {}
}

if (responseData is Map) {
_errorMessage =
responseData['error'] ??
responseData['message'] ??
"Server validation failed.";
} else {
_errorMessage =
"Server error code: ${e.response?.statusCode ?? 'unknown'}.";
}
_errorMessage = e.response?.data?['error'] ?? "Server validation failed.";
} catch (e) {
_errorMessage = "Connection parsing error occurred";
}
Expand All @@ -102,110 +80,36 @@ class AuthState extends ChangeNotifier {
_errorMessage = null;
notifyListeners();

String? finalUsername;
bool registerSuccess = false;

try {
final res = await _authService.register(username, displayName, password);

dynamic dataMap = res.data;

if (dataMap is String) {
try {
dataMap = jsonDecode(dataMap);
} catch (_) {}
}

if (dataMap is Map) {
if (dataMap['success'] == true) {
String? extractedToken;

if (dataMap['data'] != null && dataMap['data'] is Map) {
final dataEnv = dataMap['data'];
if (dataEnv['tokens'] != null && dataEnv['tokens'] is Map) {
extractedToken = dataEnv['tokens']['access_token'];
}
if (dataEnv['user'] != null && dataEnv['user'] is Map) {
finalUsername = dataEnv['user']['username'];
}
}

if (extractedToken != null) {
_token = extractedToken;
await _storage.write(key: "access_token", value: _token);
_isLoading = false;
notifyListeners();
return true;
}

registerSuccess = true;
} else {
_errorMessage =
dataMap['error'] ?? dataMap['message'] ?? "Registration failed";
if (dataMap is Map && dataMap['success'] == true) {
Map<String, dynamic>? tokens = dataMap['data']?['tokens'];
if (tokens != null &&
tokens['access_token'] != null &&
tokens['refresh_token'] != null) {
_token = tokens['access_token'];
await _authService.saveTokens(
tokens['access_token'],
tokens['refresh_token'],
);
_isLoading = false;
notifyListeners();
return false;
return true;
}
}
_errorMessage = dataMap['error'] ?? "Registration failed";
} on DioException catch (e) {
dynamic responseData = e.response?.data;
if (responseData is String) {
try {
responseData = jsonDecode(responseData);
} catch (_) {}
}
if (responseData is Map) {
_errorMessage =
responseData['error'] ??
responseData['message'] ??
"Registration rejected.";
} else {
_errorMessage =
"Registration server error: ${e.response?.statusCode ?? 'unknown'}.";
}
_isLoading = false;
notifyListeners();
return false;
_errorMessage = e.response?.data?['error'] ?? "Registration rejected.";
} catch (e) {
_errorMessage = "Server error occurred during sign up";
_isLoading = false;
notifyListeners();
return false;
}

if (registerSuccess) {
try {
final String targetUsername = finalUsername ?? username;
final loginRes = await _authService.login(targetUsername, password);

dynamic loginData = loginRes.data;
if (loginData is String) {
try {
loginData = jsonDecode(loginData);
} catch (_) {}
}

if (loginData is Map && loginData['success'] == true) {
String? loginToken;
if (loginData['data'] != null && loginData['data'] is Map) {
final dataEnv = loginData['data'];
if (dataEnv['tokens'] != null && dataEnv['tokens'] is Map) {
loginToken = dataEnv['tokens']['access_token'];
}
}

if (loginToken != null) {
_token = loginToken;
await _storage.write(key: "access_token", value: _token);
_isLoading = false;
notifyListeners();
return true;
}
}
_errorMessage =
"Account created successfully as $targetUsername! Please log in manually.";
} catch (e) {
_errorMessage = "Profile configured! Please switch tabs to log in.";
}
}

_isLoading = false;
Expand All @@ -215,14 +119,14 @@ class AuthState extends ChangeNotifier {

Future<void> logout() async {
_token = null;
await _storage.delete(key: "access_token");
await _authService.logout();
notifyListeners();
}

void logoutSilently() {
if (_token != null) {
_token = null;
_storage.delete(key: "access_token");
_authService.logout();
notifyListeners();
}
}
Expand Down
32 changes: 31 additions & 1 deletion mobile/lib/services/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,38 @@ class ApiService {
onResponse: (response, handler) {
return handler.next(response);
},
onError: (DioException e, handler) {
onError: (DioException e, handler) async {
if (e.response?.statusCode == 401) {
final refreshToken = await _auth.getRefreshToken();

if (refreshToken != null) {
try {
final refreshResponse = await _auth.refreshAccessToken(
refreshToken,
);
final resData = refreshResponse.data;

if (resData['success'] == true) {
final newAccessToken = resData['data']['access_token'];
final newRefreshToken = resData['data']['refresh_token'];

await _auth.saveTokens(newAccessToken, newRefreshToken);

final cloneOptions = e.requestOptions;
cloneOptions.headers["Authorization"] =
"Bearer $newAccessToken";

final retryResponse = await _dio.fetch(cloneOptions);
return handler.resolve(retryResponse);
}
} catch (refreshError) {
if (AuthState.onGlobalUnauthorized != null) {
AuthState.onGlobalUnauthorized!();
}
return handler.next(e);
}
}

if (AuthState.onGlobalUnauthorized != null) {
AuthState.onGlobalUnauthorized!();
}
Expand Down
17 changes: 15 additions & 2 deletions mobile/lib/services/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ class AuthService {
return await _storage.read(key: "access_token");
}

Future<void> saveToken(String token) async {
await _storage.write(key: "access_token", value: token);
Future<String?> getRefreshToken() async {
return await _storage.read(key: "refresh_token");
}

Future<void> saveTokens(String accessToken, String refreshToken) async {
await _storage.write(key: "access_token", value: accessToken);
await _storage.write(key: "refresh_token", value: refreshToken);
}

Future<void> logout() async {
await _storage.delete(key: "access_token");
await _storage.delete(key: "refresh_token");
}

Future<Response> login(String username, String password) async {
Expand All @@ -62,4 +68,11 @@ class AuthService {
},
);
}

Future<Response> refreshAccessToken(String refreshToken) async {
return await _dio.post(
"/auth/refresh",
data: {"refresh_token": refreshToken},
);
}
}
Loading