I'm trying to do an "AdminGuard", that should be based on two things:
I've an AuthService
that provides two Observable
.
I've done the following:
@Injectable({
providedIn: 'root'
})
export class IsAdminGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) { }
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> {
console.log(this.auth)
return combineLatest([this.auth.isLoggedIn, this.auth.isAdmin]).pipe(
take(1),
map((authInfo) => {
console.log(authInfo)
if (!authInfo[0]) {
console.error('Access denied - Unauthorized')
return this.router.parseUrl('/auth/');
} else if (!authInfo[1]) {
console.error('Access denied - Admin only')
return this.router.parseUrl('/auth/unauthorized');
} else {
return true;
}
})
);
}
}
the console.log(this.auth)
gets called and seems to have valid values, but the second console.log never gets called and my component isn't loaded.
If I remove the guard from my route:
{
path: 'admin',
component: AdminComponent,
//canActivate: [IsAdminGuard],
}
it works, so I'm pretty sure it's the IsAdminGuard that doesn't work.
I'm also displaying the some other things based on the same boolean values(some *ngIf="authService.IsLoggedIn | async"
which are working, so I don't really understand what I messed up?
EDIT Here is how I update the different values of the IsLoggedIn/IsAdmin/IsUser, in my AuthService:
constructor(public afAuth: AngularFireAuth, public router: Router, private afStore: AngularFirestore) {
this.afAuth.authState.subscribe(async user => {
console.log('handling auth')
if (this._roleSubscription) {
this._roleSubscription.unsubscribe();
this._roleSubscription = undefined;
}
if (user) {
this._user.next(user);
this._isLoggedIn.next(true);
this._roleSubscription = this.afStore.doc<Roles>(`roles/${user.uid}`).valueChanges().subscribe(role => {
console.log('updating roles', role)
if (role) {
this._isAdmin.next(role.admin == true)
this._isUser.next(role.admin == true || role.user == true);//Admin have also an heart, they are users too!
} else {
this._isAdmin.next(false);
this._isUser.next(false);
}
});
} else {
this._user.next(undefined);
this._isLoggedIn.next(false);
this._isAdmin.next(false);
this._isUser.next(false);
await this.router.navigate(['/auth']);
}
console.log('values updated')
})
}
You have to use a ReplaySubject
which emits the latest value. A subject only emits when there is an active subscription, and a BehaviorSubject
always emits as it starts with an initial value
readonly _isLoggedIn = new ReplaySubject<boolean>(1);
readonly _isAdmin = new ReplaySubject<boolean>(1);