Files
mobile-app/lib/features/auth/data/auth_repository.dart

193 lines
5.7 KiB
Dart

import 'package:dio/dio.dart';
import 'package:real_estate_mobile/core/constants/api_constants.dart';
import 'package:real_estate_mobile/core/network/api_client.dart';
import 'package:real_estate_mobile/core/network/api_exceptions.dart';
import 'package:real_estate_mobile/core/storage/secure_storage.dart';
import 'package:real_estate_mobile/features/auth/data/models/auth_response.dart';
import 'package:real_estate_mobile/features/auth/data/models/register_request.dart';
import 'package:real_estate_mobile/features/auth/data/models/user_model.dart';
class AuthRepository {
final Dio _dio = ApiClient.instance.dio;
Future<Map<String, dynamic>> login({
required String email,
required String password,
}) async {
try {
final response = await _dio.post(
ApiConstants.authLogin,
data: {'email': email, 'password': password},
);
final data = response.data['data'] as Map<String, dynamic>;
// Check if 2FA is required
if (data['requiresTwoFactor'] == true) {
return {
'requiresTwoFactor': true,
'tempToken': data['tempToken'],
};
}
final authResponse = AuthResponse.fromJson(data);
await SecureStorage.saveTokens(
accessToken: authResponse.accessToken,
refreshToken: authResponse.refreshToken,
);
return {
'requiresTwoFactor': false,
'user': authResponse.user,
};
} on DioException catch (e) {
if (e.error is ApiException) {
throw e.error as ApiException;
}
throw ApiException(
message: e.message ?? 'Login failed',
statusCode: e.response?.statusCode,
);
}
}
/// Verify 2FA TOTP token during login
Future<AuthResponse> verifyTwoFactor({
required String tempToken,
required String token,
}) async {
try {
final response = await _dio.post(
ApiConstants.twoFactorVerify,
data: {'tempToken': tempToken, 'token': token},
);
final data = response.data['data'] as Map<String, dynamic>;
final authResponse = AuthResponse.fromJson(data);
await SecureStorage.saveTokens(
accessToken: authResponse.accessToken,
refreshToken: authResponse.refreshToken,
);
return authResponse;
} on DioException catch (e) {
if (e.error is ApiException) throw e.error as ApiException;
throw ApiException(
message: e.message ?? '2FA verification failed',
statusCode: e.response?.statusCode,
);
}
}
/// Verify backup code during login
Future<AuthResponse> verifyBackupCode({
required String tempToken,
required String backupCode,
}) async {
try {
final response = await _dio.post(
ApiConstants.twoFactorVerifyBackup,
data: {'tempToken': tempToken, 'backupCode': backupCode},
);
final data = response.data['data'] as Map<String, dynamic>;
final authResponse = AuthResponse.fromJson(data);
await SecureStorage.saveTokens(
accessToken: authResponse.accessToken,
refreshToken: authResponse.refreshToken,
);
return authResponse;
} on DioException catch (e) {
if (e.error is ApiException) throw e.error as ApiException;
throw ApiException(
message: e.message ?? 'Backup code verification failed',
statusCode: e.response?.statusCode,
);
}
}
Future<UserModel> getMe() async {
try {
final response = await _dio.get(ApiConstants.authMe);
final data = response.data['data'] as Map<String, dynamic>;
return UserModel.fromJson(data);
} on DioException catch (e) {
if (e.error is ApiException) {
throw e.error as ApiException;
}
throw ApiException(
message: e.message ?? 'Failed to fetch user',
statusCode: e.response?.statusCode,
);
}
}
Future<void> logout() async {
try {
await _dio.post(ApiConstants.authLogout);
} catch (_) {
// Ignore logout API errors — still clear local tokens
} finally {
await SecureStorage.clearTokens();
}
}
/// Social auth (Google, Facebook, etc.) — calls POST /auth/social
Future<UserModel> socialAuth({
required String provider,
required String providerId,
required String email,
String? name,
String? avatar,
}) async {
try {
final response = await _dio.post(
ApiConstants.authSocial,
data: {
'provider': provider,
'providerId': providerId,
'email': email,
if (name != null) 'name': name,
if (avatar != null) 'avatar': avatar,
},
);
final data = response.data['data'] as Map<String, dynamic>;
final authResponse = AuthResponse.fromJson(data);
await SecureStorage.saveTokens(
accessToken: authResponse.accessToken,
refreshToken: authResponse.refreshToken,
);
return authResponse.user;
} on DioException catch (e) {
if (e.error is ApiException) throw e.error as ApiException;
throw ApiException(
message: e.message ?? 'Social login failed',
statusCode: e.response?.statusCode,
);
}
}
/// Register returns { message, user } — no tokens.
/// The user must verify their email before logging in.
Future<Map<String, dynamic>> register(RegisterRequest request) async {
try {
final response = await _dio.post(
ApiConstants.authRegister,
data: request.toJson(),
);
final data = response.data['data'] as Map<String, dynamic>;
return data;
} on DioException catch (e) {
if (e.error is ApiException) {
throw e.error as ApiException;
}
throw ApiException(
message: e.message ?? 'Registration failed',
statusCode: e.response?.statusCode,
);
}
}
}