angularjs : How to authenticate before route changes

angular-routing, angularfire, angularjs, firebase

Solution

Update

Most of this is outdated now with the progression of angularFire. Check out this module for an updated version: https://github.com/firebase/angularFire-seed/blob/master/app/js/module.simpleLoginTools.js

It decorates `ng-cloak` to work with simple login and provides some optimized helper methods as well as working with recent versions.

Old Post

Authenticating requires a round trip to the server. In an SPA, you don't generally reload the page during normal usage so this is really superfluous in most cases.

However, it's not difficult to solve with a moderate understanding of Angular. In fact, I taught this exact scenario as an example for writing directives in a recent workshop. Here's the gist I used, and here's an example app that uses the gist.

Note that if you try to clone the GitHub repo, you need to grab that particular tag (`git checkout step3`) as the course was taught in steps.

For compliance with SO format, here's the contents of the gist. First the directive:

angular.module('waitForAuth', [])

/**
 * A service that returns a promise object, which is resolved once angularFireAuth
 * is initialized (i.e. it returns login, logout, or error)
 */
.service('waitForAuth', function($rootScope, $q, $timeout) {
    var def = $q.defer(), subs = [];
    subs.push($rootScope.$on('angularFireAuth:login', fn));
    subs.push($rootScope.$on('angularFireAuth:logout', fn));
    subs.push($rootScope.$on('angularFireAuth:error', fn));
    function fn() {
       for(var i=0; i < subs.length; i++) { subs[i](); }
       $timeout(function() {
           // force $scope.$apply to be re-run after login resolves
           def.resolve();
       });
    }
    return def.promise;
})

/**
 * A directive that hides the element from view until waitForAuth resolves
 */
.directive('ngCloakAuth', function(waitForAuth) {
   return {
       restrict: 'A',
       compile: function(el) {
           console.log('waiting');
           el.addClass('hide');
           waitForAuth.then(function() {
               el.removeClass('hide');
           })
       }
   }
})

/**
 * A directive that shows elements only when the given authentication state is in effect
 */
.directive('ngShowAuth', function($rootScope) {
    var loginState;
    return {
        restrict: 'A',
        compile: function(el, attr) {
            var expState = attr.ngShowAuth;
            function fn(newState) {
                loginState = newState;
                el.toggleClass('hide', loginState !== expState );
            }
            fn(null);
            $rootScope.$on("angularFireAuth:login",  function() { fn('login') });
            $rootScope.$on("angularFireAuth:logout", function() { fn('logout') });
            $rootScope.$on("angularFireAuth:error",  function() { fn('error') });
        }
    }
});

And some example usages:

<style>
  .hide { display: none; }
</style>

<script>
  // include the waitForAuth module as a dependency
  angular.module('myApp', ['waitForAuth'])

  // you can use waitForAuth directly from your scripts
  .controller('myController', function(waitForAuth) {
     waitForAuth.then(function() {
         /* do something after auth completes */
     })
  })
</script>

<!-- and you can use the directives in your views -->
<div ng-cloak-auth>Authentication has resolved.</div>

<div ng-show-auth="login">{{user.id}} is logged in</div>

<div ng-show-auth="logout">Logged out</div>

<div ng-show-auth="error">An error occurred during authentication</div>

Problem

How to wait for authentication completed before route changes in angularjs app. I am using angularfire simple authentication. My plunker is here You can test the plunker with example@gmail.com and example as credential. I know the cause for the issue. but don't know how to resolve it... Actually when an authenticated user goes to '#/home/, in the sahayiApp.run( function(){ ...}) function a condition is checked if(next.authRequired && !$rootScope.user){ ... } . Actually a authenticated user can pass this checking, but this code runs before actual authentication happens.. So is there any way to authenticate before route changes...

Original source

Related problems