Flutter App Auth Manager πŸ”

A comprehensive Flutter authentication manager package that provides a unified interface for various authentication methods including Firebase Auth, Google Sign-In, Apple Sign-In, and more.

Features ✨

Core Authentication

  • Email/Password Authentication - Traditional sign-up and sign-in
  • Anonymous Authentication - Guest access functionality
  • Social Authentication - Google, Apple, Facebook, Twitter, Microsoft, GitHub
  • Phone Authentication - SMS-based verification
  • Biometric Authentication - Fingerprint and Face ID support

Advanced Features

  • Multi-Factor Authentication (MFA) - Enhanced security
  • Account Linking/Unlinking - Connect multiple providers
  • Session Management - Token refresh and validation
  • Password Management - Reset, update, strength validation
  • Email Verification - Verify user email addresses
  • Profile Management - Update user information
  • Custom Claims - Role-based access control
  • Offline Support - Cached authentication state

Developer Experience

  • Type-Safe Models - Using flutter_shared_utilities BaseDataModel
  • Comprehensive Error Handling - Detailed error models with user-friendly messages
  • Stream-Based State Management - Real-time authentication state updates
  • Dependency Injection Ready - Abstract interface for easy testing and mocking
  • Mixin-Based Architecture - Modular authentication utilities and validations
  • Analytics & Monitoring - Built-in authentication analytics with app_logger_manager
  • Debug Support - Comprehensive logging and debugging tools
  • Clean Architecture - Separation of concerns with interfaces and implementations

Installation πŸ“¦

Add this package to your pubspec.yaml:

dependencies:
  app_auth_manager: ^1.0.1

  # Required for Firebase Authentication
  firebase_core: ^3.14.0
  firebase_auth: ^5.6.0

  # Required for Social Authentication
  google_sign_in: ^6.3.0
  sign_in_with_apple: ^7.0.1

  # Required for Biometric Authentication (optional)
  local_auth: ^2.3.0

  # Required for Logging (optional but recommended)
  app_logger_manager: ^1.0.2

Quick Start πŸš€

1. Initialize Firebase

Follow the Firebase setup guide to add Firebase to your Flutter app.

2. Configure Authentication Providers

Google Sign-In Setup

Android Configuration:

  1. Add your SHA-1 fingerprint to Firebase Console
  2. Download and add google-services.json to android/app/
  3. Update android/build.gradle:
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.15'
    }
}
  1. Update android/app/build.gradle:
apply plugin: 'com.google.gms.google-services'

android {
    compileSdkVersion 34

    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 34
    }
}

iOS Configuration:

  1. Download and add GoogleService-Info.plist to ios/Runner/
  2. Update ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>REVERSED_CLIENT_ID</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>YOUR_REVERSED_CLIENT_ID</string>
        </array>
    </dict>
</array>

Apple Sign-In Setup

iOS Configuration:

  1. Enable Sign in with Apple in Apple Developer Console
  2. Add capability in Xcode: Signing & Capabilities β†’ + Capability β†’ Sign in with Apple
  3. Update ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>apple-sign-in</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>your.bundle.identifier</string>
        </array>
    </dict>
</array>

