Skip to main content

2 posts tagged with "Authentication"

View All Tags

Flutter với Firebase

· 3 min read

Cài đặt và Cấu hình

Thêm Dependencies

dependencies:
firebase_core: ^2.24.2
firebase_auth: ^4.15.3
cloud_firestore: ^4.13.6
firebase_storage: ^11.5.6

Khởi tạo Firebase

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(MyApp());
}

Authentication

Đăng ký người dùng

Future<UserCredential> signUpWithEmail(String email, String password) async {
try {
return await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
throw _handleAuthError(e);
}
}

Đăng nhập

Future<UserCredential> signInWithEmail(String email, String password) async {
try {
return await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
throw _handleAuthError(e);
}
}

Cloud Firestore

CRUD Operations

// Thêm dữ liệu
Future<void> addUser(String userId, Map<String, dynamic> userData) async {
await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.set(userData);
}

// Đọc dữ liệu
Future<DocumentSnapshot> getUser(String userId) async {
return await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.get();
}

// Cập nhật dữ liệu
Future<void> updateUser(String userId, Map<String, dynamic> newData) async {
await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.update(newData);
}

// Xóa dữ liệu
Future<void> deleteUser(String userId) async {
await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.delete();
}

Realtime Updates

Stream<QuerySnapshot> getUsersStream() {
return FirebaseFirestore.instance
.collection('users')
.snapshots();
}

// Sử dụng trong Widget
StreamBuilder<QuerySnapshot>(
stream: getUsersStream(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Có lỗi xảy ra');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
return ListView(
children: snapshot.data!.docs.map((doc) {
return ListTile(
title: Text(doc['name']),
);
}).toList(),
);
},
)

Cloud Storage

Upload Files

Future<String> uploadFile(File file, String path) async {
try {
final ref = FirebaseStorage.instance.ref().child(path);
final uploadTask = ref.putFile(file);
final snapshot = await uploadTask.whenComplete(() {});
return await snapshot.ref.getDownloadURL();
} catch (e) {
throw Exception('Lỗi khi upload file: $e');
}
}

Download Files

Future<void> downloadFile(String url, String localPath) async {
try {
final ref = FirebaseStorage.instance.refFromURL(url);
final file = File(localPath);
await ref.writeToFile(file);
} catch (e) {
throw Exception('Lỗi khi download file: $e');
}
}

Security Rules

Firestore Rules

rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth != null;
allow write: if request.auth.uid == userId;
}
}
}

Storage Rules

rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read: if request.auth != null;
allow write: if request.auth != null;
}
}
}

Best Practices

Authentication

  • Luôn xử lý lỗi authentication
  • Sử dụng các phương thức bảo mật như email verification
  • Implement proper session management

Database

  • Cấu trúc dữ liệu phù hợp
  • Sử dụng indexes cho queries phức tạp
  • Implement caching cho offline support

Storage

  • Validate file size và type trước khi upload
  • Implement progress monitoring cho large files
  • Sử dụng compression khi cần thiết

Tài Liệu Tham Khảo

Hướng Dẫn Firebase Authentication Cho Người Mới

· 11 min read

Firebase Authentication là một dịch vụ xác thực người dùng mạnh mẽ và dễ sử dụng. Bài viết này sẽ hướng dẫn bạn cách tích hợp Firebase Authentication vào Flutter từ đầu đến cuối.


1️⃣ Cài Đặt và Setup

1.1 Tạo Firebase Project

  1. Truy cập Firebase Console
  2. Click "Add project"
  3. Điền tên project và làm theo hướng dẫn
  4. Bật Authentication trong Firebase Console

1.2 Cài Đặt FlutterFire CLI

dart pub global activate flutterfire_cli

1.3 Cấu Hình Firebase cho Flutter

flutterfire configure

Lệnh này sẽ:

  • Tự động tạo file firebase_options.dart
  • Cấu hình cho cả Android và iOS

1.4 Thêm Dependencies

Thêm vào pubspec.yaml:

dependencies:
firebase_core: ^2.24.2
firebase_auth: ^4.15.3
google_sign_in: ^6.1.6 # Cho Google Sign In

Sau đó chạy:

flutter pub get

1.5 Khởi Tạo Firebase

import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(MyApp());
}

2️⃣ Email/Password Authentication

2.1 Đăng Ký (Sign Up)

import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;

// Đăng ký với email và password
Future<UserCredential?> signUpWithEmail({
required String email,
required String password,
}) async {
try {
final userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);

// Gửi email xác nhận (optional)
await userCredential.user?.sendEmailVerification();

return userCredential;
} on FirebaseAuthException catch (e) {
print('Sign up error: ${e.message}');
throw _handleAuthException(e);
} catch (e) {
print('Unexpected error: $e');
throw Exception('Đã xảy ra lỗi không xác định');
}
}

String _handleAuthException(FirebaseAuthException e) {
switch (e.code) {
case 'weak-password':
return 'Mật khẩu quá yếu';
case 'email-already-in-use':
return 'Email đã được sử dụng';
case 'invalid-email':
return 'Email không hợp lệ';
default:
return e.message ?? 'Đã xảy ra lỗi';
}
}
}

2.2 Đăng Nhập (Sign In)

// Đăng nhập với email và password
Future<UserCredential?> signInWithEmail({
required String email,
required String password,
}) async {
try {
final userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);

return userCredential;
} on FirebaseAuthException catch (e) {
print('Sign in error: ${e.message}');
throw _handleSignInException(e);
} catch (e) {
print('Unexpected error: $e');
throw Exception('Đã xảy ra lỗi không xác định');
}
}

String _handleSignInException(FirebaseAuthException e) {
switch (e.code) {
case 'user-not-found':
return 'Không tìm thấy tài khoản với email này';
case 'wrong-password':
return 'Mật khẩu không đúng';
case 'invalid-email':
return 'Email không hợp lệ';
case 'user-disabled':
return 'Tài khoản đã bị vô hiệu hóa';
default:
return e.message ?? 'Đã xảy ra lỗi';
}
}

2.3 Đăng Xuất (Sign Out)

Future<void> signOut() async {
try {
await _auth.signOut();
} catch (e) {
print('Sign out error: $e');
throw Exception('Không thể đăng xuất');
}
}

2.4 Reset Password

Future<void> resetPassword(String email) async {
try {
await _auth.sendPasswordResetEmail(email: email);
} on FirebaseAuthException catch (e) {
throw _handleAuthException(e);
} catch (e) {
throw Exception('Không thể gửi email reset password');
}
}

3️⃣ Google Sign In

3.1 Cấu Hình Google Sign In

Android

Thêm vào android/app/build.gradle:

dependencies {
implementation 'com.google.android.gms:play-services-auth:20.7.0'
}

Lấy SHA-1 key:

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

Thêm SHA-1 vào Firebase Console > Project Settings > Your apps

iOS

Thêm vào ios/Runner/Info.plist:

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>YOUR_REVERSED_CLIENT_ID</string>
</array>
</dict>
</array>

3.2 Implement Google Sign In

import 'package:google_sign_in/google_sign_in.dart';

class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = GoogleSignIn();

// Đăng nhập với Google
Future<UserCredential?> signInWithGoogle() async {
try {
// Trigger the authentication flow
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();

if (googleUser == null) {
// User canceled the sign-in
return null;
}

// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;

// Create a new credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);

// Sign in to Firebase with the Google credential
return await _auth.signInWithCredential(credential);
} on FirebaseAuthException catch (e) {
print('Google sign in error: ${e.message}');
throw Exception('Đăng nhập Google thất bại: ${e.message}');
} catch (e) {
print('Unexpected error: $e');
throw Exception('Đã xảy ra lỗi không xác định');
}
}

// Đăng xuất Google
Future<void> signOutGoogle() async {
await _googleSignIn.signOut();
await _auth.signOut();
}
}

4️⃣ Phone Authentication

4.1 Cấu Hình Phone Auth

  1. Bật Phone Authentication trong Firebase Console
  2. Thêm test phone numbers (cho development)

4.2 Implement Phone Auth

class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
String? _verificationId;

// Gửi OTP
Future<void> sendOTP(String phoneNumber) async {
try {
await _auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
verificationCompleted: (PhoneAuthCredential credential) async {
// Auto verification (Android only)
await _auth.signInWithCredential(credential);
},
verificationFailed: (FirebaseAuthException e) {
throw Exception('Xác thực thất bại: ${e.message}');
},
codeSent: (String verificationId, int? resendToken) {
_verificationId = verificationId;
},
codeAutoRetrievalTimeout: (String verificationId) {
_verificationId = verificationId;
},
timeout: Duration(seconds: 60),
);
} catch (e) {
throw Exception('Không thể gửi OTP: $e');
}
}

// Xác thực OTP
Future<UserCredential?> verifyOTP(String smsCode) async {
try {
if (_verificationId == null) {
throw Exception('Verification ID không tồn tại');
}

final credential = PhoneAuthProvider.credential(
verificationId: _verificationId!,
smsCode: smsCode,
);

return await _auth.signInWithCredential(credential);
} on FirebaseAuthException catch (e) {
throw Exception('Mã OTP không đúng: ${e.message}');
} catch (e) {
throw Exception('Xác thực thất bại: $e');
}
}
}

5️⃣ Quản Lý User Session

5.1 Auth State Stream

class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;

// Stream để theo dõi trạng thái đăng nhập
Stream<User?> get authStateChanges => _auth.authStateChanges();

// User hiện tại
User? get currentUser => _auth.currentUser;

// Kiểm tra đăng nhập
bool get isSignedIn => _auth.currentUser != null;

// Lấy thông tin user
User? get user => _auth.currentUser;
}

5.2 Auth Wrapper Widget

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class AuthWrapper extends StatelessWidget {
final Widget signedInWidget;
final Widget signedOutWidget;

const AuthWrapper({
Key? key,
required this.signedInWidget,
required this.signedOutWidget,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}

if (snapshot.hasData) {
return signedInWidget;
} else {
return signedOutWidget;
}
},
);
}
}

// Sử dụng
void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: AuthWrapper(
signedInWidget: HomeScreen(),
signedOutWidget: LoginScreen(),
),
);
}
}

6️⃣ User Profile Management

6.1 Cập Nhật Thông Tin User

class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;

// Cập nhật display name
Future<void> updateDisplayName(String name) async {
try {
await _auth.currentUser?.updateDisplayName(name);
await _auth.currentUser?.reload();
} catch (e) {
throw Exception('Không thể cập nhật tên: $e');
}
}

// Cập nhật email
Future<void> updateEmail(String newEmail) async {
try {
await _auth.currentUser?.verifyBeforeUpdateEmail(newEmail);
} catch (e) {
throw Exception('Không thể cập nhật email: $e');
}
}

// Cập nhật password
Future<void> updatePassword(String newPassword) async {
try {
await _auth.currentUser?.updatePassword(newPassword);
} catch (e) {
throw Exception('Không thể cập nhật mật khẩu: $e');
}
}

// Cập nhật photo URL
Future<void> updatePhotoURL(String photoURL) async {
try {
await _auth.currentUser?.updatePhotoURL(photoURL);
await _auth.currentUser?.reload();
} catch (e) {
throw Exception('Không thể cập nhật ảnh đại diện: $e');
}
}

// Xóa tài khoản
Future<void> deleteAccount() async {
try {
await _auth.currentUser?.delete();
} catch (e) {
throw Exception('Không thể xóa tài khoản: $e');
}
}
}

6.2 Email Verification

// Gửi email xác nhận
Future<void> sendEmailVerification() async {
try {
await _auth.currentUser?.sendEmailVerification();
} catch (e) {
throw Exception('Không thể gửi email xác nhận: $e');
}
}

// Kiểm tra email đã xác nhận chưa
bool get isEmailVerified => _auth.currentUser?.emailVerified ?? false;

// Reload user để cập nhật trạng thái
Future<void> reloadUser() async {
await _auth.currentUser?.reload();
}

7️⃣ Ví Dụ Hoàn Chỉnh: Auth Service

import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = GoogleSignIn();
String? _verificationId;

// Stream
Stream<User?> get authStateChanges => _auth.authStateChanges();
User? get currentUser => _auth.currentUser;
bool get isSignedIn => _auth.currentUser != null;

// Email/Password
Future<UserCredential?> signUpWithEmail({
required String email,
required String password,
}) async {
try {
final userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
await userCredential.user?.sendEmailVerification();
return userCredential;
} on FirebaseAuthException catch (e) {
throw _handleAuthException(e);
}
}

Future<UserCredential?> signInWithEmail({
required String email,
required String password,
}) async {
try {
return await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
throw _handleAuthException(e);
}
}

// Google Sign In
Future<UserCredential?> signInWithGoogle() async {
try {
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 _auth.signInWithCredential(credential);
} catch (e) {
throw Exception('Google sign in failed: $e');
}
}

