Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `DebugScreen` sign out action for quick manual verification.
- Added auth-aware routing with GoRouter redirect (`/auth/*` is public, other routes require authentication).
- Added localized authentication failure messages (ARB + `AuthFailure` → l10n mapper).
- Added Google Sign-In authentication.

### Changed

Expand Down
39 changes: 39 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,16 @@ PODS:
- abseil/meta/type_traits
- abseil/xcprivacy
- abseil/xcprivacy (1.20240722.0)
- AppAuth (2.0.0):
- AppAuth/Core (= 2.0.0)
- AppAuth/ExternalUserAgent (= 2.0.0)
- AppAuth/Core (2.0.0)
- AppAuth/ExternalUserAgent (2.0.0):
- AppAuth/Core
- AppCheckCore (11.2.0):
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0)
- PromisesObjC (~> 2.4)
- BoringSSL-GRPC (0.0.37):
- BoringSSL-GRPC/Implementation (= 0.0.37)
- BoringSSL-GRPC/Interface (= 0.0.37)
Expand Down Expand Up @@ -1323,6 +1333,11 @@ PODS:
- GoogleUtilities/Environment (~> 8.1)
- GTMSessionFetcher/Core (< 6.0, >= 3.4)
- Flutter (1.0.0)
- google_sign_in_ios (0.0.1):
- Flutter
- FlutterMacOS
- GoogleSignIn (~> 9.0)
- GTMSessionFetcher (>= 3.4.0)
- GoogleAdsOnDeviceConversion (3.2.0):
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
Expand Down Expand Up @@ -1353,6 +1368,11 @@ PODS:
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- GoogleSignIn (9.1.0):
- AppAuth (~> 2.0)
- AppCheckCore (~> 11.0)
- GTMAppAuth (~> 5.0)
- GTMSessionFetcher/Core (~> 3.3)
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
Expand Down Expand Up @@ -1472,7 +1492,14 @@ PODS:
- gRPC-Core/Privacy (= 1.69.0)
- gRPC-Core/Interface (1.69.0)
- gRPC-Core/Privacy (1.69.0)
- GTMAppAuth (5.0.0):
- AppAuth/Core (~> 2.0)
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
- GTMSessionFetcher (3.5.0):
- GTMSessionFetcher/Full (= 3.5.0)
- GTMSessionFetcher/Core (3.5.0)
- GTMSessionFetcher/Full (3.5.0):
- GTMSessionFetcher/Core
- leveldb-library (1.22.6)
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
Expand Down Expand Up @@ -1502,13 +1529,16 @@ DEPENDENCIES:
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
- firebase_storage (from `.symlinks/plugins/firebase_storage/ios`)
- Flutter (from `Flutter`)
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)

SPEC REPOS:
trunk:
- abseil
- AppAuth
- AppCheckCore
- BoringSSL-GRPC
- Firebase
- FirebaseAnalytics
Expand All @@ -1529,9 +1559,11 @@ SPEC REPOS:
- GoogleAdsOnDeviceConversion
- GoogleAppMeasurement
- GoogleDataTransport
- GoogleSignIn
- GoogleUtilities
- "gRPC-C++"
- gRPC-Core
- GTMAppAuth
- GTMSessionFetcher
- leveldb-library
- nanopb
Expand All @@ -1556,6 +1588,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_storage/ios"
Flutter:
:path: Flutter
google_sign_in_ios:
:path: ".symlinks/plugins/google_sign_in_ios/darwin"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
shared_preferences_foundation:
Expand All @@ -1565,6 +1599,8 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
abseil: a05cc83bf02079535e17169a73c5be5ba47f714b
AppAuth: 1c1a8afa7e12f2ec3a294d9882dfa5ab7d3cb063
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508
cloud_firestore: 45532e0c1fd1f6a5720af8aa0312470c366b73d2
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
Expand All @@ -1590,12 +1626,15 @@ SPEC CHECKSUMS:
FirebaseSharedSwift: 79f27fff0addd15c3de19b87fba426f3cc2c964f
FirebaseStorage: 550349b1e8f7315834ea08308696e9469d77135d
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
google_sign_in_ios: 00dfa94252eb10278b64828c81bcb7158a81a53a
GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f
GoogleAppMeasurement: 3bf40aff49a601af5da1c3345702fcb4991d35ee
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleSignIn: fcee2257188d5eda57a5e2b6a715550ffff9206d
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
"gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8
gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330
GTMAppAuth: 217a876b249c3c585a54fd6f73e6b58c4f5c4238
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
Expand Down
18 changes: 18 additions & 0 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
3D0E01DF18316EDF773EAB0D /* [CP] Embed Pods Frameworks */,
5180CEFDE13AEFAE9BBD9627 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -350,6 +351,23 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
5180CEFDE13AEFAE9BBD9627 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
Expand Down
15 changes: 15 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>GIDClientID</key>
<string>342615478088-590oga73rl6r8hoodfnfpfh6gj38kn2f.apps.googleusercontent.com</string>
<!-- Google Sign-in Section -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.342615478088-590oga73rl6r8hoodfnfpfh6gj38kn2f</string>
</array>
</dict>
</array>
<!-- End of the Google Sign-in Section -->
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
Expand Down
19 changes: 16 additions & 3 deletions lib/core/di/di.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:get_it/get_it.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:logger/logger.dart';

