diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 5e428d8..beef90f 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1444,6 +1444,8 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
+ - sign_in_with_apple (0.0.1):
+ - Flutter
- url_launcher_ios (0.0.1):
- Flutter
@@ -1461,6 +1463,7 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+ - sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
@@ -1520,6 +1523,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+ sign_in_with_apple:
+ :path: ".symlinks/plugins/sign_in_with_apple/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
@@ -1564,6 +1569,7 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
+ sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
PODFILE CHECKSUM: 53a6aebc29ccee84c41f92f409fc20cd4ca011f1
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 903ef39..9267a2a 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -340,14 +340,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
- inputPaths = (
- );
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
- outputPaths = (
- );
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
@@ -376,14 +372,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
- inputPaths = (
- );
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
- outputPaths = (
- );
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 1b1e3c5..630903a 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -62,5 +62,16 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ Apple Sign-In
+ CFBundleURLSchemes
+
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+
+
+
diff --git a/lib/repositories/auth_repository.dart b/lib/repositories/auth_repository.dart
index 83a34c1..2c334c5 100644
--- a/lib/repositories/auth_repository.dart
+++ b/lib/repositories/auth_repository.dart
@@ -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 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 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 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 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 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 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 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 getUserFromFirestore(String uid) async {
try {
@@ -207,4 +210,4 @@ class AuthRepository {
return null;
}
}
-}
\ No newline at end of file
+}
diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart
index 2110fe6..dbff737 100644
--- a/lib/services/auth_service.dart
+++ b/lib/services/auth_service.dart
@@ -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 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',
+ );
+ }
}
}
diff --git a/pubspec.lock b/pubspec.lock
index 1516e49..728df64 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -944,6 +944,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.4.1"
+ sign_in_with_apple:
+ dependency: "direct main"
+ description:
+ name: sign_in_with_apple
+ sha256: "8bd875c8e8748272749eb6d25b896f768e7e9d60988446d543fe85a37a2392b8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
+ sign_in_with_apple_platform_interface:
+ dependency: transitive
+ description:
+ name: sign_in_with_apple_platform_interface
+ sha256: "981bca52cf3bb9c3ad7ef44aace2d543e5c468bb713fd8dda4275ff76dfa6659"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ sign_in_with_apple_web:
+ dependency: transitive
+ description:
+ name: sign_in_with_apple_web
+ sha256: f316400827f52cafcf50d00e1a2e8a0abc534ca1264e856a81c5f06bd5b10fed
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
diff --git a/pubspec.yaml b/pubspec.yaml
index 33c77d6..0783493 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -55,6 +55,7 @@ dependencies:
intl: ^0.20.2
firebase_storage: ^13.0.3
url_launcher: ^6.3.2
+ sign_in_with_apple: ^7.0.1
dev_dependencies:
flutter_launcher_icons: ^0.13.1