Trong bài trước, tôi đã giới thiệu Firebase Authentication và cách tích hợp nó vào trong Android, iOS và Web project của bạn, cũng như cho users sign up thông qua email và password. Trong bài này, tôi sẽ tiếp tục hướng dẫn tổng quát về sign up và sign in thông qua các nhà cung cấp bên ngoài, như Facebook, Twitter và Google.
4. Tại sao bạn nên cân nhắc sử dụng authentication từ các nhà cung cấp bên ngoài?
Kể từ khi trào lưu mạng xã hội bùng nổ, từ “cư dân mạng” ra đời, thì tuyệt đại đa số các “cư dân mạng” đó đều có tài khoản mạng xã hội, mà đa phần là Facebook, cũng như dùng Gmail làm địa chỉ email chính. Với sức ảnh hưởng lớn và bành trướng của Facebook sang nhiều lĩnh vực, từ một mạng xã hội thuần túy chia sẻ các cảm xúc, hình ảnh, sang một (nhà cung cấp) nền tảng cho các nhà phát triển khác xây dựng các dịch vụ của mình trên nền tảng đó, và sự phổ biến của Google đến mức “google” đã trở thành một động từ trong nhiều ngôn ngữ, thì người dùng nào cũng có xu hướng thích sign up và sign in gián tiếp qua các tài khoản Facebook hoặc Google của mình. Đơn giản, họ chỉ cần kết nối với tài khoản Facebook hoặc Google trong trang web hoặc ứng dụng, họ sẽ bỏ qua rất nhiều bước phiền phức như điền địa chỉ email (có khi tới 2 lần), điền u-xơ-nem mong muốn, điền pát-uộc 2 lần, xác nhận qua email… Chỉ với 1-2 chạm là họ sẽ dễ dàng hoàn thành thao tác sign up, hoặc sign in.
Còn đối với các nhà phát triển, việc dùng nhờ authentication từ các ông lớn xanh dương hoặc bốn màu kia sẽ giảm bớt đi khá nhiều các trường thông tin trong cơ sở dữ liệu quản lí thành viên, cũng như có thể hướng người dùng đến ngay trang Facebook fanpage của tổ chức mình để họ có thể like ngay mà không phải đăng nhập lại. Như vậy, cả lập trình viên và người dùng đều có lợi với cách sign up / sign in này. Khá nhiều công ty khởi nghiệp (start ups) hiện tại còn “không thèm” tự thiết kế phương thức authentication của riêng mình, và hoàn toàn “nhờ vả” Facebook và Google.
5. Sử dụng Firebase Authentication gián tiếp trong ứng dụng của mình.
Thông thường, nếu bạn muốn dùng dịch vụ Authentication của Facebook, Twitter hay Google, thì bạn phải đăng kí với họ, và nhận được một mớ thông tin định danh loằng ngoằng. Và nếu bạn tích hợp bao nhiêu phương thức từ bao nhiêu nhà cung cấp, thì bạn có bấy nhiêu mớ kia để lưu trữ. Firebase Authentication ra đời đóng vai trò trung gian. Thay vì bạn đi theo hướng trực tiếp BẠN – NHÀ CUNG CẤP, bạn sẽ đi hướng gián tiếp BẠN – FIREBASE – NHÀ CUNG CẤP. Bạn sẽ chỉ chủ yếu “deal with” Firebase, phần còn lại thì “Ngọn lửa” sẽ lo hết.
Để thông báo với Firebase là bạn sẽ sử dụng dịch vụ Authentication gián tiếp của họ, bạn cần truy cập Firebase Console, mở project của bạn. Trong phần Authentication, bạn chọn SIGN-IN METHOD và cho Enable các nhà cung cấp mà bạn muốn.
Trong bài viết này, tôi sẽ chỉ hướng dẫn Firebase Authentication with Google Sign On và sơ lược về Anonymous auth. Đối với các nhà cung cấp khác như Facebook hay Twitter, bạn có thể làm tương tự theo hướng dẫn của các nhà cung cấp đó.
6. Google Authentication
6.1. Đối với Android:
Việc tích hợp Firebase Authentication with Google Sign On thực chất chính là việc tích hợp Google Sign On vào project Android của bạn. Nếu bạn đã làm việc này rồi, chẳng hạn như đã tích hợp nút SIGN IN WITH GOOGLE vào ứng dụng của bạn, thì bạn chỉ cần liên kết project hiện tại vào Firebase project và tạo các FirebaseAuth instances, gán events như bài trước. Còn nếu chưa, thì bạn cần yêu cầu Gradle compile thêm thư viện. Lưu ý là bạn đã phải đưa tập tin google-services.json vào project. Nếu chưa, xin xem lại Bài mở đầu.
1 |
compile 'com.google.android.gms:play-services-auth:10.2.0' // phiên bản mới nhất tại thời điểm bài viết là 10.2.0, bạn luôn nên sử dụng phiên bản mới nhất |
Trong khi đợi Android Studio sync thư viện, bạn truy cập vào Google APIs Credential, yêu cầu, nhận và cóp Client ID (ở phần OAuth 2.0 client IDs) từ Firebase project của bạn. Khi Gradle đã sync xong, bạn khai báo và tạo 1 instance GoogleApiClient mGoogleApiClient trong Activity đăng nhập, từ đây trở xuống tôi sẽ gọi là SignInActivity. Bạn truyền vào Client ID mà bạn vừa cóp bên trên. Tiếp theo, bạn tạo các FirebaseAuth instances như FirebaseAuth mAuth và FirebaseAuth.AuthStateListener mAuthListener như bài trước và vận dụng là được:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
import ... import com.google.android.gms.auth.api.Auth; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.SignInButton; /** Activity nên extends FragmentActivity hay sub class của nó (vd AppCompatActivity) * để bạn dễ xử lí sign in event trong Fragment (nếu có). * Tuy nhiên, dù có dùng Fragment hay không thì cũng nên extends FragmentActivity * hoặc AppCompatActivity. */ public class SignInActivity extends FragmentActivity implements View.OnClickListener, GoogleApiClient.OnConnectionFailedListener { String CLIENT_ID = ...; GoogleApiClient mGoogleApiClient; SignInButton mSignInButton; FirebaseAuth mAuth; FirebaseAuth.AuthStateListener mAuthListener; void onCreate(Bundle) { super.onCreate(...); // Trong layout, bạn cần có một SignInButton. setContentView(int); // Các Google instances GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken(CLIENT_ID)) .requestEmail() .build(); mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this, this) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .build(); // Nút SignInButton ở đây: mSignInButton = (SignInButton) findViewById(int); mSignInButton.setOnClickListener(this); // FirebaseAuth, xem lại bài trước. mAuth = ...; mAuthListener = ...' } // Khi nhấn mSignInButton thì gọi hàm signIn() bên dưới. void onClick(View v) { signIn(); } void signIn() { Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); startActivityForResult(signInIntent, RC_SIGN_IN); // Kết quả trả về nằm ở hàm onActivityResult bên dưới. } @Override void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Intent từ hàm signIn() trả về, thông qua requestCode if (requestCode == RC_SIGN_IN) { // Xét result GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); if (result.isSuccess()) { // Thành công GoogleSignInAccount account = result.getSignInAccount(); firebaseAuthWithGoogle(account); // Hàm được định nghĩa bên dưới. } else { // Thất bại // ... } } } void firebaseAuthWithGoogle(GoogleSignInAccount acct) { Log.d(TAG, "firebaseAuthWithGoogle:" + acct.getId()); AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null); mAuth.signInWithCredential(credential) .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() { void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { // Thành công Toast.makeText(SignInActivity.this, "Đã đăng nhập.", Toast.LENGTH_SHORT).show(); } else { // Thất bại } } ); } } |
6.2. Trên iOS, Swift.
Nếu bạn đã tích hợp nút Google Sign In (GIDSignInButton) vào ứng dụng thì bạn có thể quay lại bài trước và làm các events tương tự như vậy. Còn nếu không, bạn hãy thêm dòng dưới đây vào Podfile và cho install lại. Lưu ý là bạn cần tích hợp tập tin GoogleService-Info.plist vào project. Nếu chưa, vui lòng xem lại bài mở đầu.
1 |
pod 'Google/SignIn' |
Bạn thêm một custom URL Scheme vào project bằng cách mở project config, nhấn đúp tên project, chọn ứng dụng của bạn trong TARGETS, chọn tab Info và expand phần URL Types. Nhấn dấu cộng và thêm URL từ client ID. Client ID chính là giá trị của RESERVED_CLIENT_ID trong tập tin GoogleService-Info.plist. Cóp pát value này vào URL Schemes bên Xcode. Giao diện sẽ tương tự như dưới đây:
Tại AppDelegate.swift, bạn cho implements GIDSignInDelegate như sau:
1 |
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate { |
Trong application:didFinishLaunchingWithOptions:, bạn luôn phải có hàm FIRApp.configure(), và thêm các delegates như bên dưới.
1 2 3 |
FIRApp.configure() GIDSignIn.sharedInstance().clientID = FIRApp.defaultApp()?.options.clientID GIDSignIn.sharedInstance().delegate = self |
Tiếp tục, bạn cho implement application:openURL:options: với nội dung sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@available(iOS 9.0, *) func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool { return GIDSignIn.sharedInstance().handle(url, sourceApplication:options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String, annotation: [:]) } } // iOS 8 func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { return GIDSignIn.sharedInstance().handle(url, sourceApplication: sourceApplication, annotation: annotation) |
Trong app delegate, bạn implement GIDSignInDelegate protocol để handle quá trình sign in: (**)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) { // ... if let error = error { // ... return } guard let authentication = user.authentication else { return } let credential = FIRGoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken) // ... } func signIn(signIn: GIDSignIn!, didDisconnectWithUser user:GIDGoogleUser!, withError error: NSError!) { // Nếu người dùng bị thoát thì xử lí code ở đây. // ... } |
Trong ViewController, bạn cho implement GIDSignInUIDelegate:
1 |
class ViewController: UITableViewController, GIDSignInUIDelegate { |
Trong hàm viewDidLoad, bạn thêm các dòng bên dưới:
1 2 3 4 5 6 |
override func viewDidLoad() { super.viewDidLoad() GIDSignIn.sharedInstance().uiDelegate = self GIDSignIn.sharedInstance().signIn() } |
Trong storyboard, bạn thêm nút GIDSignInButton. Do bạn đã get Google ID token (xem lại chỗ ** phía trên), nên bạn chỉ cần bắt sự kiện cho nó với các dòng sau:
1 2 3 4 5 6 |
FIRAuth.auth()?.signIn(with: credential) { (user, error) in // ... if let error = error { // ... return } |
6.3. Với Web:
Các event sẽ được handle trong Javascript. Bạn vui lòng tham khảo bài viết trước để tìm hiểu về firebase.auth(). Ở đây tôi sẽ trình bày trường hợp đơn giản nhất.
Trước tiên, thêm một số thư viện JS của Firebase vào phần cuối của <body>:
1 2 3 4 5 6 7 8 9 10 11 |
<script src="https://www.gstatic.com/firebasejs/3.6.10/firebase-app.js"></script> <script src="https://www.gstatic.com/firebasejs/3.6.10/firebase-auth.js"></script> // <script src="https://www.gstatic.com/firebasejs/3.6.10/firebase-database.js"></script> // <script src="https://www.gstatic.com/firebasejs/3.6.10/firebase-messaging.js"></script> <script> var config = { // ... }; firebase.initializeApp(config); </script> |
Tiếp tục, ta tạo một instance của Google provider, vì ta đang dùng Google authentication:
1 |
var provider = new firebase.auth.GoogleAuthProvider(); |
Để đăng nhập với một pop-up, bạn dùng event sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
firebase.auth().signInWithPopup(provider).then(function(result) { // Lấy Google Access Token để truy cập Google API. var token = result.credential.accessToken; // Tạo biến user để chứa thông tin user. var user = result.user; // ... }).catch(function(error) { // Xử lí lỗi nếu có phát sinh var errorCode = error.code; var errorMessage = error.message; // Email của người dùng var email = error.email; // Xác định firebase.auth.AuthCredential type var credential = error.credential; // ... }); |
Còn bạn muốn dùng luôn trang hiện tại bằng cách chuyển hướng nó qua accounts.google.com, (như cách Gmail, GDrive… sử dụng) sau khi người dùng đăng nhập xong thì Google tự chuyển hướng lại trang của bạn, thì hãy dùng event dưới đây.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
firebase.auth().getRedirectResult().then(function(result) { if (result.credential) { // Lấy Google Access Token để truy cập Google API. var token = result.credential.accessToken; // ... } // Tạo biến user để chứa thông tin user. var user = result.user; })..catch(function(error) { // Xử lí lỗi nếu có phát sinh var errorCode = error.code; var errorMessage = error.message; // Email của người dùng var email = error.email; // Xác định firebase.auth.AuthCredential type var credential = error.credential; // ... }); |
Xong xuôi, không quá khó phải không?
7. Anonymous authentication.
Anonymous có nghĩa là ẩn danh. Khi người dùng đăng nhập dưới hình thức này, Firebase sẽ mở 1 temporary session cho họ thao tác. Sau khi session này được đóng, giả sử như họ chưa sign out, nếu ứng dụng Android hoặc iOS còn lưu lại dữ liệu định danh người dùng ở bộ cache, hoặc trình duyệt chưa bị xóa cache và cookies, thì khi truy cập lại sau một khoảng thời gian ngắn nhất định, Firebase sẽ mở lại session cho họ thao tác tiếp. Còn trong các trường hợp kia, thì mọi dữ liệu liên quan sẽ một đi không trở lại. Tất nhiên, Firebase có hỗ trợ để chuyển đổi từ anonymous user sang identified user, đơn giản là cho họ sign up và chuyển các temp data sang cơ sở dữ liệu “cứng”.