javascriptruby-on-railsangularjsangularjs-authentication

AngularJS- Login and Authentication in each route and controller


I have an AngularJS application created by using yeoman, grunt and bower.

I have a login page that has a controller that checks for authentication. If the credentials are correct I reroute to home page.

app.js

'use strict';
//Define Routing for app
angular.module('myApp', []).config(['$routeProvider', '$locationProvider',
  function($routeProvider,$locationProvider) {
    $routeProvider
    .when('/login', {
        templateUrl: 'login.html',
        controller: 'LoginController'
    })
    .when('/register', {
        templateUrl: 'register.html',
        controller: 'RegisterController'
      })
    .when('/forgotPassword', {
        templateUrl: 'forgotpassword.html',
        controller: 'forgotController'
      })
   .when('/home', {
       templateUrl: 'views/home.html',
       controller: 'homeController'
    })
    .otherwise({
       redirectTo: '/login'
    });
//    $locationProvider.html5Mode(true); //Remove the '#' from URL.
}]);

angular.module('myApp').factory("page", function($rootScope){
    var page={};
    var user={};
    page.setPage=function(title,bodyClass){
        $rootScope.pageTitle = title;
        $rootScope.bodylayout=bodyClass;
    };
    page.setUser=function(user){
        $rootScope.user=user;
    }
    return page;
});

LoginControler.js

'use strict';

angular.module('myApp').controller('LoginController', function($scope, $location, $window,page) {
    page.setPage("Login","login-layout");
    $scope.user = {};
    $scope.loginUser=function()
    {
        var username=$scope.user.name;
        var password=$scope.user.password;
        if(username=="admin" && password=="admin123")
        {
            page.setUser($scope.user);
            $location.path( "/home" );
        }
        else
        {
            $scope.message="Error";
            $scope.messagecolor="alert alert-danger";
        }
    }
});

On the home page I have

<span class="user-info">
    <small>Welcome,</small>
    {{user.name}}
</span>
<span class="logout"><a href="" ng-click="logoutUser()">Logout</a></span>

In the loginController, I check the login info and if it's successful, I set the user object in the service factory. I don't know whether this is correct or not.

What I need is, When the user is logged in, It sets some value in the user object so that all other pages can get that value.

Whenever any route changes happen, the controller should check if the user is logged in or not. If not, it should reroute to the login page. Also, if the user is already logged in and come back to the page, it should go to home page. The controller should also check the credentials on all of the routes.

I have heard about ng-cookies, but I don't know how to use them.

Many of the examples I saw were not very clear and they use some kind of access roles or something. I don't want that. I only want a login filter. Can someone give me some ideas?


Solution

  • My solution breaks down in 3 parts: the state of the user is stored in a service, in the run method you watch when the route changes and you check if the user is allowed to access the requested page, in your main controller you watch if the state of the user change.

    app.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {
        $rootScope.$on('$routeChangeStart', function (event) {
    
            if (!Auth.isLoggedIn()) {
                console.log('DENY');
                event.preventDefault();
                $location.path('/login');
            }
            else {
                console.log('ALLOW');
                $location.path('/home');
            }
        });
    }]);
    

    You should create a service (I will name it Auth) which will handle the user object and have a method to know if the user is logged or not.

    service:

     .factory('Auth', function(){
    var user;
    
    return{
        setUser : function(aUser){
            user = aUser;
        },
        isLoggedIn : function(){
            return(user)? user : false;
        }
      }
    })
    

    From your app.run, you should listen the $routeChangeStart event. When the route will change, it will check if the user is logged (the isLoggedIn method should handle it). It won't load the requested route if the user is not logged and it will redirect the user to the right page (in your case login).

    The loginController should be used in your login page to handle login. It should just interract with the Auth service and set the user as logged or not.

    loginController:

    .controller('loginCtrl', [ '$scope', 'Auth', function ($scope, Auth) {
      //submit
      $scope.login = function () {
        // Ask to the server, do your job and THEN set the user
    
        Auth.setUser(user); //Update the state of the user in the app
      };
    }])
    

    From your main controller, you could listen if the user state change and react with a redirection.

    .controller('mainCtrl', ['$scope', 'Auth', '$location', function ($scope, Auth, $location) {
    
      $scope.$watch(Auth.isLoggedIn, function (value, oldValue) {
    
        if(!value && oldValue) {
          console.log("Disconnect");
          $location.path('/login');
        }
    
        if(value) {
          console.log("Connect");
          //Do something when the user is connected
        }
    
      }, true);