4

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...

Shakirullahi Logico
  • 249
  • 1
  • 2
  • 10
  • what are u trying to achieve is not clear – Ajay Beniwal Dec 03 '13 at 11:22
  • I think this post may help you: http://stackoverflow.com/questions/14980805/angularjs-resolve-with-controller-as-string Run your auth call to server inside resolve part and return promise. Route will not change until resolved. – Andrej Kaurin Dec 03 '13 at 11:53
  • Also, check out [angularFire-seed](https://github.com/firebase/angularFire-seed) which has working routing examples with Firebase login. – Kato Dec 03 '13 at 16:03
  • @Kato Thanks. angularFire-seed is very useful resource for me. but same issue encounters there. If a logged user go to http://localhost:63342/app/index.html#/account (in new window, refresh the page) login page flashing before account page is shown. – Shakirullahi Logico Dec 04 '13 at 04:30

1 Answers1

6

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>
Kato
  • 40,352
  • 6
  • 119
  • 149