Firebase Auth (Dart/Flutter)
Authentication Library
Firebase Auth (Dart/Flutter)
Overview
Firebase Auth for Flutter is Google's authentication SDK for Flutter applications from the Firebase platform. As of 2025, it serves as the core of the FlutterFire ecosystem, providing unified authentication experiences for iOS, Android, Web, and desktop applications. It supports a rich variety of authentication methods including email/password authentication, OAuth (Google, Facebook, Apple, etc.), anonymous authentication, and phone number authentication, providing enterprise-level user management capabilities through integration with Firebase Console.
Details
Firebase Auth for Flutter is the definitive cross-platform authentication solution. Key features include:
- Multi-platform Support: Unified API across iOS, Android, Web, macOS, Windows, and Linux
- Rich Authentication Methods: Email/password, OAuth, anonymous, phone number, and custom token authentication
- Real-time State Management: authStateChanges, userChanges, and idTokenChanges streams
- Security Features: Multi-factor authentication, email verification, and password reset functionality
- Offline Support: Automatic authentication state persistence and offline capabilities
- Firebase Integration: Complete integration with Firestore, Cloud Functions, Analytics, and more
Pros and Cons
Pros
- High reliability with enterprise-grade authentication infrastructure provided by Google
- Excellent code reusability through cross-platform support
- Comprehensive user management and analytics via Firebase Console
- Real-time authentication state management with reactive UI updates
- Easy integration with rich OAuth providers
- Automatic token management and refresh functionality
Cons
- Vendor lock-in due to dependency on Firebase ecosystem
- Constraints in implementing custom authentication logic
- Firebase usage fees apply (free tier available)
- Difficulty handling complex enterprise authentication requirements
- Some features require Google account or internet connection
Reference Pages
- Firebase Auth Flutter Documentation
- GitHub - firebase/flutterfire
- Firebase Console
- FlutterFire API Reference
Code Examples
Basic Setup and Installation
# Install Firebase CLI
npm install -g firebase-tools
# Login to Firebase
firebase login
# Configure Firebase in Flutter project
flutter pub add firebase_core
flutter pub add firebase_auth
# Configure with FlutterFire CLI
dart pub global activate flutterfire_cli
flutterfire configure
# Rebuild project
flutter run
Firebase Initialization and Email/Password Authentication
// main.dart - Firebase initialization
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// Use emulator for development environment
if (kDebugMode) {
await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase Auth Demo',
home: AuthWrapper(),
);
}
}
// auth_wrapper.dart - Screen switching based on authentication state
class AuthWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
// Check authentication state
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(child: CircularProgressIndicator()),
);
} else if (snapshot.hasData && snapshot.data != null) {
// Logged in
return HomePage();
} else {
// Not logged in
return LoginPage();
}
},
);
}
}
// login_page.dart - Login page
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
// User registration with email and password
Future<void> _registerWithEmailPassword() async {
if (!_formKey.currentState!.validate()) return;
setState(() => _isLoading = true);
try {
final credential = await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text,
);
// Send email verification after registration
await credential.user?.sendEmailVerification();
_showMessage('Registration successful! Please check your verification email.');
} on FirebaseAuthException catch (e) {
String message;
switch (e.code) {
case 'weak-password':
message = 'The password is too weak.';
break;
case 'email-already-in-use':
message = 'This email address is already in use.';
break;
case 'invalid-email':
message = 'Invalid email address.';
break;
default:
message = 'Registration failed: ${e.message}';
}
_showMessage(message);
} catch (e) {
_showMessage('Unexpected error occurred: $e');
} finally {
setState(() => _isLoading = false);
}
}
// Sign in with email and password
Future<void> _signInWithEmailPassword() async {
if (!_formKey.currentState!.validate()) return;
setState(() => _isLoading = true);
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text,
);
} on FirebaseAuthException catch (e) {
String message;
switch (e.code) {
case 'user-not-found':
message = 'User not found.';
break;
case 'wrong-password':
message = 'Incorrect password.';
break;
case 'invalid-email':
message = 'Invalid email address.';
break;
case 'user-disabled':
message = 'This account has been disabled.';
break;
default:
message = 'Login failed: ${e.message}';
}
_showMessage(message);
} finally {
setState(() => _isLoading = false);
}
}
void _showMessage(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email Address',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter email address';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$')
.hasMatch(value)) {
return 'Please enter a valid email address';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter password';
}
if (value.length < 6) {
return 'Password must be at least 6 characters';
}
return null;
},
),
SizedBox(height: 24),
if (_isLoading)
CircularProgressIndicator()
else ...[
ElevatedButton(
onPressed: _signInWithEmailPassword,
style: ElevatedButton.styleFrom(
minimumSize: Size(double.infinity, 50),
),
child: Text('Sign In'),
),
SizedBox(height: 16),
TextButton(
onPressed: _registerWithEmailPassword,
child: Text('Register'),
),
TextButton(
onPressed: _resetPassword,
child: Text('Forgot Password'),
),
],
],
),
),
),
);
}
// Password reset
Future<void> _resetPassword() async {
final email = _emailController.text.trim();
if (email.isEmpty) {
_showMessage('Please enter email address');
return;
}
try {
await FirebaseAuth.instance.sendPasswordResetEmail(email: email);
_showMessage('Password reset email sent');
} on FirebaseAuthException catch (e) {
_showMessage('Error: ${e.message}');
}
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
}
OAuth Authentication (Google, Apple, etc.) Implementation
// oauth_service.dart - OAuth authentication service
import 'package:google_sign_in/google_sign_in.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
class OAuthService {
// Google Sign In
static Future<UserCredential?> signInWithGoogle() async {
try {
if (kIsWeb) {
// Web Google authentication
GoogleAuthProvider googleProvider = GoogleAuthProvider();
googleProvider.addScope('https://www.googleapis.com/auth/contacts.readonly');
googleProvider.setCustomParameters({
'login_hint': '[email protected]'
});
return await FirebaseAuth.instance.signInWithPopup(googleProvider);
} else {
// Native Google authentication
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
if (googleUser == null) return null;
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
return await FirebaseAuth.instance.signInWithCredential(credential);
}
} catch (e) {
print('Google sign-in error: $e');
return null;
}
}
// Facebook Sign In
static Future<UserCredential?> signInWithFacebook() async {
try {
if (kIsWeb) {
// Web Facebook authentication
FacebookAuthProvider facebookProvider = FacebookAuthProvider();
facebookProvider.addScope('email');
facebookProvider.setCustomParameters({'display': 'popup'});
return await FirebaseAuth.instance.signInWithPopup(facebookProvider);
} else {
// Native Facebook authentication
final LoginResult loginResult = await FacebookAuth.instance.login();
if (loginResult.status != LoginStatus.success) return null;
final OAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(loginResult.accessToken!.token);
return await FirebaseAuth.instance
.signInWithCredential(facebookAuthCredential);
}
} catch (e) {
print('Facebook sign-in error: $e');
return null;
}
}
// Apple Sign In
static Future<UserCredential?> signInWithApple() async {
try {
final appleProvider = AppleAuthProvider();
appleProvider.addScope('email');
appleProvider.addScope('name');
if (kIsWeb) {
return await FirebaseAuth.instance.signInWithPopup(appleProvider);
} else {
return await FirebaseAuth.instance.signInWithProvider(appleProvider);
}
} catch (e) {
print('Apple sign-in error: $e');
return null;
}
}
// Anonymous Sign In
static Future<UserCredential?> signInAnonymously() async {
try {
return await FirebaseAuth.instance.signInAnonymously();
} on FirebaseAuthException catch (e) {
switch (e.code) {
case "operation-not-allowed":
print("Anonymous authentication is not enabled");
break;
default:
print("Anonymous authentication error: ${e.message}");
}
return null;
}
}
// Sign Out
static Future<void> signOut() async {
await GoogleSignIn().signOut();
await FacebookAuth.instance.logOut();
await FirebaseAuth.instance.signOut();
}
}
// oauth_buttons.dart - OAuth authentication button widget
class OAuthButtons extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// Google Sign In Button
ElevatedButton.icon(
onPressed: () async {
final result = await OAuthService.signInWithGoogle();
if (result != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Signed in with Google')),
);
}
},
icon: Icon(Icons.login),
label: Text('Sign in with Google'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
minimumSize: Size(double.infinity, 50),
),
),
SizedBox(height: 12),
// Facebook Sign In Button
ElevatedButton.icon(
onPressed: () async {
final result = await OAuthService.signInWithFacebook();
if (result != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Signed in with Facebook')),
);
}
},
icon: Icon(Icons.facebook),
label: Text('Sign in with Facebook'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue[800],
foregroundColor: Colors.white,
minimumSize: Size(double.infinity, 50),
),
),
SizedBox(height: 12),
// Apple Sign In Button (iOS/Web)
if (Theme.of(context).platform == TargetPlatform.iOS || kIsWeb)
ElevatedButton.icon(
onPressed: () async {
final result = await OAuthService.signInWithApple();
if (result != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Signed in with Apple')),
);
}
},
icon: Icon(Icons.apple),
label: Text('Sign in with Apple'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.black,
foregroundColor: Colors.white,
minimumSize: Size(double.infinity, 50),
),
),
SizedBox(height: 12),
// Anonymous Sign In Button
TextButton.icon(
onPressed: () async {
final result = await OAuthService.signInAnonymously();
if (result != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Signed in anonymously')),
);
}
},
icon: Icon(Icons.person_outline),
label: Text('Sign in Anonymously'),
),
],
);
}
}
User Information Management and Profile Updates
// user_profile_page.dart - User profile management
class UserProfilePage extends StatefulWidget {
@override
_UserProfilePageState createState() => _UserProfilePageState();
}
class _UserProfilePageState extends State<UserProfilePage> {
final _displayNameController = TextEditingController();
final _currentPasswordController = TextEditingController();
final _newPasswordController = TextEditingController();
bool _isLoading = false;
@override
void initState() {
super.initState();
_loadUserData();
}
void _loadUserData() {
final user = FirebaseAuth.instance.currentUser;
if (user != null) {
_displayNameController.text = user.displayName ?? '';
}
}
// Update profile
Future<void> _updateProfile() async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
setState(() => _isLoading = true);
try {
await user.updateDisplayName(_displayNameController.text);
await user.reload();
_showMessage('Profile updated successfully');
} catch (e) {
_showMessage('Update failed: $e');
} finally {
setState(() => _isLoading = false);
}
}
// Change password
Future<void> _changePassword() async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
if (_currentPasswordController.text.isEmpty ||
_newPasswordController.text.isEmpty) {
_showMessage('Please enter current password and new password');
return;
}
setState(() => _isLoading = true);
try {
// Re-authenticate
final credential = EmailAuthProvider.credential(
email: user.email!,
password: _currentPasswordController.text,
);
await user.reauthenticateWithCredential(credential);
// Update password
await user.updatePassword(_newPasswordController.text);
_currentPasswordController.clear();
_newPasswordController.clear();
_showMessage('Password changed successfully');
} on FirebaseAuthException catch (e) {
String message;
switch (e.code) {
case 'wrong-password':
message = 'Current password is incorrect';
break;
case 'weak-password':
message = 'New password is too weak';
break;
default:
message = 'Password change failed: ${e.message}';
}
_showMessage(message);
} finally {
setState(() => _isLoading = false);
}
}
// Send email verification
Future<void> _sendEmailVerification() async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
try {
await user.sendEmailVerification();
_showMessage('Verification email sent');
} catch (e) {
_showMessage('Send failed: $e');
}
}
// Delete account
Future<void> _deleteAccount() async {
final confirmed = await _showConfirmDialog(
'Delete Account',
'Deleting your account will permanently remove all your data.\nAre you sure you want to continue?',
);
if (!confirmed) return;
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
try {
await user.delete();
_showMessage('Account deleted successfully');
} on FirebaseAuthException catch (e) {
if (e.code == 'requires-recent-login') {
_showMessage('For security, please re-login and try again');
} else {
_showMessage('Delete failed: ${e.message}');
}
}
}
Future<bool> _showConfirmDialog(String title, String message) async {
return await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('Delete'),
style: TextButton.styleFrom(foregroundColor: Colors.red),
),
],
),
) ?? false;
}
void _showMessage(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
return Scaffold(
appBar: AppBar(
title: Text('Profile'),
actions: [
IconButton(
onPressed: () async {
await OAuthService.signOut();
},
icon: Icon(Icons.logout),
),
],
),
body: user == null
? Center(child: Text('User not found'))
: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// User information display
Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Basic Information', style: Theme.of(context).textTheme.headlineSmall),
SizedBox(height: 16),
if (user.photoURL != null)
CircleAvatar(
radius: 40,
backgroundImage: NetworkImage(user.photoURL!),
),
SizedBox(height: 16),
Text('Email: ${user.email ?? "Not set"}'),
SizedBox(height: 8),
Text('UID: ${user.uid}'),
SizedBox(height: 8),
Row(
children: [
Text('Email Verified: '),
Icon(
user.emailVerified ? Icons.check_circle : Icons.cancel,
color: user.emailVerified ? Colors.green : Colors.red,
),
if (!user.emailVerified) ...[
SizedBox(width: 8),
TextButton(
onPressed: _sendEmailVerification,
child: Text('Send Verification Email'),
),
],
],
),
SizedBox(height: 16),
TextField(
controller: _displayNameController,
decoration: InputDecoration(
labelText: 'Display Name',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _isLoading ? null : _updateProfile,
child: _isLoading
? CircularProgressIndicator()
: Text('Update Profile'),
),
],
),
),
),
SizedBox(height: 16),
// Password change section
if (user.providerData.any((info) => info.providerId == 'password'))
Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Change Password',
style: Theme.of(context).textTheme.headlineSmall),
SizedBox(height: 16),
TextField(
controller: _currentPasswordController,
decoration: InputDecoration(
labelText: 'Current Password',
border: OutlineInputBorder(),
),
obscureText: true,
),
SizedBox(height: 16),
TextField(
controller: _newPasswordController,
decoration: InputDecoration(
labelText: 'New Password',
border: OutlineInputBorder(),
),
obscureText: true,
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _isLoading ? null : _changePassword,
child: Text('Change Password'),
),
],
),
),
),
SizedBox(height: 16),
// Account deletion section
Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Dangerous Actions',
style: Theme.of(context).textTheme.headlineSmall),
SizedBox(height: 16),
ElevatedButton(
onPressed: _deleteAccount,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: Text('Delete Account'),
),
],
),
),
),
],
),
),
);
}
@override
void dispose() {
_displayNameController.dispose();
_currentPasswordController.dispose();
_newPasswordController.dispose();
super.dispose();
}
}
Authentication State Management and Advanced Features
// auth_state_service.dart - Authentication state management service
class AuthStateService {
static final FirebaseAuth _auth = FirebaseAuth.instance;
// Get current user
static User? get currentUser => _auth.currentUser;
// Authentication state change stream
static Stream<User?> get authStateChanges => _auth.authStateChanges();
// User change stream (includes profile changes)
static Stream<User?> get userChanges => _auth.userChanges();
// ID token change stream
static Stream<User?> get idTokenChanges => _auth.idTokenChanges();
// Set persistence (Web only)
static Future<void> setPersistence(Persistence persistence) async {
if (kIsWeb) {
await _auth.setPersistence(persistence);
}
}
// Get current user details
static Future<User?> reloadCurrentUser() async {
final user = _auth.currentUser;
if (user != null) {
await user.reload();
return _auth.currentUser;
}
return null;
}
// Get ID token
static Future<String?> getIdToken({bool forceRefresh = false}) async {
final user = _auth.currentUser;
if (user != null) {
return await user.getIdToken(forceRefresh);
}
return null;
}
// Get custom claims
static Future<Map<String, dynamic>?> getCustomClaims() async {
final user = _auth.currentUser;
if (user != null) {
final idTokenResult = await user.getIdTokenResult();
return idTokenResult.claims;
}
return null;
}
}
// auth_guard.dart - Authentication guard widget
class AuthGuard extends StatelessWidget {
final Widget child;
final Widget? loginPage;
final bool requireEmailVerification;
const AuthGuard({
Key? key,
required this.child,
this.loginPage,
this.requireEmailVerification = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: AuthStateService.authStateChanges,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
final user = snapshot.data;
// User does not exist
if (user == null) {
return loginPage ?? LoginPage();
}
// Email verification required
if (requireEmailVerification && !user.emailVerified) {
return EmailVerificationPage();
}
// Authenticated user
return child;
},
);
}
}
// email_verification_page.dart - Email verification confirmation page
class EmailVerificationPage extends StatefulWidget {
@override
_EmailVerificationPageState createState() => _EmailVerificationPageState();
}
class _EmailVerificationPageState extends State<EmailVerificationPage> {
bool _isLoading = false;
Timer? _timer;
@override
void initState() {
super.initState();
_checkEmailVerification();
}
void _checkEmailVerification() {
_timer = Timer.periodic(Duration(seconds: 3), (timer) async {
await FirebaseAuth.instance.currentUser?.reload();
final user = FirebaseAuth.instance.currentUser;
if (user?.emailVerified == true) {
timer.cancel();
// Update page after verification confirmation
if (mounted) {
setState(() {});
}
}
});
}
Future<void> _resendVerificationEmail() async {
setState(() => _isLoading = true);
try {
await FirebaseAuth.instance.currentUser?.sendEmailVerification();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Verification email resent')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Send failed: $e')),
);
} finally {
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
return Scaffold(
appBar: AppBar(
title: Text('Email Verification'),
actions: [
IconButton(
onPressed: () => FirebaseAuth.instance.signOut(),
icon: Icon(Icons.logout),
),
],
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.mark_email_unread,
size: 100,
color: Colors.orange,
),
SizedBox(height: 24),
Text(
'Email Verification Required',
style: Theme.of(context).textTheme.headlineSmall,
textAlign: TextAlign.center,
),
SizedBox(height: 16),
Text(
'Please click the verification link sent to ${user?.email}.',
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
),
SizedBox(height: 32),
ElevatedButton(
onPressed: _isLoading ? null : _resendVerificationEmail,
child: _isLoading
? CircularProgressIndicator()
: Text('Resend Verification Email'),
),
SizedBox(height: 16),
TextButton(
onPressed: () async {
await FirebaseAuth.instance.currentUser?.reload();
setState(() {});
},
child: Text('Check Verification Status'),
),
],
),
),
);
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
}