New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CanActivate analogue in ui-router-ng2 1.0.0? #2964

Closed
kolkov opened this Issue Sep 2, 2016 · 7 comments

Comments

Projects
None yet
3 participants
@kolkov

kolkov commented Sep 2, 2016

How can we create protected accsess in new ui-router-ng2 similar as in standard Angular 2 router?
http://blog.thoughtram.io/angular/2016/07/18/guards-in-angular-2.html#defining-guards

@christopherthielen

This comment has been minimized.

Contributor

christopherthielen commented Sep 2, 2016

Using Transition Hooks.

Start with a simple transition hook function which asynchronously checks if the user is authenticated. It redirects to the login state if the user isn't authenticated.

The simple auth hook

function requireAuthentication(transition) {
  let $state = transition.router.stateService;
  let authSvc = transition.injector().get(AuthService);
  return authSvc.checkAuthenticated().catch(() => $state.target('login'));
}

Protect individual state

If protecting a single state, use an onEnter hook:

.state({
  name: 'foo',
  onEnter: requireAuthentication
})

Protect classes of states

If protecting a set or class of states, use a global hook which queries metadata on a state:

.state({
  name: 'foo',
  protectMe: true
});
class MyUIRouterConfig {
  configure(router: UIRouter) {
    let criteria = { entering: (state) => state.protectMe };
    router.transitionService.onBefore(criteria, requireAuthentication);
  }
}

Some other options:

Protect classes of states using data prop

The previous example could instead use the data property (which is inherited) to protect a tree of states. This allows protectMe metadata to be limited to the entry point of a state tree.

.state({
  name: 'foo',
  data: { 
    protectMe: true
  }
});

.state({
  name: 'foo.bar',
   // also protected because it inherits `data`
});
class MyUIRouterConfig {
  configure(router: UIRouter) { 
    let criteria = { to: (state) => state.data && state.data.protectMe };
    router.transitionService.onBefore(criteria, requireAuthentication);
  }
}

Protect group of states using globs

The hook could use state name globbing:

.state({
  name: 'authstates',
});

.state({
  name: 'authstates.bar',
   // also protected because it inherits `data`
});
class MyUIRouterConfig {
  configure(router: UIRouter) {
    let criteria = { to: 'authstates.**' };
    router.transitionService.onBefore(criteria, requireAuthentication);
  }
}

Hope this helps

@kolkov

This comment has been minimized.

kolkov commented Sep 2, 2016

Thank you for such a detailed answer!

@kolkov

This comment has been minimized.

kolkov commented Sep 2, 2016

I have a little problem. When transition rejected and state changed to public.user.login, the url is not changed to :8080/angular2/#/public/user/login

image

@christopherthielen

This comment has been minimized.

Contributor

christopherthielen commented Sep 2, 2016

@kolkov please open a new issue. that sounds familiar and I think I know what the problem is.

  1. We track what triggers a transition (the trigger source). When triggered by a URL change, we don't update the URL.
  2. When we redirect a transition, we copy the .options() to the new Transition. I think this is copying the transition trigger as "url change"
  3. Your initial transition was triggered by a URL change (the initial call to .sync()).
  4. I think the redirected transition optiions still has "url" as the source, but it should be something else like "redirect".

Can you try stateService.target('public.user.login', null, { source: 'unknown' }) and see if the URL updates?

Here's where we create the redirected transition options: https://github.com/angular-ui/ui-router/blob/master/src/transition/transition.ts#L374

@kolkov

This comment has been minimized.

kolkov commented Sep 2, 2016

Can you try stateService.target('public.user.login', null, { source: 'unknown' }) and see if the URL updates?
Yes! This worked!

@pscanlon1

This comment has been minimized.

pscanlon1 commented Dec 19, 2017

return authSvc.checkAuthenticated().catch(() => $state.target('login'));

is this a promise? what do i need to return here...

@christopherthielen

This comment has been minimized.

Contributor

christopherthielen commented Dec 21, 2017

@pscanlon1 in the example, authSvc.checkAuthenticated() is a fictional service call that would return a promise for whether the user is authenticated or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment