Firebase LogIn Middleware

We need to write our first Middleware for Redux. I'll explain things a bit more in-depth here than I will in the future.

1. Create middleware file: lib/middleware/auth_middleware.dart

2. Add the necessary Redux methods:

  1. // lib/middleware/auth_middleware.dart
  2. import 'package:firebase_auth/firebase_auth.dart';
  3. import 'package:google_sign_in/google_sign_in.dart';
  4. import 'package:me_suite/actions/auth_actions.dart';
  5. import 'package:me_suite/models/app_state.dart';
  6. import 'package:redux/redux.dart';
  7. // Recall that middleware is simply functions.
  8. //
  9. // These functions more or less intercept actions -- pausing
  10. // the Redux cycle while your app does some work.
  11. //
  12. // If you have multiple middleware functions that are related
  13. // to a single piece of state, you can use a method like this
  14. // which will return multiple functions that you can add
  15. // to your store.
  16. //
  17. List<Middleware<AppState>> createAuthMiddleware() {
  18. final logIn = _createLogInMiddleware();
  19. final logOut = _createLogOutMiddleware();
  20. // Built in redux method that tells your store that these
  21. // are middleware methods.
  22. //
  23. // As the app grows, we can add more Auth related middleware
  24. // here.
  25. return combineTypedMiddleware([
  26. new TypedMiddleware<AppState, LogIn>(logIn),
  27. new TypedMiddleware<AppState, LogOut>(logOut)
  28. ]);
  29. }
  30. // Now, we need to write those two methods, both of which
  31. // return a Middleware typed function.
  32. //
  33. Middleware<AppState> _createLogInMiddleware() {
  34. // These functions will always take
  35. // your store,
  36. // the action thats been dispatched
  37. // and the a special function called next.
  38. return (Store store, action, NextDispatcher next) async {
  39. // YOUR LOGIC HERE
  40. // After you do whatever logic you need to do,
  41. // call this Redux built-in method,
  42. // It continues the redux cycle.
  43. next(action);
  44. };
  45. }
  46. Middleware<AppState> _createLogOutMiddleware() {
  47. return (Store store, action, NextDispatcher next) async {
  48. // YOUR LOGIC HERE
  49. next(action);
  50. };
  51. }

3. Add Firebase Logic

Almost all of these Classes and methods come from Firebase and it's associated classes.

  1. ...
  2. Middleware<AppState> _createLogInMiddleware() {
  3. return (Store store, action, NextDispatcher next) async {
  4. // FirebaseUser is the type of your User.
  5. FirebaseUser user;
  6. // Firebase 'instances' are temporary instances which give
  7. // you access to your FirebaseUser. This includes
  8. // some tokens we need to sign in.
  9. final FirebaseAuth _auth = FirebaseAuth.instance;
  10. // GoogleSignIn is a specific sign in class.
  11. final GoogleSignIn _googleSignIn = new GoogleSignIn();
  12. // Actions are classes, so you can Typecheck them
  13. if (action is LogIn) {
  14. try {
  15. // Try to sign in the user.
  16. // This method will either log in a user that your Firebase
  17. // is aware of, or it will prompt the user to log in
  18. // if its the first time.
  19. //
  20. GoogleSignInAccount googleUser = await _googleSignIn.signIn();
  21. GoogleSignInAuthentication googleAuth = await googleUser.authentication;
  22. // After checking for authentication,
  23. // We wil actually sign in the user
  24. // using the token that firebase.
  25. user = await _auth.signInWithGoogle(
  26. accessToken: googleAuth.accessToken,
  27. idToken: googleAuth.idToken,
  28. );
  29. print('Logged in ' + user.displayName);
  30. // This can be tough to reason about -- or at least it was for me.
  31. // We're going to dispatch a new action if we logged in,
  32. //
  33. // We also continue the current cycle below by calling next(action).
  34. store.dispatch(new LogInSuccessful(user: user));
  35. } catch (error) {
  36. store.dispatch(new LogInFail(error));
  37. }
  38. }
  39. next(action);
  40. };
  41. }
  42. Middleware<AppState> _createLogOutMiddleware() {
  43. return (Store store, action, NextDispatcher next) async {
  44. // Temporary instance
  45. final FirebaseAuth _auth = FirebaseAuth.instance;
  46. try {
  47. await _auth.signOut();
  48. print('logging out...');
  49. store.dispatch(new LogOutSuccessful());
  50. } catch (error) {
  51. print(error);
  52. }
  53. };
  54. }

If you've used Firebase before, then this seems familiar. If you haven't this should be blowing your mind. You just implementing Google OAuth in a couple lines of code.

4. Add middleware to Store

  1. // main.dart
  2. ...
  3. final store = new Store<AppState>(
  4. appReducer,
  5. initialState: new AppState(),
  6. // Middleware has to be a List of MiddleWare functions
  7. // This syntax will allow you to add any middleware
  8. // regardless of wether it's already a list of middleware
  9. // or it's just a function:
  10. middleware: []
  11. ..addAll(createAuthMiddleware())
  12. ..add(new LoggingMiddleware.printer()),
  13. );
  14. ...