Angular doesn\'t provide any authorization/access permission on routing (I\'m talking default Angular route 1.x and not beta 2.0 or UI route). But I do have to implement it.
If you can use ui-router, you could do this:
.state('root', {
url: '',
abstract: true,
templateUrl: 'some-template.html',
resolve: {
user: ['Auth', function (Auth) {
return Auth.resolveUser();
}]
}
})
Auth.resolveUser()
is just a backend call to load the current user. It returns a promise so the route will wait for that to load before changing.
The route is abstract so other controllers must inherit from it in order to work and you have the added benefit that all child controllers have access to the current user via the resolve.
Now you catch the $stateChangeStart
event in app.run()
:
$rootScope.$on('$stateChangeStart', function (event, next) {
if (!Auth.signedIn()) { //this operates on the already loaded user
//user not authenticated
// all controllers need authentication unless otherwise specified
if (!next.data || !next.data.anonymous) {
event.preventDefault();
$state.go('account.login');
}
}else{
//authenticated
// next.data.roles - this is a custom property on a route.
if(!Auth.hasRequiredRole(next.data.roles)){
event.preventDefault();
$state.go('account.my'); //or whatever
}
}
});
Then a route that requires a role can look like this :
.state('root.dashboard', {
//it's a child of the *root* route
url: '/dashboard',
data: {
roles: ['manager', 'admin']
}
...
});
Hope it makes sense.
I've approached this issue many times, I've also developed a module (github). My module (built on top of ui.router) is based on $stateChangeStart (ui.router event) but the concept is the same with the default ng.route module, it's just a different implementation way.
In conclusion I think that handling routing changing events is not the good way to perform an authentication checking: For example, when we need to obtain the acl via ajax the events can't help us.
A good way, I think, could be to automatically append a resolve to each "protected" state...
Unfortunately ui.Router doesn't provides an API to intercept the state creation so I started my module rework with a little workaround on top of $stateProvider.state method.
Definitively, I'm looking for different opinions in order to find the correct way to implement a Authentication Service in AngularJS.
if are there anyone that is interested in this research... please, open an issue on my github and the discuss
The most simple way is to deal with current route's resolve dependencies, and $routeChangeStart
is a good place to manage this. Here's an example.
app.run(function ($rootScope, $location) {
var unrestricted = ['', '/login'];
$rootScope.$on('$routeChangeStart', function (e, to) {
if (unrestricted.indexOf(to.originalPath) >= 0)
return;
to.resolve = to.resolve || {};
// can be overridden by route definition
to.resolve.auth = to.resolve.auth || 'authService';
});
$rootScope.$on('$routeChangeError', function (e, to, from, reason) {
if (reason.noAuth) {
// 'to' path and params should be passed to login as well
$location.path('/login');
}
});
});
Another option would be adding default
method to $routeProvider
and patching $routeProvider.when
to extend route definition from default object.
ui-router have a lot of events that you can easy manipulate. I always use it.
State Change Events have everything you need. Something like this will be implement in the AngularJS 2.x.
But if you are looking the solution for native Angular 1.x.y router this solution will not help you. Sorry