2

I am currently using $rootScope to store user information and whether or not the user is logged in. I have tried using $window.localStorage, but with no success. My goal is to have items in my navbar appear through an ng-show once a user is logged on, have their username appear in the navbar, individual user profile view, all users view, etc. I need a persistent login. I have the navbar working with $rootscope, but whenever I try and transition over to $window.localStorage, it fails. Here is the code using $rootScope:

mainModule

angular.module('mainModule', [
        'ui.router',
         ...
    ])
    .config(configFunction)
    .run(['$rootScope', '$state', 'Auth', function($rootScope, $state, Auth) {
        $rootScope.$on('$stateChangeStart', function(event, next) {
            if (next.requireAuth && !Auth.getAuthStatus()) {
                console.log('DENY');
                event.preventDefault();
                $state.go('login');
            } else if (Auth.getAuthStatus() || !Auth.getAuthStatus()) {
                console.log('ALLOW');
            }
        });
    }]);

Auth Factory

angular.module('authModule').factory('Auth', ['$http', '$state', function authFactory($http, $state) {
        var factory = {};

        var loggedIn = false;
        var userData = {};

        factory.getAuthStatus = function() {
            $http.get('/api/v1/auth')
                .success(function(data) {
                    if (data.status == true) {
                        loggedIn = true;
                    } else {
                        loggedIn = false;
                    }
                })
                .error(function(error) {
                    console.log(error);
                    loggedIn = false;
                });
            return loggedIn;
        }

        return factory;
    }]);

Login Controller

function SigninController($scope, $rootScope, $http, $state) {
    $scope.userData = {};

    $scope.loginUser = function() {
        $http.post('api/v1/login', $scope.userData)
            .success((data) => {
                $scope.userData = data.data;
                $rootScope.loggedIn = true;
                $rootScope.userData = data;
                $state.go('home');
            })
            .error((error) => {
                console.log('Error: ' + error);
            });
    };
}

Nav Controller

function NavbarController($scope, Auth) {
    $scope.loggedIn = Auth.getAuthStatus();
}

EDIT EDIT EDIT

Here is how I am using local storage. These are the only things that changed.

Login Controller

function SigninController($scope, $window, $http, $state) {
    $scope.userData = {};

    $scope.loginUser = function() {
        $http.post('api/v1/login', $scope.userData)
            .success((data) => {
                $scope.userData = data.data;
                $window.localStorage.setItem('userData', angular.toJson(data));
                $window.localStorage.setItem('loggedIn', true);
                $state.go('home');
            })
            .error((error) => {
                console.log('Error: ' + error);
            });
    };
}

Auth Factory

angular
    .module('authModule')
    .factory('Auth', ['$http', '$window', '$state', function authFactory($http, $window, $state) {
        var factory = {};

        factory.getAuthStatus = function() {
            $http.get('/api/v1/auth')
                .success(function(data) {
                    if (data.status == true) {
                        $window.localStorage.setItem('loggedIn', true);
                    } else {
                        $window.localStorage.setItem('loggedIn', false);
                    }
                })
                .error(function(error) {
                    console.log(error);
                    $window.localStorage.setItem('loggedIn', false);
                });
            return $window.localStorage.getItem('loggedIn');
        }

        return factory;
    }]);
5
  • 2
    Could you show your attempt that is not working? Also, use localStorage, not $window.localStorage. Also, localStorage stores only strings, so you will want to use JSON.stringify() and JSON.parse() to store/retrieve your objects in localStorage Commented Nov 15, 2016 at 20:33
  • @holtc Yes, give me a few minutes! Thank you! Commented Nov 15, 2016 at 20:39
  • @holtc It is up! Could you provide an explanation on why not to use $window.localStorage? My example uses it because that is what I used before. Commented Nov 15, 2016 at 21:15
  • I would side with @2b2a and not use $window.localStorage. I've never had good results with it, and in general wrapping something like window (which is a global variable) just to have it in Angular (which refers to that global variable) just seems like a waste. Commented Nov 15, 2016 at 21:23
  • yeah, make sure to JSON-ify the booleans too. And to be honest, you don't really need the booleans at all, just check for the presence of userData, and when you log out, make sure to remove userData from localStorage Commented Nov 15, 2016 at 21:44

2 Answers 2

2

I see a potential problem with your use of localStorage.getItem('loggedIn').

Because localStorage only stores strings, what you get back is actually a stringified version of the boolean that you put in. If the string 'false' gets returned, your check of !Auth.getAuthStatus() in main module for example will always evaluate to boolean false because any non-empty string in JavaScript is "truthy".

i.e. !'false' === false (the same as !true === false)

You can get over this by using JSON.parse on the value in localStorage. e.g. JSON.parse(localStorage.getItem('loggedIn')) would parse the string 'false' to the Boolean false.

Sign up to request clarification or add additional context in comments.

Comments

1

Simply replace $window.localStorage with window.localStorage and you should be fine.

For example:

function SigninController($scope, $window, $http, $state) {
    $scope.userData = {};

    $scope.loginUser = function() {
        $http.post('api/v1/login', $scope.userData)
            .success((data) => {
                $scope.userData = data.data;
                window.localStorage.setItem('userData', angular.toJson(data));
                window.localStorage.setItem('loggedIn', true);
                $state.go('home');
            })
            .error((error) => {
                console.log('Error: ' + error);
            });
    };
}

This being said, storing authenticated status in localStorage (or sessionStorage) is not a good path to go down. Both key/value pairs can be read in the developer pane and then altered (aka spoofed) via the console. A better solution is to return a unique value (GUID) after a successful login and store it in a cookie (set to expire in a short amount of time, like 20 minutes) that can be read on the server and verified there. You can and should use $cookie for this. Your user login state should be controlled server-side, never client-side. The client should always have to prove that it is authenticated.

To persist login, create a service that handles your visitor and let that service handle the login/logout and provide the proof of being logged in. That proof of being logged in should always be a private value that is held internally by the service and not accessible outside of it.

(function () {
    'use strict';

    var visitorModelService = ['$http', function ($http) {
            var loggedIn = false,
                visitorModel = {
                    login:function(){
                        //do login stuff with $http here
                        //set loggedIn to true upon success
                    },
                    loggedIn:function(){
                        return loggedIn;
                    },
                    logout:function(){
                        //do logout stuff with $http here
                        //no matter what, set loggedIn to false
                    }
            };
            return visitorModel;
        }];

    var module = angular.module('models.VisitorModel', []);
    module.factory('VisitorModel', visitorModelService);
}());

Doing this, you can simply check for visitor.loggedIn in your ng-show and have everything centralized. Such as:

<a ng-click='visitor.logout' ng-show='visitor.loggedIn'>Log Out</a>

Better yet, put the elements that are only visible to authenticated users in a div tag and hide/show them en-mass.

Comments

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.