Search code examples
angularjsangularjs-service

Creating an AuthService to check if user is logged in


I have an AngularJS app which communicates with a Laravel PHP backend. Sending a GET request to /api/checkLogin will return { logged: false, username: undefined, id: undefined } if the user is not logged in, otherwise, it will return something like { logged: true, username: 'John', id: 123 }.

I am not too familiar with using AngularJS services, but I would like to set up a service called AuthService that can, well, perform my app's authentication services.

I would like to implement the following functions: AuthService.loggedIn, AuthService.isAdmin, AuthService.username, and AuthService.id.

I want these functions implemented in such a way that calling one will set the values for all the rest. For example, let's say I call AuthService.isAdmin. This function will check if isAdmin is set, if so, it will return the value of isAdmin. If isAdmin is not set, it will make an HTTP request to /api/checkLogin, set the values for loggedIn, isAdmin, username, and id, and then return the value of isAdmin. How can I accomplish this?

Here is the service I have tried putting together:

angular.module('myApp').factory('AuthService', ['$http', function($http) {

    var loggedIn;
    var isAdmin;
    var username;
    var id;

    var checkLogin = function() {
        if(loggedIn != undefined) {
            return loggedIn
        } else {
            setUserData(checkLogin);
        }
    }
    var checkAdmin = function() {
        if(isAdmin != undefined) {
            return isAdmin
        } else {
            setUserData(checkLogin);
        }
    }
    var returnUsername = function() {
        if(username != undefined) {
            return username
        } else {
            setUserData(checkLogin);
        }
    }
    var returnId = function() {
        if(id != undefined) {
            return id
        } else {
            setUserData(checkLogin);
        }
    }


    // Our function call which will set our loggedIn, isAdmin, username, and id values
    var setUserData = function(callback) {
        $http.get(baseURL+'/api/checkLogin').success(function(data) {
            loggedIn = data.logged;
            if(loggedIn) {
                isAdmin = data.is_admin;
                username = data.username;
                id = data.id;
            }
            callback();
        });
    }


    return {
        loggedIn: function() { return checkLogin(); },
        isAdmin: function() { return checkAdmin(); },
        username: function() { return returnUsername(); },
        id: function() { return returnId(); },
    }

}]);

Solution

  • It looks you want to use checkLogin as a callback, but instead of doing it the way you have it, return the promise back to checkLogin from setUserData. Then in checkLogin, create your own deferred to handle the results.

    You are acting on asynchronous logic by introducing the $http call, so checkLogin is going to need to return a promise in all cases:

    var checkLogin = function() {
    
            // Create a custom deferred
            var defer = $q.defer();
    
            if(loggedIn != undefined) {
                // Resolve your deferred with the value of logged in
                defer.resolve(loggedIn);
            } else {
                setUserData().then(function (data) {
                   console.log('Set user data returned successfully');
                   loggedIn = data.logged;
                   if(loggedIn) {
                      isAdmin = data.is_admin;
                      username = data.username;
                      id = data.id;
                      defer.resolve(loggedIn);
                   } else {
                      defer.reject();
                   }
                }, function () {
                   console.log('setUserData failed');
                   defer.reject();
                });
            }
    
            return defer.promise;
        }
    
    var setUserData = function() {
            return $http.get(baseURL+'/api/checkLogin');
        }
    

    AuthService.loggedIn() will now return a promise. You have to resolve the promise to get the value out of it:

    AuthService.loggedIn().then(function (data) { 
         console.log(data);
    });  
    

    The function passed to then above will be called when the promise is resolved with the value that the promise was resolved with. In this case, its the value of data.logged because thats what was passed to defer.resolve in your checkLogin function.

    Here's some reading on Promises:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

    http://www.html5rocks.com/en/tutorials/es6/promises/

    https://docs.angularjs.org/api/ng/service/$q