import '../../features/auth/data/repositories/auth_repository_impl.dart';
Expand All @@ -21,7 +22,7 @@ import '../utils/logger/logger_setup.dart';
final di = GetIt.instance;

/// Initializes the application's dependencies using GetIt.
void setupDI() {
Future<void> setupDI() async {
// Logger
di.registerLazySingleton<Logger>(() => createLogger());
di.registerLazySingleton<AppLogger>(() => AppLoggerImpl(di<Logger>()));
Expand All @@ -36,8 +37,18 @@ void setupDI() {

// Authentication
di.registerLazySingleton<FirebaseAuth>(() => FirebaseAuth.instance);
di.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(di<AppLogger>(), di<FirebaseAuth>()),
di.registerSingletonAsync<GoogleSignIn>(() async {
final googleSignIn = GoogleSignIn.instance;
await googleSignIn.initialize();
return googleSignIn;
});
di.registerSingletonWithDependencies<AuthRepository>(
() => AuthRepositoryImpl(
di<AppLogger>(),
di<FirebaseAuth>(),
di<GoogleSignIn>(),
),
dependsOn: [GoogleSignIn],
);
di.registerLazySingleton(() => AuthBloc(di<AppAnalytics>(), di<AuthRepository>()));

Expand All @@ -47,4 +58,6 @@ void setupDI() {
() => NetworkServiceImpl(di<Connectivity>()),
dispose: (param) => param.dispose(),
);

await di.allReady();
}
72 changes: 72 additions & 0 deletions lib/core/failures/feature/auth/auth_failure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,78 @@ final class NetworkRequestFailedFailure extends AuthFailure {
}) : super('');
}

/// Operation cancelled error.
final class OperationCancelledFailure extends AuthFailure {
@override
String get code => 'canceled';

/// Creates an instance of [OperationCancelledFailure].
const OperationCancelledFailure({
super.stackTrace,
super.parentException,
}) : super('');
}

/// Operation interrupted error.
final class OperationInterruptedFailure extends AuthFailure {
@override
String get code => 'interrupted';

/// Creates an instance of [OperationInterruptedFailure].
const OperationInterruptedFailure({
super.stackTrace,
super.parentException,
}) : super('');
}

/// Client configuration error.
final class ClientConfigurationFailure extends AuthFailure {
@override
String get code => 'clientConfigurationError';

/// Creates an instance of [ClientConfigurationFailure].
const ClientConfigurationFailure({
super.stackTrace,
super.parentException,
}) : super('');
}

/// Provider configuration error.
final class ProviderConfigurationFailure extends AuthFailure {
@override
String get code => 'providerConfigurationError';

/// Creates an instance of [ProviderConfigurationFailure].
const ProviderConfigurationFailure({
super.stackTrace,
super.parentException,
}) : super('');
}

/// UI unavailable error.
final class UIUnavailableFailure extends AuthFailure {
@override
String get code => 'uiUnavailable';

/// Creates an instance of [UIUnavailableFailure].
const UIUnavailableFailure({
super.stackTrace,
super.parentException,
}) : super('');
}

/// Operation cancelled error.
final class UserMismatchFailure extends AuthFailure {
@override
String get code => 'userMismatch';

/// Creates an instance of [UserMismatchFailure].
const UserMismatchFailure({
super.stackTrace,
super.parentException,
}) : super('');
}

/// Unknown authentication error.
final class UnknownAuthFailure extends AuthFailure {
@override
Expand Down
45 changes: 45 additions & 0 deletions lib/features/auth/data/mappers/google_auth_failure_mapper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:google_sign_in/google_sign_in.dart';

import '../../../../core/failures/feature/auth/auth_failure.dart';

/// Extension for converting [GoogleSignInException] to domain [AuthFailure].
extension GoogleAuthFailureMapper on GoogleSignInException {
/// Converts [GoogleSignInException] to domain [AuthFailure].
///
/// Returns a specific [AuthFailure] subclass based on the Google Sign In error code,
/// or [UnknownAuthFailure] for unmapped error codes.
AuthFailure toAuthFailure([StackTrace? stackTrace]) {
return switch (code) {
GoogleSignInExceptionCode.canceled => OperationCancelledFailure(
parentException: this,
stackTrace: stackTrace,
),
GoogleSignInExceptionCode.interrupted => OperationInterruptedFailure(
parentException: this,
stackTrace: stackTrace,
),
GoogleSignInExceptionCode.clientConfigurationError => ClientConfigurationFailure(
parentException: this,
stackTrace: stackTrace,
),
GoogleSignInExceptionCode.providerConfigurationError => ProviderConfigurationFailure(
parentException: this,
stackTrace: stackTrace,
),
GoogleSignInExceptionCode.uiUnavailable => UIUnavailableFailure(
parentException: this,
stackTrace: stackTrace,
),
GoogleSignInExceptionCode.userMismatch => UserMismatchFailure(
parentException: this,
stackTrace: stackTrace,
),
GoogleSignInExceptionCode.unknownError => UnknownAuthFailure(
'unknownError',
originalMessage: description,
parentException: this,
stackTrace: stackTrace,
),
};
}
}
Loading