// Phone Auth
Future<void> sendOTP(String phoneNumber) async {
await _auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
verificationCompleted: (credential) async {
await _auth.signInWithCredential(credential);
},
verificationFailed: (e) {
throw Exception('Verification failed: ${e.message}');
},
codeSent: (verificationId, resendToken) {
_verificationId = verificationId;
},
codeAutoRetrievalTimeout: (verificationId) {
_verificationId = verificationId;
},
timeout: Duration(seconds: 60),
);
}

Future<UserCredential?> verifyOTP(String smsCode) async {
if (_verificationId == null) {
throw Exception('Verification ID not found');
}

final credential = PhoneAuthProvider.credential(
verificationId: _verificationId!,
smsCode: smsCode,
);

return await _auth.signInWithCredential(credential);
}

// Other methods
Future<void> signOut() async {
await _googleSignIn.signOut();
await _auth.signOut();
}

Future<void> resetPassword(String email) async {
await _auth.sendPasswordResetEmail(email: email);
}

Future<void> sendEmailVerification() async {
await _auth.currentUser?.sendEmailVerification();
}

bool get isEmailVerified => _auth.currentUser?.emailVerified ?? false;

String _handleAuthException(FirebaseAuthException e) {
switch (e.code) {
case 'weak-password':
return 'Mật khẩu quá yếu';
case 'email-already-in-use':
return 'Email đã được sử dụng';
case 'user-not-found':
return 'Không tìm thấy tài khoản';
case 'wrong-password':
return 'Mật khẩu không đúng';
case 'invalid-email':
return 'Email không hợp lệ';
default:
return e.message ?? 'Đã xảy ra lỗi';
}
}
}

8️⃣ UI Example: Login Screen

class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _authService = AuthService();
bool _isLoading = false;

Future<void> _signIn() async {
setState(() => _isLoading = true);

try {
await _authService.signInWithEmail(
email: _emailController.text.trim(),
password: _passwordController.text,
);
// Navigation sẽ được xử lý bởi AuthWrapper
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
} finally {
setState(() => _isLoading = false);
}
}

Future<void> _signInWithGoogle() async {
setState(() => _isLoading = true);

try {
await _authService.signInWithGoogle();
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
} finally {
setState(() => _isLoading = false);
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 16),
TextField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Mật khẩu'),
obscureText: true,
),
SizedBox(height: 24),
ElevatedButton(
onPressed: _isLoading ? null : _signIn,
child: _isLoading
? CircularProgressIndicator()
: Text('Đăng nhập'),
),
SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _isLoading ? null : _signInWithGoogle,
icon: Icon(Icons.g_mobiledata),
label: Text('Đăng nhập với Google'),
),
],
),
),
);
}
}

9️⃣ Best Practices

9.1 Security

  • ✅ Luôn validate input
  • ✅ Sử dụng strong password requirements
  • ✅ Enable email verification
  • ✅ Implement rate limiting
  • ✅ Store sensitive data securely

9.2 Error Handling

  • ✅ Hiển thị thông báo lỗi thân thiện
  • ✅ Log errors để debug
  • ✅ Handle network errors

9.3 User Experience

  • ✅ Hiển thị loading state
  • ✅ Auto-fill email khi có thể
  • ✅ Remember me functionality
  • ✅ Biometric authentication (optional)

🔟 Kết Luận

Firebase Authentication giúp bạn:

Dễ tích hợp: Setup nhanh chóng
Nhiều phương thức: Email, Google, Phone, etc.
Bảo mật cao: Được Google quản lý
Miễn phí: Free tier rộng rãi
Scalable: Tự động scale

💡 Lời khuyên: Luôn enable email verification và implement proper error handling. Sử dụng AuthWrapper để quản lý authentication state trong toàn bộ app.


🎓 Học Sâu Hơn Về Flutter

Muốn master Flutter, Firebase, và các best practices? Tham gia các khóa học tại Hướng Nghiệp Dữ Liệu:

📚 Khóa Học Liên Quan:


📝 Bài viết này được biên soạn bởi đội ngũ Hướng Nghiệp Dữ Liệu. Để cập nhật thêm về Flutter, Firebase Authentication và các best practices trong phát triển ứng dụng di động, hãy theo dõi blog của chúng tôi.