I am trying to implement a canActivate guard for an admin route in Angular 4.2.4.
Based off this stack question here: canActivate Question, I think I'm doing everything correctly. But alas, I can't seem to get things to work.
Here is the module guard:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private _store: Store<AppState>,
private router: Router,
private czauth: CzauthService) { }
canActivate(route: ActivatedRouteSnapshot):Observable<boolean>{
return this.czauth.verifyAdminUser().first();
}
};
Here is my czauth service:
getToken():Observable<firebase.User>{
return this.af.idToken;
}
verifyUserRole(token){
this.token = token;
console.log(token);// Log out "PromiseObservable {_isScalar: false, promise: D, scheduler: undefined}"
let headers = new Headers({ 'Authorization': 'Bearer ' + token});
let options = new RequestOptions({ headers: headers });
return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response.json()});
}
verifyAdminUser():Observable<boolean>{
return this.getToken()
.map((user)=>{return Observable.fromPromise(user.getIdToken())})
.map((token)=>{return this.verifyUserRole(token)})
.do((response)=>{console.log(response)})// Always returns "Observable {_isScalar: false, source: Observable, operator: MapOperator}"
.switchMap((response:any)=>{ return response.status === 200 ? Observable.of(true) : Observable.of(false)});
}
I can never get the response from my http async call. I always get what looks to be a cold observable in the console. It's as if the router never subscribes to my observable? I would like to return a boolean based off what my http response is. How can I achieve this?
EDIT:
I am calling the verifyAdminUser
method from my guard. The service is a sinlgeton on the root module. The guard is protecting access to a lazy-loaded module.
Below I have included where I am using the guard in the routing.
Routing Module:
const routes: Routes = [
{path: '', loadChildren : './landing/landing.module#LandingModule'},
{path: 'dashboard', loadChildren : './dashboard/dashboard.module#DashboardModule', canActivate: [ AuthGuard ]}
];
@NgModule({
imports: [RouterModule.forRoot(routes, {preloadingStrategy: PreloadAllModules})],
exports: [RouterModule]
})
export class AppRoutingModule { }
The trouble is when the user tries to navigate to the dashboard module, canActivate always returns false because the response in my switchMap operator is undefined.
EDIT 2:
I refactored and simplified the following two methods in my code, and now everything works just fine. Now, I'm trying to understand why. Here are the adjusted to methods:
verifyUserRole(){
let headers = new Headers({ 'Authorization': 'Bearer ' + this.token});
let options = new RequestOptions({ headers: headers });
return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response});
}
verifyAdminUser():Observable<boolean>{
return this.getToken()
.map((user)=>{
user.getIdToken().then((token)=>this.token = token);
return user;
})
.map(()=>{return this.verifyUserRole})
.switchMap((response:any)=>{ return response ? Observable.of(true) : Observable.of(false)});
}
Solution:
Thanks to @BeetleJuice excellent answer below, I was able to create a derivative solution. Here are the two methods I refactored:
verifyUserRole(token){
this.token = token;
let headers = new Headers({ 'Authorization': 'Bearer ' + token});
let options = new RequestOptions({ headers: headers });
return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response});
}
verifyAdminUser():Observable<boolean>{
return this.getToken()
.switchMap((user)=>{return Observable.fromPromise(user.getIdToken())})
.switchMap((token)=>{return this.verifyUserRole(token)})
.map((response:any)=>{ return response.status === 200 ? true : false});
}