Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - Checking if a user is authenticated and then routing them to the correct page

What I am looking to do I when a user comes to the index.html page I need the login module to do 2 things:

  1. I need this to check if a user is authenticated ( which I think I already started with the "function authService" ) if the user has a valid token then change the ui-view to dashboard/dashboard.html and if the key is not valid or there is no key at all then load login/login.html into ui-view.

  2. Once they have successfully logged in I want them to be routed to "dashboard/dashboard.html"

Here is my login script:

function authInterceptor(API) {
  return {
    request: function(config) {
      if(config.url.indexOf(API) === 0) {
        request.headers = request.headers || {};
        request.headers['X-PCC-API-TOKEN'] = localStorage.getItem('token');
      }
      return config;
    }
  } 
}

function authService(auth) {
  var self = this;
  self.isAuthed = function() {
    localStorage.getItem('token');
  }
}

function userService($http, API) {

  $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;';
  $http.defaults.transformRequest = [function(data) {
      return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
    }];

  var self = this;

  self.login = function(username, pwd, ctrl) {
    ctrl.requestdata = API + '/winauth' + '; with ' + username;
    return $http.post(API + '/winauth', {
        username: username,
        pwd: pwd
      })
  };

  var param = function(obj) {
    var query = '', name, value, fullSubName, subName, subValue, innerObj, i;

    for(name in obj) {
      value = obj[name];

      if(value instanceof Array) {
        for(i=0; i<value.length; ++i) {
          subValue = value[i];
          fullSubName = name + '[' + i + ']';
          innerObj = {};
          innerObj[fullSubName] = subValue;
          query += param(innerObj) + '&';
        }
      }
      else if(value instanceof Object) {
        for(subName in value) {
          subValue = value[subName];
          fullSubName = name + '[' + subName + ']';
          innerObj = {};
          innerObj[fullSubName] = subValue;
          query += param(innerObj) + '&';
        }
      }
      else if(value !== undefined && value !== null)
        query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
    }

    return query.length ? query.substr(0, query.length - 1) : query;
  };

}

function LoginCtrl(user) {
  var self = this;

  function handleRequest(res) {
    self.responsedata = res;
    self.message = res.data.message;

    var authToken = res.data.auth_token;
    localStorage.setItem('token', authToken);
  }

  self.login = function() {
    this.requestdata = 'Starting request...';
    user.login(self.username, self.pwd, self)
      .then(handleRequest, handleRequest)
  }
}

// Login Module
var login = angular.module('login', ["ui.router"])

login.factory('authInterceptor', authInterceptor)
login.service('user', userService)
login.service('auth', authService)
login.constant('API', 'http://myserver.com/api')

EDIT - I added this into my login controller to provide the login routes

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

  $httpProvider.interceptors.push('authInterceptor');

  $urlRouterProvider.otherwise('/login');

  $stateProvider
  // HOME STATES AND NESTED VIEWS ========================================
  .state('login', {
    url: '/login',
    templateUrl: 'login/login.html',
    controller: "mainLogin",
    controllerAs: "log"
  })           
  // nested list with just some random string data
  .state('dashboard', {
    url: '/dashboard',
    templateUrl: 'dashboard/dashboard.html',
  })

})

login.controller('mainLogin', LoginCtrl)

Here is my index.html:

EDIT - I removed "ng-include" and added "ng-view" to control the routes.

<body ng-app="login" ng-controller="mainLogin as log" class="loginPage">

  <div class="main" ui-view></div>

</body>

As you can see I have a function that is checking for the token in the users local storage:

function authService(auth) {
  var self = this;
  self.isAuthed = function() {
    localStorage.getItem('token');
  }
}

And I am loading it in the module as a service:

login.service('auth', authService)

This is where I am stuck. I don't know where to go from here. I don't even know if I am using my authService function properly. I am still learning a lot about AngularJS so its easy for me to get stuck. :)

Another thing you will notice is in my index.html file I am just loading the "login/login.html" partial as default. I need it to load either login.html or dashboard.html depending if they are logged in or not. And then also route them to dashboard.html once they have successfully logged in.

The script works great as far as hitting the auth API, authenticating the user and then storing a valid auth key on their local storage.

Anyone know how I can accomplish this?

like image 981
agon024 Avatar asked Jul 28 '16 22:07

agon024


3 Answers

There are two separate concerns that you are dealing with. The first, is to be able to determine if you are logged in. Assuming the user needs to be logged in for any state except the login state, you would implement it like so by listening for $stateChangeState events and verifying that the user is logged in:

login.run(function($state, authService) {
    $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
        var authToken = authService.isAuthed();
        if (!authToken && toState !== 'login') {
            //not logged in, so redirect to the login view instead of the view
            //the user was attempting to load
            event.preventDefault();
            $state.go('login');

        } 
    })
});

This will put them on the login state if they haven't already logged in.

The second part is to redirect to the correct view after they login, which you would do in your login controller:

function LoginCtrl(user, $state) {
    var self = this;

    function handleRequest(res) {
        self.responsedata = res;
        self.message = res.data.message;

        var authToken = res.data.auth_token;
        localStorage.setItem('token', authToken);

        //after successful login, redirect to dashboard
        $state.go('dashboard');
    }

    self.login = function() {
        this.requestdata = 'Starting request...';
        user.login(self.username, self.pwd, self)
                .then(handleRequest, handleRequest)
    }
}
like image 150
mcgraphix Avatar answered Oct 24 '22 09:10

mcgraphix


ok I see you are using ui.router so let's work within this framework.

You want to

  1. check if a user is logged in
  2. redirect user to a view

What you're looking for is resolve:{loggedIn: checkLoggedInFn}

so your route for dashboard could be something like

.state('dashboard', {
 url: '/dashboard',
 templateUrl: 'dashboard/dashboard.html',
 resolve: {
    loggedIn: function(){
       //do your checking here
    }
  }
})

what this does basically is that the controller will not instantiate until every resolve is resolved (so you can use a promise here for example), and then the value is passed into the controller as a parameter, so you could then do something like:

if(!loggedIn){
   $state.go('login');
}
like image 2
Dacheng Avatar answered Oct 24 '22 09:10

Dacheng


You would handle the logic inside your login controller specifically here:

 self.login = function() {
    this.requestdata = 'Starting request...';
    user.login(self.username, self.pwd, self)
      .then(handleRequest, handleRequest)
  }

Inside the callback for the success on login, you would simple do a state change to send the user to the correct state.

like image 1
Garuuk Avatar answered Oct 24 '22 10:10

Garuuk