How to redirect users with unverified emails using Angular UI-Router?

All we need is an easy explanation of the problem, so here it is.

I am using AngularJS with Meteor and wanted to redirect users with unverified emails to the sign in page. I have created a sign in view in /client/routes.js:

app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
  $urlRouterProvider.otherwise('/');

  $stateProvider

  .state('signin', {
    url:'/signin',
    views: {
      main: {
        templateUrl: 'client/views/profile/signin.tpl'
      }
    }
  })

Note that there are other states I am not listing for brevity sake.

Now, I want to redirect users to this sign in page if their emails have not been verified. How do I modify the example below from UI-Router FAQs to meet my needs? Other solutions not using the example below are acceptable to me as long as they address the issue at hand.

Example: Uses the data object on the state config to define a rule
function that will run logic against the user (here using an example
service called $currentUser). The $stateChangeStart handler catches
all state transition and performs this rule check before allowing the
transition, potentially blocking it and/or redirecting to a different
state.

app.config(function($stateProvider) {
  $stateProvider.state('privatePage', {
    data: {
      rule: function(user) {
        // ...
      }
  });
});
app.run(function($rootScope, $state, $currentUser) {
  $rootScope.$on('$stateChangeStart', function(e, to) {
    if (!angular.isFunction(to.data.rule)) return;
    var result = to.data.rule($currentUser);

    if (result && result.to) {
      e.preventDefault();
      // Optionally set option.notify to false if you don't want 
      // to retrigger another $stateChangeStart event
      $state.go(result.to, result.params, {notify: false});
    }
  });
});

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

The example from the FAQ attempts to create a general way to add a rule to any page. Let’s keep it simple:

app.run(function($rootScope, $state, UserService) {
    $rootScope.$on('$stateChangeStart', function(event, toState) {
        // don't check auth on login routes
        if (["signin"].indexOf(toState.name) === -1) {
            if (UserService.doesNotHaveVerifiedEmail()) {
                event.preventDefault();
                $state.go('signin');
                return;
            }
        }
    }
}); 

Anytime a state is loaded and it’s not the signin state, you check if the user is verified (depends on your application, here I am injecting a UserService which I assume has knowledge about the user’s status) and if not, you prevent that state change and redirect them to the signin page.

Method 2

You can use the resolve functionality provided by angular-ui-router to check email verification of current user before the state is resolved. Here’s how the code will look like:

app.config(['$stateProvider', '$urlRouterProvider',
    function($stateProvider, $urlRouterProvider) {

        var isVerified = ['User', '$state', '$q',
            function(User, $state, $q) {
                var d = $q.defer();
                var loginPromise = User.getVerificationStatus();
                loginPromise.then(
                    function(response) {
                        d.resolve(response);
                    },
                    function(error) {
                        $state.go('login');
                    });
                return d.promise;
            }
        ];

        $urlRouterProvider.otherwise('/');

        $stateProvider

            .state('signin', {
                url: '/signin',
                views: {
                    main: {
                        templateUrl: 'client/views/profile/signin.tpl'
                    }
                }
            })
            .state('home', {
                url: '/home',
                views: {
                    main: {
                        templateUrl: 'client/views/profile/home.tpl'
                    }
                },
                resolve: {
                    verified: isVerified
                }
            });
    }
]);

Here home state checks for the verification before resolving. I have injected a service User which will arrange the information whether user is verified or not.

You can add resolve property to only those states where you want to check verification status. In this way this is better than checking on $stateChangeStart event which will be fired every time state changes irrespective of whether this check is needed or not.

Here’s the link to the documentation.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply