简体   繁体   中英

Angular AuthGuard canActivate with observable from promise not working

I'm trying to guard the admin panel in Angular so that only admin users can access it. While creating the admin AuthGuard I ran into a problem where the AuthGuard doesn't seem to work when the logic is a little bit more complex than just "is the user logged in?".

Instead of the expected redirect I get an empty white screen.

I've been banging my head agains the wall for the past couple of hours trying to find the root cause and it seems to get stuck at this line: const user = await this.firebaseAuth.user.toPromise(); but I don't understand why.

Can someone guide me in the right direction because I seem to be lost in the jungle of Angular and Observables?

The AuthGuard canActivate method:

canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> {
    return this.accountDataService.userIsAdmin().pipe(
      take(1),
      map((admin) => {
        console.log(admin);

        if (admin) {
          return true;
        }

        this.router.navigate(['/aanmelden']);
        return false;
      })
    );
  }

This is the dataService:

userIsAdmin(): Observable<boolean> {
    return from(
      new Promise<boolean>(async (resolve) => {
        const user = await this.firebaseAuth.user.toPromise();

        if (user === undefined) {
          resolve(false);
        }

        const result = await user.getIdTokenResult();
        const admin = result.claims.admin;

        if (admin === undefined || admin === null || admin === false) {
          resolve(false);
        }

        resolve(true);
      })
    );
  }

I guess this.firebaseAuth.user doesn't complete. toPromise only resolves when the observable completes.

You should rewrite your userIsAdmin function to only use observables.

userIsAdmin(): Observable<boolean> {
  return this.firebaseAuth.user.pipe(
    switchMap(user => {
      if (user === undefined || user === null) {
        return of(false);
      }

      return from(user.getIdTokenResult()).pipe(
        map(result => {
          const admin = result.claims.admin;

          if (admin === undefined || admin === null || admin === false) {
            return false;
          }

          return true;
        })
      )
    })
  );
}

The observable needs to complete. I had more success with .take(1) at the end. That would explain why Observable.of(true) worked. Try this:

canActivate(): Observable<boolean> {
  return this.auth.map(authState => {
    if (!authState) this.router.navigate(['/aanmelden']);
    console.log('activate?', !!authState);
    return !!authState;
  }).take(1)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM