5
votes

I am trying to use the new Firebase with the Auth system and restricting routes in my $routeProvider via resolves.

However, I'm not quite understanding.

This is what I have. In my .config function, I am defining routes and initializing firebase. The following is in my config block.

$routeProvider
   .when('/', {
      templateUrl: 'templates/dashboard.ejs',
      //resolve. this needs restricted
   })
   .when('/login', {
      templateUrl: 'templates/login.ejs'
   })

firebase.initializeApp(config);

I have found on the new docs site that these functions are available that are listed in my .run() block for angular.

.run(function($rootScope, $location) {

    $rootScope.authentication = firebase.auth();

    /*firebase.auth().onAuthStateChanged(function(user) {
        if(user) {
            $rootScope.user = user;
        } else {
            $rootScope.user = false;
            $location.path('/login')
        }
    })*/

    var user = firebase.auth().currentUser;

    if (user) {
        $rootScope.user = user;
    } else {
        $rootScope.user = false;
        console.log('No User!');
        $location.path('/login');
    }
})

Now, what I have above is only firing every other time I access any url on my site.

So my question is, how do I take what's in my .run() function and make it into a resolve for my routeProvider so I'm able to restrict routes again.

With the old firebase you would just call $firebaseAuth() like below and have a routeChangeError function that was called like the following.

// In config block. **old way**
$routeProvider
        .when('/', {
            templateUrl: 'templates/dashboard.ejs',
            resolve: {
                "currentAuth": ["$firebaseAuth", function($firebaseAuth) {
                    var ref = new Firebase(fbUrl);
                    var authObj = $firebaseAuth(ref);

                    return authObj.$requireAuth();
                }]
            }
        })

And then the routechangeerror in the .run() block.

 // .run block **old way**
 .run(function($rootScope, $location) {
    $rootScope.$on("$routeChangeError", function(event, current, previous, eventObj) {
        if (eventObj === 'AUTH_REQUIRED') {
            console.log('auth required!');
            $location.path("/login");
        }
    });
})

This doesn't work anymore because you aren't using $firebaseAuth() anymore. or new Firebase(fbUrl); for that matter either.

UPDATE

I feel like it's a bit messy but looking into routes a little more, I came up with this.

In my route resolve:

.when('/', {
            templateUrl: 'templates/dashboard.ejs',
            resolve: {
                userAuthenticated: ["$http", "$q", function($http, $q) {
                    var deferred = $q;
                    if(firebase.auth().currentUser) {
                        deferred.resolve();
                    } else {
                        deferred.reject();
                        console.log('Really!?');
                    }
                    return deferred.promise;
                }]
            }
        })  

And then a simple routeChangeError.

$rootScope.$on("$routeChangeError", function (event, current, previous, rejection) {
        alert("Not authorised");
})

The only problem is, it appears the deferred.promise is returning undefined. It's not activating the routeChangeError function. Even though my console.log() is getting called.

Could this be because firebase.auth().currentUser returns null when no user is authenticated?

2

2 Answers

4
votes

The methods have been renamed to $waitForSignIn() and $requireSignIn.

You can also use the $firebaseRefProvider service to configure your Database URL so it automatically configures the $firebaseAuthService.

angular.module('app', ['firebase'])
  .constant('FirebaseDatabaseUrl', '<my-firebase-url>')
  .controller('HomeCtrl', HomeController)
  .config(function($firebaseRefProvider, FirebaseDatabaseUrl, $routeProvider) {
     $firebaseRefProvider.registerUrl(FirebaseDatabaseUrl);
     $routeProvider.when("/home", {
        controller: "HomeCtrl",
        templateUrl: "views/home.html",
        resolve: {
          // controller will not be loaded until $waitForSignIn resolves
          "firebaseUser": function($firebaseAuthService) {
            return $firebaseAuthService.$waitForSignIn();
          }
        }
      }).when("/account", {
        controller: "AccountCtrl",
        templateUrl: "views/account.html",
        resolve: {
          // controller will not be loaded until $requireSignIn resolves
          "firebaseUser": function(Auth) {
            // If the promise is rejected, it will throw a $stateChangeError
            return $firebaseAuthService.$requireSignIn();
          }
        }
      });
  });

function HomeController($scope, $firebaseRef, firebaseUser) {

} 
0
votes

So I'm not sure if it's the cleanest cut code on the globe but it works.

Firebase now provides a new function for getting the current user authenticated if there is one. After you initialize firebase, you have access to this function. firebase.auth().currentUser.

(This function returns user details if logged in and null if not)

So, in your route, you would create and resolve a promise based on the results of this function. Be sure to define deferred as $q.defer(); (I struggled with this as you can see in my question update)

    $routeProvider
    .when('/', {
            templateUrl: 'templates/dashboard.ejs',
            resolve: {
                userAuthenticated: ["$http", "$q", function($http, $q) {
                    var deferred = $q.defer();
                    if(firebase.auth().currentUser) {
                        deferred.resolve();
                    } else {
                        deferred.reject('NOT_AUTHORIZED');
                    }
                    return deferred.promise;
                }]
            }
        })  

And then in your run function for Angular, you'll watch for route change errors.

.run(function($rootScope, $location) {
   $rootScope.$on("$routeChangeError", function (event, current, previous, rejection) {
      if(rejection == 'NOT_AUTHORIZED')
      {
         $location.path('/login');
      }
    })
})

In the function above, you simply watch for an error in the route change. If the rejection is equal to 'NOT_AUTHORIZED' (passed in our rejection function in the resolve function) then we redirect the user to the login screen to become authenticated.

NOTE: In this routeChangeError function, you are free to do what you like maybe even show a little growl that the user is not authorized.