Add Apple Login => Works only on iPhone for now, will be adding rest later.

This commit is contained in:
Van Leemput Dayron
2025-11-03 01:06:19 +01:00
parent 9cfd9b4ec7
commit 745f2597d9
7 changed files with 160 additions and 47 deletions

View File

@@ -2,9 +2,10 @@ import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/user.dart';
import '../services/auth_service.dart';
import '../services/error_service.dart';
/// Repository for authentication operations and user data management.
///
///
/// This repository handles authentication logic and user data persistence
/// by coordinating between Firebase Auth and Firestore. It provides a
/// clean interface for authentication operations while managing the
@@ -12,38 +13,39 @@ import '../services/auth_service.dart';
class AuthRepository {
/// Authentication service for Firebase Auth operations.
final AuthService _authService;
/// Firestore instance for user data operations.
final FirebaseFirestore _firestore;
/// Error service for logging authentication errors.
final _errorService = ErrorService();
/// Creates a new [AuthRepository] with optional service dependencies.
///
///
/// If [authService] or [firestore] are not provided, default instances will be used.
AuthRepository({
AuthService? authService,
FirebaseFirestore? firestore,
}) : _authService = authService ?? AuthService(),
_firestore = firestore ?? FirebaseFirestore.instance;
AuthRepository({AuthService? authService, FirebaseFirestore? firestore})
: _authService = authService ?? AuthService(),
_firestore = firestore ?? FirebaseFirestore.instance;
/// Stream of authentication state changes.
///
///
/// Emits Firebase user objects when authentication state changes.
Stream<firebase_auth.User?> get authStateChanges =>
_authService.authStateChanges;
/// Gets the currently authenticated Firebase user.
///
///
/// Returns null if no user is currently authenticated.
firebase_auth.User? get currentUser => _authService.currentUser;
/// Signs in a user with email and password.
///
///
/// Authenticates with Firebase Auth and retrieves the corresponding
/// user data from Firestore.
///
///
/// [email] - User's email address
/// [password] - User's password
///
///
/// Returns the [User] model if successful, null if user data not found.
/// Throws an exception if authentication fails.
Future<User?> signInWithEmailAndPassword({
@@ -57,20 +59,20 @@ class AuthRepository {
);
return await getUserFromFirestore(firebaseUser.user!.uid);
} catch (e) {
throw Exception('Sign-in error: $e');
_errorService.showError(message: 'Utilisateur ou mot de passe incorrect');
}
}
/// Creates a new user account with email and password.
///
///
/// Creates a Firebase Auth account and stores additional user information
/// in Firestore.
///
///
/// [email] - User's email address
/// [password] - User's password
/// [nom] - User's last name
/// [prenom] - User's first name
///
///
/// Returns the created [User] model if successful.
/// Throws an exception if account creation fails.
Future<User?> signUpWithEmailAndPassword({
@@ -95,17 +97,16 @@ class AuthRepository {
await _firestore.collection('users').doc(user.id).set(user.toMap());
return user;
} catch (e) {
throw Exception('Registration error: $e');
_errorService.showError(message: 'Erreur lors de la création du compte');
}
}
/// Signs in a user using Google authentication.
///
///
/// Handles Google sign-in flow and creates/retrieves user data from Firestore.
/// If the user doesn't exist, creates a new user document.
///
///
/// Returns the [User] model if successful, null if Google sign-in was cancelled.
/// Throws an exception if authentication fails.
Future<User?> signInWithGoogle() async {
@@ -115,7 +116,7 @@ class AuthRepository {
if (firebaseUser.user != null) {
// Check if user already exists in Firestore
final existingUser = await getUserFromFirestore(firebaseUser.user!.uid);
if (existingUser != null) {
return existingUser;
}
@@ -133,24 +134,25 @@ class AuthRepository {
}
return null;
} catch (e) {
throw Exception('Google sign-in error: $e');
_errorService.showError(message: 'Erreur lors de la connexion Google');
}
return null;
}
/// Signs in a user using Apple authentication.
///
///
/// Handles Apple sign-in flow and creates/retrieves user data from Firestore.
/// If the user doesn't exist, creates a new user document.
///
///
/// Returns the [User] model if successful, null if Apple sign-in was cancelled.
/// Throws an exception if authentication fails.
Future<User?> signInWithApple() async {
try {
final firebaseUser = await _authService.signInWithApple();
if (firebaseUser?.user != null) {
final existingUser = await getUserFromFirestore(firebaseUser!.user!.uid);
if (firebaseUser.user != null) {
final existingUser = await getUserFromFirestore(firebaseUser.user!.uid);
if (existingUser != null) {
return existingUser;
}
@@ -167,33 +169,34 @@ class AuthRepository {
}
return null;
} catch (e) {
throw Exception('Apple sign-in error: $e');
_errorService.showError(message: 'Erreur lors de la connexion Apple');
}
return null;
}
/// Signs out the current user.
///
///
/// Clears the authentication state and signs out from Firebase Auth.
Future<void> signOut() async {
await _authService.signOut();
}
/// Sends a password reset email to the specified email address.
///
///
/// [email] - The email address to send the reset link to
///
///
/// Throws an exception if the operation fails.
Future<void> resetPassword(String email) async {
await _authService.resetPassword(email);
}
/// Retrieves user data from Firestore by user ID.
///
///
/// Fetches the user document from the 'users' collection and converts
/// it to a [User] model.
///
///
/// [uid] - The Firebase user ID to look up
///
///
/// Returns the [User] model if found, null otherwise.
Future<User?> getUserFromFirestore(String uid) async {
try {
@@ -207,4 +210,4 @@ class AuthRepository {
return null;
}
}
}
}

View File

@@ -1,6 +1,7 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart';
import 'package:travel_mate/services/error_service.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
/// Service for handling Firebase authentication operations.
///
@@ -178,8 +179,83 @@ class AuthService {
/// Signs in a user using Apple authentication.
///
/// This method is currently a placeholder for future Apple authentication support.
Future signInWithApple() async {
// TODO: Implement Apple sign-in
/// Handles the complete Apple Sign-In flow including platform initialization
/// and credential exchange with Firebase. Supports both iOS and Android platforms.
///
/// Returns a [UserCredential] containing the authenticated user's information.
/// Throws various exceptions if authentication fails or platform is not supported.
Future<UserCredential> signInWithApple() async {
try {
await ensureInitialized();
// Check if Apple Sign-In is available on this platform
final bool isAvailable = await SignInWithApple.isAvailable();
if (!isAvailable) {
throw FirebaseAuthException(
code: 'ERROR_APPLE_SIGNIN_NOT_AVAILABLE',
message: 'Apple Sign-In is not available on this platform',
);
}
// Request Apple ID credential with platform-specific configuration
final AuthorizationCredentialAppleID credential =
await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
// Configuration for Android/Web
webAuthenticationOptions: WebAuthenticationOptions(
clientId: 'be.devdayronvl.TravelMate',
redirectUri: Uri.parse(
'https://your-project-id.firebaseapp.com/__/auth/handler',
),
),
);
// Create OAuth credential for Firebase
final OAuthCredential oauthCredential = OAuthProvider("apple.com")
.credential(
idToken: credential.identityToken,
accessToken: credential.authorizationCode,
);
// Sign in with Firebase
UserCredential userCredential = await firebaseAuth.signInWithCredential(
oauthCredential,
);
// Update display name if it's the first sign-in and we have name info
if (userCredential.additionalUserInfo?.isNewUser == true &&
credential.givenName != null &&
credential.familyName != null) {
final String displayName =
'${credential.givenName} ${credential.familyName}';
await userCredential.user?.updateDisplayName(displayName);
}
return userCredential;
} on SignInWithAppleException catch (e) {
_errorService.logError('Apple Sign-In error: $e', StackTrace.current);
throw FirebaseAuthException(
code: 'ERROR_APPLE_SIGNIN_FAILED',
message: 'Apple Sign-In failed: ${e.toString()}',
);
} on FirebaseAuthException catch (e) {
_errorService.logError(
'Firebase error during Apple Sign-In: $e',
StackTrace.current,
);
rethrow;
} catch (e) {
_errorService.logError(
'Unknown error during Apple Sign-In: $e',
StackTrace.current,
);
throw FirebaseAuthException(
code: 'ERROR_APPLE_SIGNIN_UNKNOWN',
message: 'Unknown error during Apple Sign-In: $e',
);
}
}
}