2b2a
2b2a

Reputation: 95

Use local storage to store AngularJS data

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;
    }]);

Upvotes: 2

Views: 1370

Answers (2)

MBielski
MBielski

Reputation: 6620

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.

Upvotes: 1

rom99
rom99

Reputation: 799

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.

Upvotes: 2

Related Questions