feat: Refactor session management and improve user session data retrieval
This commit is contained in:
@@ -24,9 +24,8 @@ import '../../domain/repositories/auth_repository_interface.dart';
|
||||
/// identity management and Krow's Data Connect SDK for storing user profile data.
|
||||
class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
/// Creates an [AuthRepositoryImpl] with the real dependencies.
|
||||
AuthRepositoryImpl({
|
||||
dc.DataConnectService? service,
|
||||
}) : _service = service ?? dc.DataConnectService.instance;
|
||||
AuthRepositoryImpl({dc.DataConnectService? service})
|
||||
: _service = service ?? dc.DataConnectService.instance;
|
||||
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
@@ -36,11 +35,8 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
required String password,
|
||||
}) async {
|
||||
try {
|
||||
final firebase.UserCredential credential =
|
||||
await _service.auth.signInWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
final firebase.UserCredential credential = await _service.auth
|
||||
.signInWithEmailAndPassword(email: email, password: password);
|
||||
|
||||
final firebase.User? firebaseUser = credential.user;
|
||||
if (firebaseUser == null) {
|
||||
@@ -60,9 +56,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
technicalMessage: 'Firebase error code: ${e.code}',
|
||||
);
|
||||
} else if (e.code == 'network-request-failed') {
|
||||
throw NetworkException(
|
||||
technicalMessage: 'Firebase: ${e.message}',
|
||||
);
|
||||
throw NetworkException(technicalMessage: 'Firebase: ${e.message}');
|
||||
} else {
|
||||
throw SignInFailedException(
|
||||
technicalMessage: 'Firebase auth error: ${e.message}',
|
||||
@@ -71,9 +65,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
} on domain.AppException {
|
||||
rethrow;
|
||||
} catch (e) {
|
||||
throw SignInFailedException(
|
||||
technicalMessage: 'Unexpected error: $e',
|
||||
);
|
||||
throw SignInFailedException(technicalMessage: 'Unexpected error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,11 +80,8 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
|
||||
try {
|
||||
// Step 1: Try to create Firebase Auth user
|
||||
final firebase.UserCredential credential =
|
||||
await _service.auth.createUserWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
final firebase.UserCredential credential = await _service.auth
|
||||
.createUserWithEmailAndPassword(email: email, password: password);
|
||||
|
||||
firebaseUser = credential.user;
|
||||
if (firebaseUser == null) {
|
||||
@@ -111,9 +100,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
);
|
||||
} on firebase.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'weak-password') {
|
||||
throw WeakPasswordException(
|
||||
technicalMessage: 'Firebase: ${e.message}',
|
||||
);
|
||||
throw WeakPasswordException(technicalMessage: 'Firebase: ${e.message}');
|
||||
} else if (e.code == 'email-already-in-use') {
|
||||
// Email exists in Firebase Auth - try to sign in and complete registration
|
||||
return await _handleExistingFirebaseAccount(
|
||||
@@ -122,9 +109,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
companyName: companyName,
|
||||
);
|
||||
} else if (e.code == 'network-request-failed') {
|
||||
throw NetworkException(
|
||||
technicalMessage: 'Firebase: ${e.message}',
|
||||
);
|
||||
throw NetworkException(technicalMessage: 'Firebase: ${e.message}');
|
||||
} else {
|
||||
throw SignUpFailedException(
|
||||
technicalMessage: 'Firebase auth error: ${e.message}',
|
||||
@@ -133,15 +118,17 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
} on domain.AppException {
|
||||
// Rollback for our known exceptions
|
||||
await _rollbackSignUp(
|
||||
firebaseUser: firebaseUser, businessId: createdBusinessId);
|
||||
firebaseUser: firebaseUser,
|
||||
businessId: createdBusinessId,
|
||||
);
|
||||
rethrow;
|
||||
} catch (e) {
|
||||
// Rollback: Clean up any partially created resources
|
||||
await _rollbackSignUp(
|
||||
firebaseUser: firebaseUser, businessId: createdBusinessId);
|
||||
throw SignUpFailedException(
|
||||
technicalMessage: 'Unexpected error: $e',
|
||||
firebaseUser: firebaseUser,
|
||||
businessId: createdBusinessId,
|
||||
);
|
||||
throw SignUpFailedException(technicalMessage: 'Unexpected error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,16 +148,15 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
required String password,
|
||||
required String companyName,
|
||||
}) async {
|
||||
developer.log('Email exists in Firebase, attempting sign-in: $email',
|
||||
name: 'AuthRepository');
|
||||
developer.log(
|
||||
'Email exists in Firebase, attempting sign-in: $email',
|
||||
name: 'AuthRepository',
|
||||
);
|
||||
|
||||
try {
|
||||
// Try to sign in with the provided password
|
||||
final firebase.UserCredential credential =
|
||||
await _service.auth.signInWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
final firebase.UserCredential credential = await _service.auth
|
||||
.signInWithEmailAndPassword(email: email, password: password);
|
||||
|
||||
final firebase.User? firebaseUser = credential.user;
|
||||
if (firebaseUser == null) {
|
||||
@@ -180,32 +166,40 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
}
|
||||
|
||||
// Sign-in succeeded! Check if user already has a BUSINESS account in PostgreSQL
|
||||
final bool hasBusinessAccount =
|
||||
await _checkBusinessUserExists(firebaseUser.uid);
|
||||
final bool hasBusinessAccount = await _checkBusinessUserExists(
|
||||
firebaseUser.uid,
|
||||
);
|
||||
|
||||
if (hasBusinessAccount) {
|
||||
// User already has a KROW Client account
|
||||
developer.log('User already has BUSINESS account: ${firebaseUser.uid}',
|
||||
name: 'AuthRepository');
|
||||
developer.log(
|
||||
'User already has BUSINESS account: ${firebaseUser.uid}',
|
||||
name: 'AuthRepository',
|
||||
);
|
||||
throw AccountExistsException(
|
||||
technicalMessage: 'User ${firebaseUser.uid} already has BUSINESS role',
|
||||
technicalMessage:
|
||||
'User ${firebaseUser.uid} already has BUSINESS role',
|
||||
);
|
||||
}
|
||||
|
||||
// User exists in Firebase but not in KROW PostgreSQL - create the entities
|
||||
developer.log(
|
||||
'Creating BUSINESS account for existing Firebase user: ${firebaseUser.uid}',
|
||||
name: 'AuthRepository');
|
||||
'Creating BUSINESS account for existing Firebase user: ${firebaseUser.uid}',
|
||||
name: 'AuthRepository',
|
||||
);
|
||||
return await _createBusinessAndUser(
|
||||
firebaseUser: firebaseUser,
|
||||
companyName: companyName,
|
||||
email: email,
|
||||
onBusinessCreated: (_) {}, // No rollback needed for existing Firebase user
|
||||
onBusinessCreated:
|
||||
(_) {}, // No rollback needed for existing Firebase user
|
||||
);
|
||||
} on firebase.FirebaseAuthException catch (e) {
|
||||
// Sign-in failed - check why
|
||||
developer.log('Sign-in failed with code: ${e.code}',
|
||||
name: 'AuthRepository');
|
||||
developer.log(
|
||||
'Sign-in failed with code: ${e.code}',
|
||||
name: 'AuthRepository',
|
||||
);
|
||||
|
||||
if (e.code == 'wrong-password' || e.code == 'invalid-credential') {
|
||||
// Password doesn't match - check what providers are available
|
||||
@@ -229,8 +223,10 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
// We can't distinguish between "wrong password" and "no password provider"
|
||||
// due to Firebase deprecating fetchSignInMethodsForEmail.
|
||||
// The PasswordMismatchException message covers both scenarios.
|
||||
developer.log('Password mismatch or different provider for: $email',
|
||||
name: 'AuthRepository');
|
||||
developer.log(
|
||||
'Password mismatch or different provider for: $email',
|
||||
name: 'AuthRepository',
|
||||
);
|
||||
throw PasswordMismatchException(
|
||||
technicalMessage:
|
||||
'Email $email: password mismatch or different auth provider',
|
||||
@@ -242,7 +238,8 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
Future<bool> _checkBusinessUserExists(String firebaseUserId) async {
|
||||
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response =
|
||||
await _service.run(
|
||||
() => _service.connector.getUserById(id: firebaseUserId).execute());
|
||||
() => _service.connector.getUserById(id: firebaseUserId).execute(),
|
||||
);
|
||||
final dc.GetUserByIdUser? user = response.data.user;
|
||||
return user != null &&
|
||||
(user.userRole == 'BUSINESS' || user.userRole == 'BOTH');
|
||||
@@ -258,14 +255,16 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
// Create Business entity in PostgreSQL
|
||||
|
||||
final OperationResult<dc.CreateBusinessData, dc.CreateBusinessVariables>
|
||||
createBusinessResponse = await _service.run(() => _service.connector
|
||||
.createBusiness(
|
||||
businessName: companyName,
|
||||
userId: firebaseUser.uid,
|
||||
rateGroup: dc.BusinessRateGroup.STANDARD,
|
||||
status: dc.BusinessStatus.PENDING,
|
||||
)
|
||||
.execute());
|
||||
createBusinessResponse = await _service.run(
|
||||
() => _service.connector
|
||||
.createBusiness(
|
||||
businessName: companyName,
|
||||
userId: firebaseUser.uid,
|
||||
rateGroup: dc.BusinessRateGroup.STANDARD,
|
||||
status: dc.BusinessStatus.PENDING,
|
||||
)
|
||||
.execute(),
|
||||
);
|
||||
|
||||
final dc.CreateBusinessBusinessInsert businessData =
|
||||
createBusinessResponse.data.business_insert;
|
||||
@@ -273,28 +272,28 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
|
||||
// Check if User entity already exists in PostgreSQL
|
||||
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> userResult =
|
||||
await _service.run(() =>
|
||||
_service.connector.getUserById(id: firebaseUser.uid).execute());
|
||||
await _service.run(
|
||||
() => _service.connector.getUserById(id: firebaseUser.uid).execute(),
|
||||
);
|
||||
final dc.GetUserByIdUser? existingUser = userResult.data.user;
|
||||
|
||||
if (existingUser != null) {
|
||||
// User exists (likely in another app like STAFF). Update role to BOTH.
|
||||
await _service.run(() => _service.connector
|
||||
.updateUser(
|
||||
id: firebaseUser.uid,
|
||||
)
|
||||
.userRole('BOTH')
|
||||
.execute());
|
||||
await _service.run(
|
||||
() => _service.connector
|
||||
.updateUser(id: firebaseUser.uid)
|
||||
.userRole('BOTH')
|
||||
.execute(),
|
||||
);
|
||||
} else {
|
||||
// Create new User entity in PostgreSQL
|
||||
await _service.run(() => _service.connector
|
||||
.createUser(
|
||||
id: firebaseUser.uid,
|
||||
role: dc.UserBaseRole.USER,
|
||||
)
|
||||
.email(email)
|
||||
.userRole('BUSINESS')
|
||||
.execute());
|
||||
await _service.run(
|
||||
() => _service.connector
|
||||
.createUser(id: firebaseUser.uid, role: dc.UserBaseRole.USER)
|
||||
.email(email)
|
||||
.userRole('BUSINESS')
|
||||
.execute(),
|
||||
);
|
||||
}
|
||||
|
||||
return _getUserProfile(
|
||||
@@ -340,7 +339,8 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
@override
|
||||
Future<domain.User> signInWithSocial({required String provider}) {
|
||||
throw UnimplementedError(
|
||||
'Social authentication with $provider is not yet implemented.');
|
||||
'Social authentication with $provider is not yet implemented.',
|
||||
);
|
||||
}
|
||||
|
||||
Future<domain.User> _getUserProfile({
|
||||
@@ -349,8 +349,9 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
bool requireBusinessRole = false,
|
||||
}) async {
|
||||
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response =
|
||||
await _service.run(() =>
|
||||
_service.connector.getUserById(id: firebaseUserId).execute());
|
||||
await _service.run(
|
||||
() => _service.connector.getUserById(id: firebaseUserId).execute(),
|
||||
);
|
||||
final dc.GetUserByIdUser? user = response.data.user;
|
||||
if (user == null) {
|
||||
throw UserNotFoundException(
|
||||
@@ -383,22 +384,22 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
role: user.role.stringValue,
|
||||
);
|
||||
|
||||
final QueryResult<dc.GetBusinessesByUserIdData,
|
||||
dc.GetBusinessesByUserIdVariables> businessResponse =
|
||||
await _service.run(() => _service.connector
|
||||
.getBusinessesByUserId(
|
||||
userId: firebaseUserId,
|
||||
)
|
||||
.execute());
|
||||
final QueryResult<
|
||||
dc.GetBusinessesByUserIdData,
|
||||
dc.GetBusinessesByUserIdVariables
|
||||
>
|
||||
businessResponse = await _service.run(
|
||||
() => _service.connector
|
||||
.getBusinessesByUserId(userId: firebaseUserId)
|
||||
.execute(),
|
||||
);
|
||||
final dc.GetBusinessesByUserIdBusinesses? business =
|
||||
businessResponse.data.businesses.isNotEmpty
|
||||
? businessResponse.data.businesses.first
|
||||
: null;
|
||||
? businessResponse.data.businesses.first
|
||||
: null;
|
||||
|
||||
dc.ClientSessionStore.instance.setSession(
|
||||
dc.ClientSession(
|
||||
user: domainUser,
|
||||
userPhotoUrl: user.photoUrl,
|
||||
business: business == null
|
||||
? null
|
||||
: dc.ClientBusinessSession(
|
||||
|
||||
Reference in New Issue
Block a user