feat(auth): Refactor AuthRepositoryImpl and ProfileSetupRepositoryImpl to use DataConnectService for authentication and data operations

This commit is contained in:
Achintha Isuru
2026-02-16 15:47:01 -05:00
parent 3245c957f6
commit d0585d12ab
3 changed files with 76 additions and 74 deletions

View File

@@ -10,20 +10,14 @@ import '../../domain/ui_entities/auth_mode.dart';
import '../../domain/repositories/auth_repository_interface.dart';
/// Implementation of [AuthRepositoryInterface].
class AuthRepositoryImpl
with DataErrorHandler
implements AuthRepositoryInterface {
AuthRepositoryImpl({
required this.firebaseAuth,
required this.dataConnect,
});
class AuthRepositoryImpl implements AuthRepositoryInterface {
AuthRepositoryImpl() : _service = DataConnectService.instance;
final FirebaseAuth firebaseAuth;
final ExampleConnector dataConnect;
final DataConnectService _service;
Completer<String?>? _pendingVerification;
@override
Stream<domain.User?> get currentUser => firebaseAuth
Stream<domain.User?> get currentUser => _service.auth
.authStateChanges()
.map((User? firebaseUser) {
if (firebaseUser == null) {
@@ -44,7 +38,7 @@ class AuthRepositoryImpl
final Completer<String?> completer = Completer<String?>();
_pendingVerification = completer;
await firebaseAuth.verifyPhoneNumber(
await _service.auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
verificationCompleted: (PhoneAuthCredential credential) {
// Skip auto-verification for test numbers to allow manual code entry
@@ -101,7 +95,8 @@ class AuthRepositoryImpl
@override
Future<void> signOut() {
StaffSessionStore.instance.clear();
return firebaseAuth.signOut();
_service.clearCache();
return _service.auth.signOut();
}
/// Verifies an OTP code and returns the authenticated user.
@@ -115,10 +110,10 @@ class AuthRepositoryImpl
verificationId: verificationId,
smsCode: smsCode,
);
final UserCredential userCredential = await executeProtected(
final UserCredential userCredential = await _service.run(
() async {
try {
return await firebaseAuth.signInWithCredential(credential);
return await _service.auth.signInWithCredential(credential);
} on FirebaseAuthException catch (e) {
if (e.code == 'invalid-verification-code') {
throw const domain.InvalidCredentialsException(
@@ -128,45 +123,56 @@ class AuthRepositoryImpl
rethrow;
}
},
requiresAuthentication: false,
);
final User? firebaseUser = userCredential.user;
if (firebaseUser == null) {
throw const domain.SignInFailedException(
technicalMessage: 'Phone verification failed, no Firebase user received.',
technicalMessage:
'Phone verification failed, no Firebase user received.',
);
}
final QueryResult<GetUserByIdData, GetUserByIdVariables> response =
await executeProtected(() => dataConnect
.getUserById(
id: firebaseUser.uid,
)
.execute());
await _service.run(
() => _service.connector
.getUserById(
id: firebaseUser.uid,
)
.execute(),
requiresAuthentication: false,
);
final GetUserByIdUser? user = response.data.user;
GetStaffByUserIdStaffs? staffRecord;
if (mode == AuthMode.signup) {
if (user == null) {
await executeProtected(() => dataConnect
.createUser(
id: firebaseUser.uid,
role: UserBaseRole.USER,
)
.userRole('STAFF')
.execute());
await _service.run(
() => _service.connector
.createUser(
id: firebaseUser.uid,
role: UserBaseRole.USER,
)
.userRole('STAFF')
.execute(),
requiresAuthentication: false,
);
} else {
// User exists in PostgreSQL. Check if they have a STAFF profile.
final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables>
staffResponse = await executeProtected(() => dataConnect
.getStaffByUserId(
userId: firebaseUser.uid,
)
.execute());
staffResponse = await _service.run(
() => _service.connector
.getStaffByUserId(
userId: firebaseUser.uid,
)
.execute(),
requiresAuthentication: false,
);
if (staffResponse.data.staffs.isNotEmpty) {
// If profile exists, they should use Login mode.
await firebaseAuth.signOut();
await _service.auth.signOut();
throw const domain.AccountExistsException(
technicalMessage:
'This user already has a staff profile. Please log in.',
@@ -177,35 +183,44 @@ class AuthRepositoryImpl
// they are allowed to "Sign Up" for Staff.
// We update their userRole to 'BOTH'.
if (user.userRole == 'BUSINESS') {
await executeProtected(() =>
dataConnect.updateUser(id: firebaseUser.uid).userRole('BOTH').execute());
await _service.run(
() => _service.connector
.updateUser(id: firebaseUser.uid)
.userRole('BOTH')
.execute(),
requiresAuthentication: false,
);
}
}
} else {
if (user == null) {
await firebaseAuth.signOut();
await _service.auth.signOut();
throw const domain.UserNotFoundException(
technicalMessage: 'Authenticated user profile not found in database.',
);
}
// Allow STAFF or BOTH roles to log in to the Staff App
if (user.userRole != 'STAFF' && user.userRole != 'BOTH') {
await firebaseAuth.signOut();
await _service.auth.signOut();
throw const domain.UnauthorizedAppException(
technicalMessage: 'User is not authorized for this app.',
);
}
final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables>
staffResponse = await executeProtected(() => dataConnect
.getStaffByUserId(
userId: firebaseUser.uid,
)
.execute());
staffResponse = await _service.run(
() => _service.connector
.getStaffByUserId(
userId: firebaseUser.uid,
)
.execute(),
requiresAuthentication: false,
);
if (staffResponse.data.staffs.isEmpty) {
await firebaseAuth.signOut();
await _service.auth.signOut();
throw const domain.UserNotFoundException(
technicalMessage: 'Your account is not registered yet. Please register first.',
technicalMessage:
'Your account is not registered yet. Please register first.',
);
}
staffRecord = staffResponse.data.staffs.first;

View File

@@ -1,18 +1,13 @@
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:krow_domain/krow_domain.dart';
import 'package:firebase_auth/firebase_auth.dart' as auth;
import '../../domain/repositories/profile_setup_repository.dart';
class ProfileSetupRepositoryImpl implements ProfileSetupRepository {
final auth.FirebaseAuth _firebaseAuth;
final ExampleConnector _dataConnect;
final DataConnectService _service;
ProfileSetupRepositoryImpl({
required auth.FirebaseAuth firebaseAuth,
required ExampleConnector dataConnect,
}) : _firebaseAuth = firebaseAuth,
_dataConnect = dataConnect;
ProfileSetupRepositoryImpl() : _service = DataConnectService.instance;
@override
Future<void> submitProfile({
@@ -23,17 +18,19 @@ class ProfileSetupRepositoryImpl implements ProfileSetupRepository {
required List<String> industries,
required List<String> skills,
}) async {
final auth.User? firebaseUser = _firebaseAuth.currentUser;
if (firebaseUser == null) {
throw Exception('User not authenticated.');
}
return _service.run(() async {
final auth.User? firebaseUser = _service.auth.currentUser;
if (firebaseUser == null) {
throw const NotAuthenticatedException(
technicalMessage: 'User not authenticated.');
}
final StaffSession? session = StaffSessionStore.instance.session;
final String email = session?.user.email ?? '';
final String? phone = firebaseUser.phoneNumber;
final StaffSession? session = StaffSessionStore.instance.session;
final String email = session?.user.email ?? '';
final String? phone = firebaseUser.phoneNumber;
final fdc.OperationResult<CreateStaffData, CreateStaffVariables>
result = await _dataConnect
final fdc.OperationResult<CreateStaffData, CreateStaffVariables> result =
await _service.connector
.createStaff(
userId: firebaseUser.uid,
fullName: fullName,
@@ -63,5 +60,6 @@ class ProfileSetupRepositoryImpl implements ProfileSetupRepository {
StaffSession(user: session.user, staff: staff, ownerId: session.ownerId),
);
}
});
}
}

View File

@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase;
import 'package:staff_authentication/src/data/repositories_impl/auth_repository_impl.dart';
import 'package:staff_authentication/src/domain/repositories/auth_repository_interface.dart';
import 'package:staff_authentication/src/domain/usecases/sign_in_with_phone_usecase.dart';
@@ -28,18 +27,8 @@ class StaffAuthenticationModule extends Module {
@override
void binds(Injector i) {
// Repositories
i.addLazySingleton<AuthRepositoryInterface>(
() => AuthRepositoryImpl(
firebaseAuth: firebase.FirebaseAuth.instance,
dataConnect: ExampleConnector.instance,
),
);
i.addLazySingleton<ProfileSetupRepository>(
() => ProfileSetupRepositoryImpl(
firebaseAuth: firebase.FirebaseAuth.instance,
dataConnect: ExampleConnector.instance,
),
);
i.addLazySingleton<AuthRepositoryInterface>(AuthRepositoryImpl.new);
i.addLazySingleton<ProfileSetupRepository>(ProfileSetupRepositoryImpl.new);
i.addLazySingleton<PlaceRepository>(PlaceRepositoryImpl.new);
// UseCases