3. Basic Usage

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:app_auth_manager/app_auth_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  IAuthManager? _authManager;
  bool _isInitializing = true;

  @override
  void initState() {
    super.initState();
    _initializeAuth();
  }

  Future<void> _initializeAuth() async {
    _authManager = await FirebaseAuthManager.create();
    if (mounted) {
      setState(() {
        _isInitializing = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: _isInitializing || _authManager == null
          ? const Scaffold(
              body: Center(child: CircularProgressIndicator()),
            )
          : StreamBuilder<AuthStateModel>(
              stream: _authManager!.authStateStream,
              builder: (context, snapshot) {
                final authState = snapshot.data ?? AuthStateModel.initial();

                if (authState.isLoading) {
                  return const Scaffold(
                    body: Center(child: CircularProgressIndicator()),
                  );
                }

                if (authState.isAuthenticated) {
                  return HomePage(authManager: _authManager!);
                }

                return LoginPage(authManager: _authManager!);
              },
            ),
    );
  }
}

Authentication Methods πŸ”‘

Email/Password Authentication

// Sign up
final result = await authManager.signUpWithEmailAndPassword(
  email: 'user@example.com',
  password: 'securePassword123',
  displayName: 'John Doe',
);

// Sign in
final result = await authManager.signInWithEmailAndPassword(
  email: 'user@example.com',
  password: 'securePassword123',
  rememberMe: true,
);

// Handle result
if (result.success) {
  print('Welcome ${result.user?.displayName}!');
} else {
  print('Error: ${result.error}');
}

Social Authentication

// Google Sign-In
final googleResult = await authManager.signInWithGoogle();

// Apple Sign-In
final appleResult = await authManager.signInWithApple();

// Anonymous Sign-In
final anonymousResult = await authManager.signInAnonymously();

Password Management

// Send password reset email
await authManager.sendPasswordResetEmail(
  email: 'user@example.com',
);

// Update password
await authManager.updatePassword(
  currentPassword: 'oldPassword',
  newPassword: 'newSecurePassword123',
);

// Validate password strength
bool isStrong = authManager.validatePasswordStrength('myPassword');
int score = authManager.getPasswordStrengthScore('myPassword'); // 0-4

Profile Management

// Update user profile
await authManager.updateProfile(
  displayName: 'New Name',
  photoURL: 'https://example.com/photo.jpg',
);

// Update email
await authManager.updateEmail(
  newEmail: 'new@example.com',
  password: 'currentPassword',
);

Account Linking

// Link Google account
await authManager.linkWithGoogle();

// Link Apple account
await authManager.linkWithApple();

// Unlink provider
await authManager.unlinkFromProvider(providerId: 'google.com');

// Get linked providers
List<String> providers = authManager.getLinkedProviders();

State Management πŸ“Š

The authentication manager provides real-time state updates through streams:

StreamBuilder<AuthStateModel>(
  stream: authManager.authStateStream,
  builder: (context, snapshot) {
    final state = snapshot.data ?? AuthStateModel.initial();

    return switch (state.status) {
      AuthStatus.authenticated => AuthenticatedView(user: state.user!),
      AuthStatus.unauthenticated => LoginView(),
      AuthStatus.unknown => LoadingView(),
    };
  },
);

Authentication State Properties

final authState = authManager.currentState;

// Check authentication status
bool isAuthenticated = authState.isAuthenticated;
bool isLoading = authState.isLoading;
bool hasError = authState.hasError;

// Get current user
AuthUserModel? user = authState.user;

// Access user properties
if (user != null) {
  print('User ID: ${user.uid}');
  print('Email: ${user.email}');
  print('Display Name: ${user.displayName}');
  print('Email Verified: ${user.isEmailVerified}');
  print('Providers: ${user.allProviderIds}');
}

Error Handling 🚨

The package provides comprehensive error handling with user-friendly messages:

final result = await authManager.signInWithEmailAndPassword(
  email: email,
  password: password,
);

if (result.isFailure) {
  final error = result.error;
  final userMessage = authManager.getUserFriendlyErrorMessage(result.errorCode);

  // Show user-friendly message
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(content: Text(userMessage)),
  );

  // Log technical details
  print('Technical error: $error');
  print('Error code: ${result.errorCode}');
  print('Retryable: ${authManager.isErrorRetryable(result.errorCode)}');
}

Common Error Codes

Error Code User-Friendly Message Retryable
user-not-found No account found with this email address. βœ…
wrong-password Incorrect password. Please try again. βœ…
invalid-email Please enter a valid email address. βœ…
user-disabled This account has been disabled. ❌
too-many-requests Too many failed attempts. Please try again later. βœ…
email-already-in-use An account with this email already exists. ❌
weak-password Please choose a stronger password. βœ…
network-request-failed Network connection error. βœ…

Advanced Features πŸ”§

Session Management

// Set session timeout
authManager.setSessionTimeout(Duration(hours: 2));

// Check session status
bool isValid = await authManager.validateSession();
Duration? remaining = authManager.getRemainingSessionTime();
bool nearExpiry = authManager.isSessionNearExpiry();

// Refresh tokens
await authManager.refreshToken();
String? token = await authManager.getIdToken(forceRefresh: true);

Biometric Authentication

// Check if biometric auth is available
bool available = await authManager.isBiometricAvailable();

if (available) {
  // Enable biometric auth
  await authManager.enableBiometricAuth();

  // Authenticate with biometric
  final result = await authManager.authenticateWithBiometric(
    localizedFallbackTitle: 'Use Passcode',
    cancelButtonText: 'Cancel',
  );
}

Analytics and Monitoring

// Get authentication statistics
Map<String, Object?> stats = authManager.getAuthStats();
print('Sign-in attempts: ${stats['signIn_attempts']}');
print('Success rate: ${stats['success_rate']}');

// Reset statistics
authManager.resetAuthStats();

// Enable debug logging
authManager.enableDebugLogging(true);

// Get debug information
Map<String, Object?> debugInfo = authManager.getDebugInfo();

Configuration

// Configure authentication settings
authManager.configure(
  sessionTimeout: Duration(hours: 24),
  enableBiometric: true,
  enableRememberMe: true,
  enabledProviders: ['google.com', 'apple.com', 'password'],
  providerConfigs: {
    'google': {'scopes': ['email', 'profile']},
    'apple': {'scopes': ['email', 'name']},
  },
);

