diff --git a/apps/packages/data_connect/lib/krow_data_connect.dart b/apps/packages/data_connect/lib/krow_data_connect.dart index 3a42b836..783b61df 100644 --- a/apps/packages/data_connect/lib/krow_data_connect.dart +++ b/apps/packages/data_connect/lib/krow_data_connect.dart @@ -6,9 +6,8 @@ /// TODO: These mocks currently do not implement any specific interfaces. /// They will implement interfaces defined in feature packages once those are created. -export 'src/mocks/auth_repository_mock.dart'; +// export 'src/mocks/auth_repository_mock.dart'; // Comentado, usaremos la implementación real export 'src/mocks/staff_repository_mock.dart'; -export 'src/mocks/business_repository_mock.dart'; export 'src/mocks/event_repository_mock.dart'; export 'src/mocks/skill_repository_mock.dart'; export 'src/mocks/financial_repository_mock.dart'; @@ -16,3 +15,6 @@ export 'src/mocks/rating_repository_mock.dart'; export 'src/mocks/support_repository_mock.dart'; export 'src/mocks/home_repository_mock.dart'; export 'src/data_connect_module.dart'; + +// Export the generated Data Connect SDK +export 'src/dataconnect_generated/generated.dart'; \ No newline at end of file diff --git a/apps/packages/features/client/authentication/lib/client_authentication.dart b/apps/packages/features/client/authentication/lib/client_authentication.dart index 268a1dc7..2a5b3cde 100644 --- a/apps/packages/features/client/authentication/lib/client_authentication.dart +++ b/apps/packages/features/client/authentication/lib/client_authentication.dart @@ -1,5 +1,6 @@ library client_authentication; +import 'package:firebase_auth/firebase_auth.dart' as firebase; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_data_connect/krow_data_connect.dart'; import 'src/data/repositories_impl/auth_repository_impl.dart'; @@ -28,7 +29,10 @@ class ClientAuthenticationModule extends Module { void binds(Injector i) { // Repositories i.addLazySingleton( - () => AuthRepositoryImpl(dataSource: i.get()), + () => AuthRepositoryImpl( + firebaseAuth: firebase.FirebaseAuth.instance, + dataConnect: ExampleConnector.instance, + ), ); // UseCases diff --git a/apps/packages/features/client/authentication/lib/src/data/repositories_impl/auth_repository_impl.dart b/apps/packages/features/client/authentication/lib/src/data/repositories_impl/auth_repository_impl.dart index 176f6a64..9df42375 100644 --- a/apps/packages/features/client/authentication/lib/src/data/repositories_impl/auth_repository_impl.dart +++ b/apps/packages/features/client/authentication/lib/src/data/repositories_impl/auth_repository_impl.dart @@ -1,43 +1,154 @@ -import 'package:krow_data_connect/krow_data_connect.dart'; -import 'package:krow_domain/krow_domain.dart'; - +import 'package:firebase_auth/firebase_auth.dart' as firebase; +import 'package:krow_data_connect/krow_data_connect.dart' as dc; +import 'package:krow_domain/krow_domain.dart' as domain; import '../../domain/repositories/auth_repository_interface.dart'; -/// Production-ready implementation of the [AuthRepositoryInterface]. +/// Production-ready implementation of the [AuthRepositoryInterface] for the client app. /// -/// This implementation integrates with the [krow_data_connect] package to provide -/// authentication services. It delegates actual data operations to the -/// [AuthRepositoryMock] (or eventually the real implementation) injected from the app layer. +/// This implementation integrates with Firebase Authentication for user +/// identity management and Krow's Data Connect SDK for storing user profile data. class AuthRepositoryImpl implements AuthRepositoryInterface { - /// The data source used for authentication operations. - final AuthRepositoryMock _dataSource; + final firebase.FirebaseAuth _firebaseAuth; + final dc.ExampleConnector _dataConnect; - /// Creates an [AuthRepositoryImpl] with the injected [dataSource] dependency. - AuthRepositoryImpl({required AuthRepositoryMock dataSource}) - : _dataSource = dataSource; + /// Creates an [AuthRepositoryImpl] with the real dependencies. + AuthRepositoryImpl({ + required firebase.FirebaseAuth firebaseAuth, + required dc.ExampleConnector dataConnect, + }) : _firebaseAuth = firebaseAuth, + _dataConnect = dataConnect; @override - Future signInWithEmail({ + Future signInWithEmail({ required String email, required String password, - }) { - return _dataSource.signInWithEmail(email, password); + }) async { + try { + final credential = await _firebaseAuth.signInWithEmailAndPassword( + email: email, + password: password, + ); + + final firebaseUser = credential.user; + if (firebaseUser == null) { + throw Exception('Sign-in failed, no Firebase user received.'); + } + + return _getUserProfile( + firebaseUserId: firebaseUser.uid, + fallbackEmail: firebaseUser.email ?? email, + ); + + } on firebase.FirebaseAuthException catch (e) { + if (e.code == 'user-not-found' || e.code == 'wrong-password') { + throw Exception('Incorrect email or password.'); + } else { + throw Exception('Authentication error: ${e.message}'); + } + } catch (e) { + throw Exception('Failed to sign in and fetch user data: ${e.toString()}'); + } } @override - Future signUpWithEmail({ + Future signUpWithEmail({ required String companyName, required String email, required String password, - }) { - return _dataSource.signUpWithEmail(email, password, companyName); + }) async { + try { + final credential = await _firebaseAuth.createUserWithEmailAndPassword( + email: email, + password: password, + ); + + final firebaseUser = credential.user; + if (firebaseUser == null) { + throw Exception('Sign-up failed, Firebase user could not be created.'); + } + + // Client-specific business logic: + // 1. Create a `Business` entity. + // 2. Create a `User` entity associated with the business. + final createBusinessResponse = await _dataConnect.createBusiness( + businessName: companyName, + userId: firebaseUser.uid, + rateGroup: dc.BusinessRateGroup.STANDARD, + status: dc.BusinessStatus.PENDING, + ).execute(); + + final businessData = createBusinessResponse.data?.business_insert; + if (businessData == null) { + await firebaseUser.delete(); // Rollback if business creation fails + throw Exception('Business creation failed after Firebase user registration.'); + } + + final createUserResponse = await _dataConnect.createUser( + id: firebaseUser.uid, + role: dc.UserBaseRole.USER, + ) + .email(email) + .userRole('BUSINESS') + .execute(); + + final newUserData = createUserResponse.data?.user_insert; + if (newUserData == null) { + await firebaseUser.delete(); // Rollback if user profile creation fails + // TODO: Also delete the created Business if this fails + throw Exception('User profile creation failed after Firebase user registration.'); + } + + return _getUserProfile( + firebaseUserId: firebaseUser.uid, + fallbackEmail: firebaseUser.email ?? email, + ); + + } on firebase.FirebaseAuthException catch (e) { + if (e.code == 'weak-password') { + throw Exception('The password provided is too weak.'); + } else if (e.code == 'email-already-in-use') { + throw Exception('An account already exists for that email address.'); + } else { + throw Exception('Sign-up error: ${e.message}'); + } + } catch (e) { + throw Exception('Failed to sign up and create user data: ${e.toString()}'); + } } @override - Future signInWithSocial({required String provider}) { - return _dataSource.signInWithSocial(provider); + Future signOut() async { + try { + await _firebaseAuth.signOut(); + } catch (e) { + throw Exception('Error signing out: ${e.toString()}'); + } } @override - Future signOut() => _dataSource.signOut(); + Future signInWithSocial({required String provider}) { + throw UnimplementedError('Social authentication with $provider is not yet implemented.'); + } + + Future _getUserProfile({ + required String firebaseUserId, + required String? fallbackEmail, + }) async { + final response = await _dataConnect.getUserById(id: firebaseUserId).execute(); + final user = response.data?.user; + if (user == null) { + throw Exception('Authenticated user profile not found in database.'); + } + + final email = user.email ?? fallbackEmail; + if (email == null || email.isEmpty) { + throw Exception('User email is missing in profile data.'); + } + + return domain.User( + id: user.id, + email: email, + role: user.role.stringValue, + ); + } }