0

I have an angularjs application that is using $transitions to check if a user is authenticated before certain $state changes. I am using passport.js to do the authentication strategy. If authentication has not occurred or fails the AuthisLoggedIn() check I want the user to be reverted back to the 'login' state. The issue I am having is that the default state '/' always seems to load before my authentication promise, Auth.getCurrentUserAsync() is done executing causing a bunch of issues. How can I enforce that no state gets loaded while waiting for this authentication check and then send the user to my 'login' state if they in all cases when they are not authenticated. My code is as follows

app.js

clientApp.run(['$transitions', '$state', '$http', 'Auth', '$rootScope',
    function($transitions, $state, $http, Auth, $rootScope) {
        $http.defaults.xsrfHeaderName = 'X-CSRFToken'
        $http.defaults.xsrfCookieName = 'csrftoken'


        $rootScope.baseApiUrl='/app/api'

        //verify the user
        $transitions.onStart(
        {
            to:  function(state) {
                if (typeof state.authenticate !== 'undefined' && state.authenticate === false) {
                    return // this state does not require authentication
                }
                if (!Auth.isLoggedIn()) {

                    Auth.getCurrentUserAsync().then(
                        function() {
                            if (!Auth.isLoggedIn()) {
                                $state.go('login')
                            }
                        }
                    )
                }
            }
        })
}])
clientApp.config(['$urlRouterProvider', '$stateProvider', '$locationProvider',
    function($urlRouterProvider, $stateProvider, $locationProvider) {
        $urlRouterProvider
            .otherwise('/')
        $stateProvider
            .state('main', {
                url: '/',
                component: 'main',
                authenticate: true
            })
            .state('login', {
                url: '/login',
                template: loginTemplate,
                controller: 'LoginCtrl',
                controllerAs: 'Login',
                authenticate: false,
                params: {
                    loginMessage: "",
                    loginErrorMessage: "",
                    username: "",
                    password: undefined,
                    errors: []
                }, 
            })
        $locationProvider.html5Mode(true)
}])    

auth.js

import angular from 'angular'

angular
    .module('app.factories.auth', [])
    .factory('Auth',
        ['$http', '$q', '$rootScope',
            function Auth($http, $q, $rootScope) {

                function _isLoggedIn() {
                    if (Object.keys(authObj.user).length !== 0) {
                        return true
                    } else {
                        return false
                    }
                }

                function _login(user) {
                    const deferred = $q.defer()

                    $http.post($rootScope.baseApiUrl + '/user/login', {
                        username: user.username,
                        password: user.password
                    })
                        .then(
                            function(response) {
                                if (response.status === 200) {
                                    authObj.user = response.data.user
                                    deferred.resolve(response)
                                } else {
                                    authObj.user = {}
                                    deferred.reject(response)
                                }
                            },
                            function(err) {
                                authObj.user = {}
                                deferred.reject(err)
                            }
                        )

                    return deferred.promise
                }


                function _getCurrentUserAsync() {
                    return $http.get($rootScope.baseApiUrl + '/user/status')
                        .then(
                            function(response) {
                                console.log('response', response)
                                if (response.data.status) {
                                    authObj.user = response.data.user
                                } else {
                                    authObj.user = {}
                                }
                            },
                            function() {
                                authObj.user = {}
                            })
                }

                const authObj = {
                    isLoggedIn: _isLoggedIn,
                    getCurrentUserAsync: _getCurrentUserAsync,
                    login: _login,
                    user: {}
                }

                return authObj
            }
        ])
georgeawg
  • 48,608
  • 13
  • 72
  • 95
medium
  • 4,136
  • 16
  • 55
  • 66
  • Avoid using the [deferred Anti-Pattern](https://stackoverflow.com/questions/30750207/is-this-a-deferred-antipattern). – georgeawg Jun 14 '18 at 20:33
  • See [You're Missing the Point of Promises](https://blog.domenic.me/youre-missing-the-point-of-promises/). – georgeawg Jun 14 '18 at 20:37
  • Make one promise wait for another by [chaining](https://docs.angularjs.org/api/ng/service/$q#chaining-promises) them. – georgeawg Jun 14 '18 at 20:40
  • See [Solution 2: using state `resolve`](https://stackoverflow.com/a/28267504/5535245) – georgeawg Jun 15 '18 at 17:36

0 Answers0