I'm trying to make a role-based access for my app in angular, and I need some help because I'm newbie in angular ...
First this is what I have in the route where I establish which roles can access it...
from app-routing.module.ts
{
path: 'profile',
component: ComunityComponent,
canActivate: [AuthGuard],
data: {
allowedRoles: ['admin', 'user']
}
},
Second I use the canActivate function to check if the user can access the route or not. from auth.guard.ts
private hasAccess: boolean;
constructor(private router: Router, private auth: AuthService) {}
canActivate(next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
const allowedRoles = next.data.allowedRoles;
if (localStorage.getItem('currentUser')) {
return this.checkAccess(localStorage.getItem('currentUser'), allowedRoles);
}
this.router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
return false;
}
Third, I develop the function accessLoginToken, that will find the role of the logged user through an httpclient.get service which returns an array with the roles assigned to the user. Example {success: true, roles:['user']}
from auth.service.ts
accessLoginToken(token: string) {
const check = '/users/access';
const httpOptions = {
headers: new HttpHeaders({
'Authorization': 'Bearer ' + token,
})
};
return this.http.get<any>('/api' + check, httpOptions).pipe(
catchError(err => this.handleError('accessLoginToken', err))
);
}
Fourth in the function checkAccess
, I compare it with those of the route and if they match I allow the access otherwise not.
from auth.guard.ts
private checkAccess(token: string, allowedRoles: string[]): boolean {
this.hasAccess = false;
this.auth.accessLoginToken(token).subscribe(data => {
if (data.success) {
data.roles.forEach(rol => {
if (allowedRoles.findIndex(rols => rols === rol) >= 0) {
this.hasAccess = true;
}
});
}
}, (error) => {
console.log(error);
});
console.log(this.hasAccess);
return this.hasAccess;
}
The main problem is that I can not get the httpclient subscribe to change the value of the hasAccess variable to true to allow access. Even when the code this.hasAccess = true;
is executed the function returns false
By the way I do not like the idea of saving the role in a session variable like the access_token so I'm trying to keep it in the database...
any help on the subject will be appreciated .. thanks
You're correct, the checkAccess
function finishes before the async api call finishes, so false
is always returned. Instead you should pass the observable to the canActivate method.
private checkAccess(token: string, allowedRoles: string[]): boolean {
this.hasAccess = false;
return this.auth.accessLoginToken(token).pipe(
map(data => {
if (data.success) {
data.roles.forEach(rol => {
if (allowedRoles.findIndex(rols => rols === rol) >= 0) {
this.hasAccess = true;
}
});
}
return this.hasAccess;
})
)
}
Because an observable is returned to the canActivate
method, the guard will subscribe to the observable and wait for a true or false result.