Search code examples
javascriptangularjsangularjs-scopeangularjs-routing

AngularJS - handle routes before app startup


Is it ok to use a Service within $stateProvider, for this purpose?

I have been through a lot of posts on stack regarding routing but most are confusing for beginners as everyone uses a different approach and treat it like rocket science. I would like to develop good habits from the start, and avoid bad ones...

Objective : run some functions before application starts and based on that output route my end user.

What I have tried:

Implement a Service >> AuthService | Actions >> simple check for :

  1. Fetch JWT token from client Side (SQLite DB) - Token contains basic details + API Key
  2. Send to server for authentication
  3. Response ( Boolean )
  4. If Authorized >> load Home View. Otherwise, redirect to register View .
  5. Server side validation takes place before loading any view

Service Providers

myApp.factory('AuthService', function ( Session) {
         var authService = {};
          authService.isAuthorized = function(){
          return Session.authorized() ;
         };
        return authService;
 });


myApp.service('Session', function ($resource, $q, $timeout, URL_Config) {
  this.authorized = function () {
        var deferred = $q.defer();
        $timeout(function () {
            var db = window.openDatabase("config.db", "1.0", "Config", 2 * 1024 * 1024);
            db.transaction(function (tx) {
                tx.executeSql("SELECT * FROM user_details", [], function (tx, data) {
                    var token = data.rows.item(0).token;
                    var request = $resource(URL_Config.BASE_URL + '/authCheck', {token: token },
                        { query: {
                            isArray: false,
                            method: 'GET'
                        } });
                    request.query(function (res) {
                        if (res.error) {
                            deferred.resolve(false);
                        } else {
                            deferred.resolve(true);
                        }
                    });

                }, function (e) {
                    deferred.resolve(false);
                });
            });
        }, 500);
        return deferred.promise;
    };

 return this;
 });

Route Configuration

This is the part I am struggling with. Where and how to inject this Service? I got it working but I am not sure whether this is the correct way to handle it.

  • Call the Service within the Resolve Property of $stateProvider?
  • based on AuthService's value redirect to view
  • Process before application starts.

myApp.config(function($stateProvider, $urlRouterProvider ) {
   $urlRouterProvider.otherwise('/home');
   $stateProvider
    .state('home', {
        url: '/home',
        templateUrl: 'templates/home.html',
        controller: 'MainController',
        resolve: ['AuthService', function(AuthService, $location, $q){
            var deferred = $q.defer();
            var authChk = AuthService.isAuthorized(); 
            authChk.then(function(data){
                if(!data){
                    $location.path('/register'); 
                    deferred.resolve();
                } else{
                    $location.path('/home');
                    deferred.resolve();
                }
            }, function(e){
                $location.path('/register');
                deferred.resolve();
            });
            return deferred.promise;
        }]
    })
    .state('register', {
        url: '/register',
        templateUrl: 'templates/register.html',
        controller  : 'RegisterController',
        resolve: ['AuthService', function(AuthService, $location, $q){
            var deferred = $q.defer();
            var authChk = AuthService.isAuthorized();
            authChk.then(function(data){
                if(!data){
                    $location.path('/register');
                    deferred.resolve();
                } else{
                    $location.path('/home');
                    deferred.resolve();
                }
            }, function(e){
                $location.path('/register');
                deferred.resolve();
            });
            return deferred.promise;
        }]
    })
});

Is this the proper place to run such Authentication validation?

This is a simple Hello World demo to get me started with Angular. I do not have navigation (multiple views) so the check should be straight forward.

I have seen many example using locationChangeStart to fire a service before a view is loaded. Then $scope.watch and $broadcast is used when handling sessions (also not the case) but might be useful for future uses.

Thanks. Any advise on how and where to handle this scenario is welcomed.


Solution

  • This is actually something that I had to do as well. You can use the resolve on each of the states.

    In the below code I have a resolveUserAuth function that will return the user object.

     var resolveUserAuth = {
        'userAuth': function (AuthService, $rootScope) {
          if ($rootScope.user) {
            return $rootScope.user;
          } else {
            return myApp._getUserAuth(AuthService, $rootScope);
          }
        }
      };
    
      $stateProvider
        .state('login', {
          url: '/login',
          templateUrl: 'views/login.html',
          controller: 'LoginCtrl'
        })
        .state('logout', {
          url: '/logout',
          templateUrl: 'views/login.html',
          controller: 'LoginCtrl'
        })
        .state('home', {
          url: '/home',
          templateUrl: 'views/home.html',
          controller: 'HomeCtrl',
          resolve: resolveUserAuth
        })
    

    getUserAuth

    myApp._getUserAuth = function (service, rootScope) {
      return service.getRole().then(function () {
        service.getUser().then(function (user) {
          rootScope.user = user;
        });
      });
    };
    

    login

     service.login = function (data) {
        var promise = $http.post('/auth/login', data).then(function (response) { // Success
          $rootScope.user = response.data;
          AssignedService.get({},
            function(assigned) {
              $rootScope.assigned = assigned;
              $rootScope.showLogin = false;
            }, function(error) {
              if (console && console.error) {
                console.error('Error getting assigned: ', error);
              }
            }
          );
          return response;
        }, function() { // Error
          $rootScope.showAlert('Unable to login, please try again', 'danger');
        });
    
        return promise;
      };