angularjsauthenticationangularjs-scopeangularjs-model

Angularjs model not updated when switching between users at login


I need the following functionality: When a user goes to the login form, the browser should auto fill the username and password.

My implementation works (on FF and Chrome) but, there is this bug (not consistent) where the model data does not get updated correctly when switching between users. This means that I log in with user ONE, then log out, and enter the credentials for user TWO, but after I click the login button, I'm still logged in with user ONE.

The login form looks like this:

<form class="form-horizontal" ng-submit="login(credentials)">
    <fieldset>
        <div class="form-group" ng-class="{'has-error' : validationErrors.email}">
            <div class="btn-icon-lined btn-icon-round btn-icon-sm btn-default-light">
                <span class="glyphicon glyphicon-envelope"></span>
            </div>
            <input type="email" name="email" autocomplete="on" ng-model="credentials.email" class="form-control input-lg input-round text-center" placeholder="Email" >
            <span class="help-block" ng-if="validationErrors.email">{{ validationErrors.email.0 }}</span>
        </div>
        <div class="form-group" ng-class="{'has-error' : validationErrors.password}">
            <div class="btn-icon-lined btn-icon-round btn-icon-sm btn-default-light">
                <span class="glyphicon glyphicon-lock"></span>
            </div>
            <input type="password" name="password" autocomplete="on" ng-model="credentials.password" class="form-control input-lg input-round text-center" placeholder="Password" >
            <span class="help-block" ng-if="validationErrors.password">{{ validationErrors.password.0 }}</span>
        </div>
        <div class="form-group">
            <input type="submit" value="Sign in" class="btn btn-primary btn-lg btn-round btn-block text-center">
        </div>
    </fieldset>
</form>

The login controller contains something like:

// Login Controller
app.controller( 'LoginCtrl', ['$rootScope','$scope', '$state', 'AppUser', 'Auth',
    function($rootScope, $scope, $state, AppUser, Auth){

    console.log("LoginCtrl");

    $scope.credentials = {
        "email" : "",
        "password": ""
    };

    $scope.redirectAfterLogin = function() {
        // set user data
        AppUser.setUserData($scope.user);
        ...
    }

    // Login attempt handler
    $scope.login =  function(data) {
        data = {'user':data};

        Auth.login(data,
            function(response) {
                $scope.user = response.data;

                ...

            },function(response){
                $scope.validationErrors = {
                    "email" : [],
                    "password": []
                };
                ...
            }

        );
    };

}]);

Logout:

// logout
$scope.logout  = function() {
    // remove attempted URL, if any
    AppUser.removeAttemptUrl();

    data =  {'user':
        {
            'email': $scope.user.email
        }
    };
    Auth.logout(data,
        function(){
            AppUser.unSetUserData($scope.user); // se method below
            $state.go(ApplicationState.LOGIN);
        },
        function(){
            console.log("Logout failed");
     });
}

angular.module('app.service').factory('AppUser', [
'$window', '$rootScope', 'LocalStorage', 'appConfig', '$injector', '$location',
function($window, $rootScope, localStorage, appConfig, $injector, $location){

// Redirect to the original requested page after login
var redirectToUrlAfterLogin = { url: '' };
var userKey = "AppUser";
var userData = {};

angular.element($window).on('storage', function(event) {
    if (event.key === userKey) {
        $rootScope.$apply();
    }
});

return {

    /**
     * Redirect to the original requested page after login
     * - we need to be able to save the intended URL, request it, remove it and redirect to it
     */
    saveAttemptUrl: function() {
        if ($location.path().toLowerCase() != ApplicationState.LOGIN) {
            redirectToUrlAfterLogin.url = $location.path();
        }
        else {
            redirectToUrlAfterLogin.url = '/';
        }
    },
    getAttemptedUrl: function() {
        return redirectToUrlAfterLogin.url;
    },
    removeAttemptUrl: function() {
        // re-initialize URL
        redirectToUrlAfterLogin = { url: '' };
    },
    redirectToAttemptedUrl: function() {
        $location.path(redirectToUrlAfterLogin.url);
    },

    /**
     * Returns the current user's state
     * @returns {boolean}
     */
    isAuthenticated: function() {
        userData = JSON.parse(localStorage.get(userKey) || '{}').userData;

        if (!this._isSessionExpired()) {
            if (userData !== undefined){
                return !!(userData.id !== null && userData.email);
            }
            else{
                return false;
            }
        }
        else{
            if (userData !== undefined){
                var data =  {
                    'user':{
                        'email': userData.email
                    }
                }

                // we use $injector to avoid Circular Dependency which is thrown by injecting the $api service
                $injector.invoke(['$api', function($api){
                    $api.auth.logout(data).success(function(result){
                        userData = {};
                        localStorage.remove(userKey);
                    });
                }]);
                return false;
            }
        }
    },

    getUserData: function() {
        return userData;
    },

    setUserData: function(data) {
        userData = data;
        localStorage.set(userKey, JSON.stringify({
            userData: data,
            stamp: Date.now()
        }));
    },

    unSetUserData: function() {
        userData = {};

        localStorage.remove(userKey);
    },

    _isSessionExpired: function() {
        var session = JSON.parse(localStorage.get(userKey) || '{}');
        return (Date.now() - (session.stamp || 0)) > appConfig.sessionTimeout;
    },

    userData : userData
};

}] );

Any ideas on why this is happening?


Solution

  • After you logout check the localStorage with the browser inspector.
    Probably you will find some variable that you didn't clear. So just clear the storage and it should be fine.

    To clear the storage use:

    localStorage.clear();
    

    One additional problem it could be you didn't clean the $rootScope if you didn't refresh all the data are still in there.