So I have to do two things in my canActivate guard of the router. First I have to check if the user is authorized, I do this by calling OpenIdcSecutiry service, and then if the user is authorized I must check the users roles by calling another service that returns UserProfile including its roles. I know I can return an Observable and in fact if I just do:
return this.oidcSecurityService.getIsAuthorized();
It works, but obviously I does not check user roles. Right now I have this:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.oidcSecurityService.getIsAuthorized()
.map(
data => {
if (data === false) {
this.router.navigate(['/login']);
return data;
}
if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
this.profileService.getObservableUserProfileFromServer().map(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
return true;
}
}
return false;
});
}
return data;
}
},
error => {
this.router.navigate(['/login']);
return error;
}
);
this.profileService.getObservableUserProfileFromServer() is never called and I dont know how to check the user roles after I know the user is Authorized. How can I do this? Can I return the observable inside my observable?
EDIT1: I changed the code to this:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.oidcSecurityService.getIsAuthorized()
.map(
data => {
if (data === false) {
this.router.navigate(['/login']);
return data;
}
if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
return this.profileService.getObservableUserProfileFromServer().toPromise().then(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
return true;
}
}
return false;
}
);
}
return data;
}
},
error => {
this.router.navigate(['/login']);
return error;
}
);
// return this.oidcSecurityService.getIsAuthorized();
}
Now I can see how this.profileService.getObservableUserProfileFromServer is called and receive results from server, but the result is not evaluated in canActivate guard, it simply ignores the result and It says the Navigation was canceled.
EDIT FINAL:
@llai gave me the answer, two switchmaps must be used to chain several observables, final code here:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.oidcSecurityService.getIsAuthorized()
.switchMap(
data => {
if (data === false) {
this.router.navigate(['/login']);
return Observable.of(false); // user not logged in, canActivate = false
} else if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
// return inner observable
return this.profileService.getObservableUserProfileFromServer()
.switchMap(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
// role found, canActivate = true
return Observable.of(true);
}
}
// no matching role, canActivate = false
return Observable.of(false);
});
} else {
// no roles defined in route data, canActivate = false
return Observable.of(true);
}
}
});
}