0

Have an auth service which fetches data immediately after auth:

export class AuthService {

  profile: BehaviorSubject<Profile| undefined> = new BehaviorSubject<Profile| undefined>(undefined);

  constructor(private auth: Auth, private http: HttpHandlerService) {

    //login and fetch profile data
    this.auth.isAuth.subscribe(isAuth => {
      if (isAuth) { 
        this.http.getProfile.subscribe(profile => this.profile.next(profile))
      } 
    });
  }
}

The profile data is required globally in the app (in the header, etc.).

Problem: On a page refresh/page reload, my Guard does not wait for the Authsevice to finish, hence I cannot access my profile data within the guard.

The only feasable solution seems to be to move the data fetching logic into the Guard, see AuthGuard doesn't wait for authentication to finish before checking user

However, this is not sufficient in my case. I need my profile data globally, it shall not only be fetched if I'm on a guarded route.

So all I want to do is to ensure that my data is fetched before the guard does his work.

My current solution:

export class ProfileGuard {

  constructor(private auth: AuthService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.auth.profile.pipe(
      skipWhile(profile => !profile),
      tap(profile=> {
        if (profile && profile.banned) {
          this.router.navigate(['/banned']).then();
        }
      }),
      take(1),
      map(() => true)
    );
  }

I have two problems with this: 1) I need to add this additional logic to ALL my guards! 2) This solution feels quite hacky.

Is there really no simpler/better way to check my profile data within a Guard right after refresh/page load?

  • 1
    What do you mean _"On a page refresh/page reload, my Guard does not wait for the Authsevice to finish, hence I cannot access my profile data within the guard."_? Is the service not initilalized? On a sidenote, are you intentionally using `skipWhile` instead of `filter`? Your code will let undefined/falsy values through _if_ you once get a value that passes the `skipWhile` condition, but `filter` will not. – Daniel B Jan 27 '22 at 07:54
  • Hm how would I initialize the service? I mean I fetch the data immediately after isAuth becomes true. On page refresh this may take a moment and my guard does not wait for that. –  Jan 27 '22 at 09:46
  • Ah regarding skipWhile it makes no difference as take(1) would emmit the first value received. The idea is to simply wait until auth profile returns a valid value. –  Jan 27 '22 at 10:00
  • 1
    Well, you are correct in the case that you´re using `take`, I didn't reflect on that, good catch. Otherwise, your code should work just fine, I made a small [Stackblitz example](https://stackblitz.com/edit/angular-ivy-d61xvd?file=src/app/service.ts) that works the same way with no issues. Are you sure the service doesn't emit anything before. As to your original question to whether there are better ways, have a look at [this question](https://stackoverflow.com/questions/41219439/angular2-global-guard-user-has-to-be-logged-in-always). – Daniel B Jan 27 '22 at 10:12

1 Answers1

0

The problem is that you have 2 requests (authentication and getting profile), and you want to have them work synchronously, but they are async.

  1. Avoid having subscribe inside subscribe. It's bad behavior and root of your problem.
  2. Use switchMap (or concatMap if you need to keep isAuth Observable active, or any other merging operator) in pipe to have requests working one by one
export class AuthService {

  profile: BehaviorSubject<Profile| undefined> = new BehaviorSubject<Profile| undefined>(undefined);

  constructor(private auth: Auth, private http: HttpHandlerService) {

    //login and fetch profile data
    this.auth.isAuth.pipe(
     switchMap(isAuth => {
      if (isAuth) return this.http.getProfile;
      return of(undefined)
     }))
     .subscribe(profile => {
       if (profile) { 
         this.profile.next(profile))
       } else {
        // do whatever you want if user is not authorized
       }
     });
  }
}
Ivan Vilch
  • 86
  • 1
  • 4