1

I'm trying to implement basic authentication routing in AngularJS. I have a model that has a authorize method that returns a promise. I want the routing to wait until that authorize function has returned true or false to continue, once that has completed it should resume the path or redirect the user to the login page.

I think essentially i need to stop routing, call that method and then resume or redirect to login. Below is the code I have so far but i'm not sure how to accomplish the pause/resume. Any ideas?

return angular.module('d', ['ngCookies', 'ngRoute'])
    .config(['$routeProvider', '$locationProvider', '$httpProvider', 
        function ($routeProvider, $locationProvider, $httpProvider) {

            $routeProvider.when('/',
            {
                templateUrl: 'views/home.html',
                controller: 'HomeCtrl'
            });
            $routeProvider.when('/login',
            {
                templateUrl: 'views/login.html',
                controller: 'LoginCtrl'
            });

            $routeProvider.otherwise({ redirectTo: '/404' });
            $locationProvider.html5Mode(true);

            // handle unauthorized requests by redirecting to login page
            $httpProvider.responseInterceptors.push(
                ['$location', '$q', function ($location, $q) {
                    function success(response) {
                        return response;
                    }

                    function error(response) {
                        if (response.status === 401) {
                            $location.path('/login');
                            return $q.reject(response);
                        }
                        else {
                            return $q.reject(response);
                        }
                    }

                    return function (promise) {
                        return promise.then(success, error);
                    }
                }]);

        }])
    .run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {

        $rootScope.$on("$routeChangeStart", function (event, next, current) {
            $rootScope.error = null;
            Auth.authorize().then(function(){
                $location.path('/');
            },function(){
                $location.path('/login');
            });
        });

    }]);
amcdnl
  • 8,470
  • 12
  • 63
  • 99
  • [$routeProvider resolve][1] is the correct approach I feel. [1]: http://stackoverflow.com/questions/18788586/angularjs-handle-routing-before-they-load – amcdnl Nov 05 '13 at 20:49
  • Could you provide the ´authorize´ method please – Merlin May 14 '14 at 11:54

1 Answers1

2

Your solution is very similar to a prototype I wrote a while back.

The idea is that whenever you "touch" the server and get an authentication error, a modal window pops-up asking for a login without changing the URL (you let it change to the new URL and stay there).

My implementation was also based on an interceptor checking for 401. It has a dependency on $rootScope and sets a property "needsLogin" to true. The page template has the login modal window visible when needsLogin === true and hides the ng-view (this is important since the new route has been loaded but it misses its data). Finally, upon successfull login, the login controller does the $route.reload() and then sets $rootScope.needsLogin = false.

Small snippets:

<div id="main" role="main" ng-show="needsLogin === false">
    <div ng-view></div>
</div>

<div ng-show="needsLogin === true" ng-include="'views/login.html'" class="overlay"></div>

The login controller can be something like:

function LoginCtrl($scope, $rootScope, $route, $http) {

    $scope.login = function () {
        $http.post( /* Somehow do your login */ )
            .success(function () {
                var deregister = $rootScope.$on('$routeChangeSuccess', function () {
                    // hide login / show ng-view after route has been reloaded
                    $rootScope.needsLogin = false;
                    deregister();
                });
                $route.reload();
            })
            .error(function () {
                /* handle errors */
            });
    };
}

$route.reload() is not a full page refresh, it merely re-initializes the route (controller/view etc). Hopefully, the call that was rejected before will run again and the page will be fine.

Kos Prov
  • 4,207
  • 1
  • 18
  • 14
  • I like your idea about the dropping the modal over the top to better preserve the URL, in code I did not show in this sample I cached the url and then redirected upon success. I'm scared with this approach I might get a ton of errors before I get that dialog due to failures via the service auth. – amcdnl Nov 04 '13 at 19:00
  • I found this https://gist.github.com/thomasnordlund/4025773 but it seems over complicated. – amcdnl Nov 04 '13 at 19:13