Stalso
Stalso

Reputation: 1484

AngularJs SPA logout

I have posted more complex question here AngularJs logout action . But I have another aspect to see, and I think that it should be separated question.

I have Angualr SPA with authentication. Authentication implemented with bearer JWT tokens. I use ng-route to make different views. Let us imagine, that one view (using /protected route ,protecetedController and protectedDataService) contains protected resources, and another view (using /unprotected route, unprotecetedController and unprotectedDataService) contains unprotected resources. Controllers sample code :

(function () {
    'use strict';

    angular
        .module('protectedCtrl',[])
        .controller('protectedController', ['$location','protectedDataService', function ($location, protectedDataService) {
            var vm = this;
            vm.title = 'protectedController';
            protectedDataService.getData().then(function (res) {
                vm.values = res.data;
            }, function (err) {
                console.log(err);
            });
        }]);
})();

Unprotected controller:

(function () {
    'use strict';

    angular
        .module('unprotectedCtrl',[])
        .controller('unprotectedController', ['$location','unprotectedDataService', function ($location, unprotectedDataService) {
            var vm = this;
            vm.title = 'unprotectedController';
            unprotectedDataService.getData().then(function (res) {
                vm.values = res.data;
            }, function (err) {
                console.log(err);
            });
        }]);
})();

If unlogined user goes to protected resource (/protected path) he will be redirected to login page. But let us imagine, that logined user press logout button (for now I simply delete from browser local strage my token info and reload page). If user is on /protected page, he should be redirected to login page. If he is on /unprotected page nothing should happen. How can I implement this? How can I determine which page is active in ng-view (needs auth or does not) and make decision about redirection?

Upvotes: 1

Views: 802

Answers (1)

iulian
iulian

Reputation: 5812

You can use the $routeChangeStart event to catch the change of a route and a helper function to decide whether to redirect to login page or not.

In the run block of your angular app, define a variable for routes that do not require authentication and a function that checks whether the route the user is heading to is in this list.

angular
  .module('awesomeApp')
  .run(function (/* inject dependencies */) {
    // other code

    // Define a list of routes that follow a specific rule. This example
    // can be extended to provide access based on user permissions by
    // defining other variables that will list routes that require the
    // user to have specific permissions to access those routes.
    var routesThatDoNotRequireAuthentication = ['/unprotected'];

    // Check if current location is contained in the list of given routes.
    // Note: the check here can be made using different criteria. For
    // this example I check whether the route contains one of the
    // available routes, but you can check for starting position, or other criteria.
    function rootIsPresent ( route, availableRoutes ) {
      var routeFound = false;
      for ( var i = 0, l = availableRoutes.length; i < l; i++ ) {
        routeFound = routeFound || route.indexOf(availableRoutes[i]) > -1;
      }
      return routeFound;
    }

    // And now, when the $routeChangeStart event is fired, check whether
    // its safe to let the user change the route.
    $rootScope.$on('$routeChangeStart', function () {
      // Get the authentication status of a user
      var isAuthenticated = authenticationService.isAuthenticated();
      var newRoute = $location.url();

      // If the route requires authentication and the user is not 
      // authenticated, then redirect to login page.
      if ( !rootIsPresent(newRoute, routesThatDoNotRequireAuthentication) && !isAuthenticated ) {
        $location.path('/login');
      }
    });
});

Update

Seems that I've misunderstood the question and thought that all your routes are prefixed with either /protected or /unprotected (which, if you give it a thought, is really not that smart to do in most of the cases).

In this case, you can set an additional property when defining the routes in $routeProvider:

$routeProvider
  .when('/awesomePath', {
    templateUrl: 'awesome-template.html',
    controller: 'AwesomeController',
    controllerAs: 'vm',
    requireAuth: true  // add the property that defines a route as protected or unprotected
  })

And now, in your $routeChangeStart handler function, you check whether the route user is going to is protected or not:

$rootScope.$on('$routeChangeStart', function () {
      // Get the authentication status of a user
      var isAuthenticated = authenticationService.isAuthenticated();
      var newRoute = $location.url();

      // In a controller, $route object has a 'current' property that
      // holds route defined route properties, but here the 'current'
      // property is null, so we need to get the $route object we 
      // need by using the $location.url()
      if ( $route.routes[$location.url()].requireAuth && !isAuthenticated ) {
        $location.path('/login');
      }
    });

Thus you don't need to hardcode your routes anywhere.

Upvotes: 1

Related Questions