// Get current configuration
Map<String, Object?> config = authManager.getConfiguration();

// Reset to defaults
authManager.resetConfiguration();

Testing πŸ§ͺ

The package is designed with testing in mind:

// Create mock auth manager for testing
class MockAuthManager implements IAuthManager {
  // Implement interface methods for testing
}

// Use in tests
testWidgets('Login page test', (tester) async {
  final mockAuthManager = MockAuthManager();

  await tester.pumpWidget(
    MaterialApp(
      home: LoginPage(authManager: mockAuthManager),
    ),
  );

  // Test authentication flow
});

Best Practices πŸ“‹

1. Proper Initialization

class _MyAppState extends State<MyApp> {
  IAuthManager? _authManager;
  bool _isInitializing = true;

  @override
  void initState() {
    super.initState();
    _initializeAuth();
  }

  Future<void> _initializeAuth() async {
    _authManager = await FirebaseAuthManager.create();
    if (mounted) {
      setState(() {
        _isInitializing = false;
      });
    }
  }

  @override
  void dispose() {
    _authManager?.dispose(); // Important: dispose resources
    super.dispose();
  }
}

2. Error Handling

Future<void> _handleAuthOperation(Future<AuthResultModel> operation) async {
  try {
    final result = await operation;

    if (result.success) {
      // Handle success
      _showSuccess('Operation completed successfully');
    } else {
      // Handle failure with user-friendly message
      final userMessage = authManager.getUserFriendlyErrorMessage(result.errorCode);
      _showError(userMessage);
    }
  } catch (e) {
    // Handle unexpected errors
    _showError('An unexpected error occurred');
  }
}

3. State Management

// Use StreamBuilder for reactive UI
StreamBuilder<AuthStateModel>(
  stream: authManager.authStateStream,
  builder: (context, snapshot) {
    final state = snapshot.data ?? AuthStateModel.initial();

    if (state.isLoading) {
      return LoadingWidget();
    }

    if (state.hasError) {
      return ErrorWidget(error: state.error);
    }

    return state.isAuthenticated
      ? AuthenticatedView(user: state.user!)
      : UnauthenticatedView();
  },
);

4. Security Considerations

// Always validate inputs
if (!authManager.validateEmail(email)) {
  return _showError('Please enter a valid email address');
}

// Use strong password validation
if (!authManager.validatePasswordStrength(password)) {
  return _showError('Password does not meet security requirements');
}

// Enable session timeout for security
authManager.setSessionTimeout(Duration(hours: 1));

// Use biometric authentication when available
if (await authManager.isBiometricAvailable()) {
  await authManager.enableBiometricAuth();
}

Migration Guide πŸ“‹

From firebase_auth package

If you're migrating from the firebase_auth package:

// Before (firebase_auth)
final userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(
  email: email,
  password: password,
);
final user = userCredential.user;

// After (app_auth_manager)
final result = await authManager.signInWithEmailAndPassword(
  email: email,
  password: password,
);
if (result.success) {
  final user = result.user;
}

Troubleshooting πŸ”§

Common Issues

  1. Firebase not initialized

    // Ensure Firebase is initialized before using auth manager
    await Firebase.initializeApp();
    
  2. Google Sign-In not working

    • Check SHA-1 fingerprint in Firebase Console
    • Verify google-services.json is in correct location
    • Ensure Google Sign-In is enabled in Firebase Console
  3. Apple Sign-In not working

    • Enable Sign in with Apple in Apple Developer Console
    • Add Sign in with Apple capability in Xcode
    • Verify bundle identifier matches
  4. State not updating

    // Ensure you're listening to the auth state stream
    StreamBuilder<AuthStateModel>(
      stream: authManager.authStateStream,
      builder: (context, snapshot) {
        // Handle state changes
      },
    );
    

Contributing 🀝

Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.

License πŸ“„

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog πŸ“

See CHANGELOG.md for a detailed list of changes and version history.

Support πŸ’¬

Roadmap πŸ—ΊοΈ

  • Facebook Sign-In implementation
  • Twitter Sign-In implementation
  • Microsoft Sign-In implementation
  • GitHub Sign-In implementation
  • Phone authentication implementation
  • Email verification implementation
  • Password reset implementation
  • Multi-factor authentication
  • Enterprise SSO support
  • Offline authentication caching
  • Advanced analytics and monitoring
  • Custom authentication flows

Made with ❀️ by Bahrican Yesil

Libraries

app_auth_manager
A comprehensive Flutter app authentication manager that provides authentication support with Firebase Auth, Google Sign-In, Apple Sign-In, and other authentication methods.