Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

An AngularJS service is injected into two separate modules. This is causing the service to re-initialize separately when the second module calls it. I have used the FireFox debugger to confirm that the module is being re-initialized. How can I avoid this problem?

Here is the specific case:

An AngularJS app uses an authentication service in a module called auth to manage authentication concerns. The auth service is imported into a message module which manages access to the secure /message route and auth is also imported into a navigation module which manages both login/registration and also the visible contents of the navigation links in the user's browser. A user is able to successfully login using the GUI tools linked to the navigation module, and is then successfully redirected to the secure /message route as an authenticated user because the auth.authenticated1 and auth.authenticated2 properties are set to true just before the redirect to /message occurs.

However, the FireFox debugger confirms that the problem is that, when the user refreshes the browser to reload the /message url pattern, the auth module is re-initialized, setting the values of auth.authenticated1 and auth.authenticated2 back to false, and thus giving the user a message that they are not logged in, even though they were logged in a moment before using valid credentials provided by the user. What specific changes need to be made to the code below so that the user is NOT logged out on page re-load?

I want the AngularJS code to check the pre-existing value for auth.authenticated2 when the /message route is loaded or reloaded. If auth.authenticated2=false, then the user gets a message saying they are logged out. But if auth.authenticated2=true, I want the user to be able to see the secure content at the /message route. I DO NOT want auth.authenticated2 to be automatically re-set to false upon reloading the route, the way it is now.

Here is the code in message.html which contains the GUI elements for the /message route:

<div ng-show="authenticated2()">
    <h1>Secure Content</h1>
    <div>
        <p>Secure content served up from the server using REST apis for authenticated users.</p>
    </div>
</div>
<div ng-show="!authenticated2()">
    <h1>You are not logged in.</h1>
</div>

Here is the code in message.js which is the controller for the message module that manages the /message route:

angular.module('message', ['auth']).controller('message', function($scope, $http, $sce, auth) {

    $scope.authenticated2 = function() {
        return auth.authenticated2;
    }

    //Other code calling REST apis from the server omitted here to stay on topic

});

Here is the code for the navigation module, which also injects the auth service:

angular.module('navigation', ['ngRoute', 'auth']).controller('navigation', function($scope, $route, auth, $http, $routeParams, $location) {

    $scope.credentials = {};//from old navigation module
    $scope.leadresult = "blank";
    $scope.processStep = "start";
    $scope.uname = "blank";
    $scope.wleadid = "initial blank value";
    $scope.existing = "blank";

    $scope.tab = function(route) {
        return $route.current && route === $route.current.controller;
    };

    $scope.authenticated1 = function() {
        return auth.authenticated1;
    }

    $scope.authenticated2 = function() {
        return auth.authenticated2;
    }

    $scope.login = function() {
        auth.authenticate1($scope.credentials, function(authenticated1) {
            //a bunch of stuff that does level 1 authentication, which is not relevant here
        })
    }

    $scope.logout = auth.clear;

    //some other methods to manage registration forms in a user registration process, which are omitted here because they are off-topic

    $scope.pinForm = function(isValid) {//this method finishes authentication of user at login
        if (isValid) {
            $scope.resultmessage.webleadid = $scope.wleadid;
            $scope.resultmessage.name = $scope.uname;
            $scope.resultmessage.existing = $scope.existing;
            var funcJSON = $scope.resultmessage;        
            auth.authenticate2(funcJSON, function(authenticated2) {
                if (authenticated2) {
                    $location.path('/message');
                    $scope.$apply();//this line successfully re-directs user to `/message` route LOGGED IN with valid credentials
                }
            });
        }
    };

    $scope.$on('$viewContentLoaded', function() {
        //method that makes an unrelated call to a REST service for ANONYMOUS users
    });

});

Here is the code for the auth service in auth.js:

angular.module('auth', []).factory( 'auth', function($rootScope, $http, $location) {

    var auth = {

        authenticated1 : false,
        authenticated2 : false,
        usrname : '',

        loginPath : '/login',
        logoutPath : '/logout',
        homePath : '/message',
        path : $location.path(),

        authenticate1 : function(credentials, callback) {
            var headers = credentials && credentials.username ? {
                authorization : "Basic " + btoa(credentials.username + ":" + credentials.password)
            } : {};

            $http.get('user', {
                headers : headers
            }).success(function(data) {
                if (data.name) { auth.authenticated1 = true; } 
                else { auth.authenticated1 = false; }
                callback && callback(auth.authenticated1);
            }).error(function() {
                auth.authenticated1 = false;
                callback && callback(false);
            });
        },

        authenticate2 : function(funcJSON, callback) {
            $http.post('/check-pin', funcJSON).then(function(response) {
                if(response.data.content=='pinsuccess'){
                    auth.authenticated2=true;
                    callback && callback(auth.authenticated2);
                }else {
                    auth.authenticated2=false;
                    auth.authenticated2 = false;
                    callback && callback(false);
                }
            });
        },

        clear : function() {
            $location.path(auth.loginPath);
            auth.authenticated1 = false;
            auth.authenticated2 = false;
            $http.post(auth.logoutPath, {}).success(function() { console.log("Logout succeeded");
            }).error(function(data) { console.log("Logout failed"); });
        },

        init : function(homePath, loginPath, logoutPath) {
            auth.homePath = homePath;
            auth.loginPath = loginPath;
            auth.logoutPath = logoutPath;
        }
    };
    return auth;
});

The routeProvider is managed by the main js file for the app, which is hello.js and is as follows:

angular.module('hello', [ 'ngRoute', 'auth', 'home', 'message', 'public1', 'navigation' ])
    .config(

        function($routeProvider, $httpProvider, $locationProvider) {

            $locationProvider.html5Mode(true);/* This line is one of 3 places where we set natural urls to remote the default # */

            $routeProvider.when('/', {
                templateUrl : 'js/home/home.html',
                controller : 'home'
            }).when('/message', {
                templateUrl : 'js/message/message.html',
                controller : 'message'
            }).when('/public1', {
                templateUrl : 'js/public1/public1.html',
                controller : 'public1'
            }).when('/register', {
                templateUrl : 'js/navigation/register.html',
                controller : 'navigation'
            }).otherwise('/');

            $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

        }).run(function(auth) {

            // Initialize auth module with the home page and login/logout path
            // respectively
            auth.init('/checkpin', '/login', '/logout');

        });
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
125 views
Welcome To Ask or Share your Answers For Others

1 Answer

Not a complete answer as in, exact code to change here. But enough to get something built nicely.

You shouldn't explicitly use only boolean values inside your auth service. As far as I can see, you're not using any data that you're receiving from the server after a successful authentication. So whenever you refresh, everything is lost. You don't have anything in your code to recover from a refresh.

Ideally, you should have something like a token or a cookie. The cookie will be saved and can be recovered after a refresh, so while starting the auth service, you can check for the existence of that cookie.

You could also save a token that can be used to access an API inside an indexedDB or something like that. Just as I said before, during boot time of the auth service, you'll have to check for the existence of that token.

If you need more information, check for Oauth2. Even though Oauth2 isn't an Authentication API but an authorization API, the password grant type is something you could use. To get an idea how to build a solid system. The other grant types are mostly only focused on the authorization side of OAuth.

Because OP ask for code here it is:

when creating_service:
  if exists(cookie) or exists(token) and valid(cookie) or valid(token):
     mark_service_as_logged_in()

If pseudo code is better than words.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...