angular-ui / ui-router

The de-facto solution to flexible routing with nested views in AngularJS
http://ui-router.github.io/
MIT License
13.53k stars 3k forks source link

using onBefore/onStart for authentication problem #3289

Closed 29er closed 7 years ago

29er commented 7 years ago

Hi, The docs don't really cover this scenario and I'm having issues getting this to work in the latest ui-router version ( "angular-ui-router": "1.0.0-beta.3" ). upon any route change, i need to make sure that a user is authenticated. i don't want to reroute them to a 'login' state, I just want to make an API call which populates cookie, and then only after that call returns, Do I want the app to be loaded and routed to the intended state. this current implementation does not wait for the doAuth function to finish it's callback, therefore I have components being loaded and getting login errors etc.

 $transitions.onBefore({ }, () => {
            const deferred = $q.defer();
            session.checkAuth().then(() => {
                deferred.resolve(true)
            }, () => {
                deferred.resolve(false)
            });
            return deferred.promise;
        }, {priority: 10});

I know this implementation sucks so far, but can someone provide a little bit of guidance ? the repo at https://github.com/ui-router/sample-app-ng1-to-ng2 does not cover this type of scenario. it just redirects to a 'login' state.

Thanks

christopherthielen commented 7 years ago

The docs for HookResult explain how promises work when returned from a hook:

Promise: the transition will wait for the promise to resolve or reject

  • If the promise is rejected (or resolves to false), the transition will be cancelled
  • If the promise resolves to a TargetState, the transition will be redirected
  • If the promise resolves to anything else, the transition will resume

In your case, your hook should check if the users is authenticated. If they are not, set the cookie and let the transition continue.

Using standard promise behavior, call .catch() to handle the case where they are not authenticated. If you reject the promise (or return the rejection from checkAuth() the transition would be cancelled. Instead, handle the rejection and let the transition continue.

$transitions.onBefore({ }, () =>
  session.checkAuth().catch(() => cookieservice.setcookie()));

Hope this helps

beforeoafterm commented 6 years ago

HI! How would I do the same thing, but instead of setting a cookie when the checkAuth() request from my API fails, it would redirect the user to the login page without the transition hook successfully resolving?

This is the code I have: app.js

angular.module('app')
  .run(function ($state, $transitions, AuthService) {
    $transitions.onBefore({ to: 'auth.**' }, function() {
      AuthService.isAuthenticated().then(function (isAuthenticated) {
        if (!isAuthenticated) {
          $state.go('login');
        }
      });
    });
  });

I'm using the $state service to redirect the user to the login page when the unauthenticated user tries to access an auth restricted view. But with this implementation, the transition onBefore() is already resolved, so the transition is succeeding before my checkAuth() method finishes. So it's still showing the view its going to for a (split) sec, before it transitions to the login view.

Here is the implementation of the auth service methods used in the code above: auth.service.js

authService.isAuthenticated = function () {
  // Checks if there is an authenticated user in the app state.
  var authUser = AuthUserStore.get();
  if (authUser) {
    return Promise.resolve(true);
  }
  // Do check auth API request when there's no auth user in the app state.
  return authService.checkAuth()
    .then(function () {
        return !!AuthUserStore.get();
      });
};

authService.checkAuth = function () {
  return $http.get(API.URL + 'check-auth')
    .then(function (res) {
        // Store the user session data from the API.
        AuthUserStore.set(res.data);
        return res;
      });
};
zrss commented 6 years ago

@neilpette hi neilpette, i view your code (er i am a fresh man in ui-router), but as christopherthielen said, may be u should return the promise in transition hook

that means your code should be

angular.module('app')
  .run(function ($state, $transitions, AuthService) {
    $transitions.onBefore({ to: 'auth.**' }, function() {
      return AuthService.isAuthenticated().then(function (isAuthenticated) {
        if (!isAuthenticated) {
          $state.go('login');
        }
      });
    });
  });

as transition hook will wait for the promise to resolve or reject so it will wait for your authentication completed

but there is one thing confused me, that is what's the difference between $state.go and return a TargetService in transition hook ?