2

EDIT: forgot to mention that i've been working with AngularJs for a week only, so if you see something you think should be changed for the better and is not related to the question itself feel free to tell me on the comments section.


ok, so I have my authentication Controllers and providers which I won't show because they're irrelevant for the scope of the question. Then I have an interceptor to check if the user is authenticated when a Call is made. If so I set the Authentication header on the request to include the user's Token if not I redirect the user to the login page and don't even make the request to the server (obviously if someone bypasses this theres also an Authorize on the API).

What I want is to add a few exceptions, meaning there are some pages I want to allow even if the user has no Auth Token. I'm able to this if it's a specific path, but I want to allow my 404 page to be accessed and it's in the Routing that I'm specifying .otherwise to go to the 404 page, how can I make so that my interceptor only redirects to login if it's not going to this page.

The interceptor

.factory('authInterceptorService', ['$q', '$location', 'localStorageService', function ($q, $location, localStorageService) {

    var authInterceptorServiceFactory = {};

    var authData = localStorageService.get('authorizationData');

    var _request = function (config) {

        config.headers = config.headers || {};

        if (authData) {
            config.headers.Authorization = 'Bearer ' + authData.token;
        } else if ($location.path != '/accounts/login' && $location.path != '/accounts/register') {
            $location.path('/accounts/login');
        }

        return config;
    }

    var _responseError = function (rejection) {
        if (rejection.status === 401) {
            $location.path('/accounts/login');
        }
        return $q.reject(rejection);
    }

    authInterceptorServiceFactory.request = _request;
    authInterceptorServiceFactory.responseError = _responseError;

    return authInterceptorServiceFactory;
}])

and in my Routing

 $urlRouterProvider.otherwise('/page-not-found');

 $stateProvider
     (...)//rest of the states

     .state('page-not-found', {
         url: '/page-not-found',
         templateUrl: '/Content/partials/error/404.html',
         data: {
             displayName: false
         }
     })

     (...)//rest of the states

I tried to add '/page-not-found' to my if but it won't work as expected because by the time the location is checked for the first time it's still not redirected.

edit As sugested by charlietfl I'm now trying to use resolve but it's not even passing my function.

I removed this code from my interceptor:

else if ($location.path != '/accounts/login' && $location.path != '/accounts/register') {
    $location.path('/accounts/login');
}

and add a new service to the authentication module:

.service('authCheckService', ['$http', '$q', 'localStorageService', function ($http, $q, localStorageService) {
    var self = {
        'onlyLoggedIn': function ($state, $q) {
            var deferred = $q.defer();
            var authData = localStorageService.get('authorizationData');
            console.log(authData);
            if (authData) {
                deferred.resolve();
            } else {
                deferred.reject();
                $state.go('login');
            }
            return deferred.promise;
        }
    }

    return self;
}]);

and i'm trying to call it as:

.state('smo-dashboard', {
        url: '/dashboard',
        templateUrl: '/Content/partials/dashboard.html',
        resolve: authCheckServiceProvider.onlyLoggedIn
})

notice that i'm trying to log authData var to check if it's working but it isn't and there's no error on the console also.

3
  • 2
    one suggestion is have a parent state for all paths that require login. Then you can have a resolve on that parent and if user not logged in all children of that state are inaccessible because parent won't resolve. for all other routes there would be nothing to check Commented Sep 23, 2015 at 16:28
  • @charlietfl tried to use resolve as suggested but I might be doing something wrong, can you please take a look at my edit Commented Sep 24, 2015 at 9:44
  • Just want to add couple of words about issue from real app, I am redirecting user to login page, and if I do reject promise, there is burst of unauthorized requests to server until redirect is actually happen at browser. So I decided not to reject error before redirect. Commented Jun 8, 2016 at 9:20

2 Answers 2

6

Finally figured out how to solve it using resolve.

first of all I completely removed the interceptor I was using before. then I made a function inside my Routing .config to use with every resolve for the authentication. finally to handle my resolve I'm using $stateChangeError to redirect to the login state

the Routing Config

.config(function ($stateProvider, $urlRouterProvider) {

    // function to check the authentication //
    var Auth = ["$q", "authService", function ($q, authService) {
        authService.fillAuthData;
        if (authService.authentication.isAuth) {
            return $q.when(authService.authentication);
        } else {
            return $q.reject({ authenticated: false });
        }
    }];

    /* if the state does not exist */
    $urlRouterProvider
        .otherwise('/page-not-found'); 

    $stateProvider

        // state that allows non authenticated users //
        .state('home', {
            url: '/',
            templateUrl: '/Content/partials/home.html',
        })

        // state that needs authentication //
        .state('smo-dashboard', {
            url: '/dashboard',
            templateUrl: '/Content/partials/dashboard.html',
            resolve: {
                auth: Auth
            }
        })

        // errors //
         .state('page-not-found', {
             url: '/page-not-found',
             templateUrl: '/Content/partials/error/404.html'
         })

        // accounts //
        .state('login', {
            url: '/accounts/login',
            templateUrl: '/Content/partials/account/login.html'
        })

        // OTHER STATES //
    }
);

in the MainController

$scope.$on("$stateChangeError", function (event, toState, toParams, fromState, fromParams, error) {
    $state.go("login");
});
Sign up to request clarification or add additional context in comments.

Comments

0

An error service like this could help to handle what to do according to status in responses:

'use strict';

/**
 * Error Service
 */

angular.module('app.errorService', [])
        .factory("errorService", function ($route, $location) {        
                return {   
                    checkAndReturnError: function(a,b,c) {
                        if (a.status === 401){
                            (function(){
                                return $location.path("/accounts/login");
                            }());
                            return;
                        }
                        if (a.status === 404)
                            return;

                           alert("Error \n *" + a.data.message);

                    }
                };
        });

Then when you do your calls if the response status is 401 it will redirect. The vbad thing agout this is you have to add it to all calls:

            $scope.pageChanged = function() {
                $scope.Promise = Resource.get({}, function(response) {
                }, errorService.checkAndReturnError);
            };

1 Comment

what I want is to have a 401 status response for some pages, even if there is no call to the server. Meaning I have a dashboard page which is only a partial-view with no calls to the API itself and I want to restrict it to authenticated users

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.