1

My canActivate guard works like this: if the current (fire auth) user has a user profile, then he can access the route. Otherwise, I route him to the profile creation page.

This works perfectly fine, unless I refresh the page!

Guard:

export class UserProfileGuard implements CanActivate {

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

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (this.authService.userProfile) {
      return true;
    }

    this.router.navigate(['user/createprofile']).then();
    return false;
  }


}

Auth Service:

export class AuthService implements OnDestroy {
  public userProfile: UserProfile | null = null;
  private subscription: Subscription | undefined;

  constructor(public fire: AngularFireAuth, private httpService: HttpHandlerService) {
    this.subscription = this.fire.authState.subscribe(fireAuthUser => {
      if (fireAuthUser) {
        this.httpService.getMyProfile().subscribe((profile: UserProfile) => {
            this.userProfile = profile;
          })
      }
    });
  }
}

So if I'm on a protected route and refresh the page, I'm always sent to the profile creation page, even if a profile exists.

I fear that the guard checks too early in case of refresh, in a time in which the profile is not yet fetched by the Auth Service.

Can you please give me some assistance on how to solve this?

Thanks in advance!

kapafo
  • 11
  • 2

2 Answers2

0

Make sure your guard is waiting for the API to get the data.

export class UserProfileGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router, private httpService: YourService) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return checkUser();
  }

  checkUser(): Observable<boolean> {
   this.httpService.getMyProfile().subscribe((profile: UserProfile) => {
            // Do your if the profile exists check
            return true if they have profile
            return false if they don't
          })
  }


}
Brian Smith
  • 1,467
  • 15
  • 31
0

canActivate accepts Observable as return type. So instead of directly returning true/false you can return Observable Also instead of writing router.navigate you can return a UrlTree which will auto redirect in that case.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return this.authService.getUserAuth().pipe(
    map((authResponse) => {
       if(authResponse) {
          return true;
       }
       return this.router.createUrlTree(['user, 'createprofile']).toString()
    }),
    catchError(() => {
      return this.router.createUrlTree(['user, 'createprofile']).toString()
    }))
  }

In your service you can add a method to return auth observable.

userProfile = null;

getUserAuth: () => {
   if(userProfile) {
     return of(userProfile)
   }

   return this.fire.authState.pipe(switchMap((fireAuthUser) => {
     if(fireAuthUser) {
       return this.httpService.getMyProfile()
     }
     return throwError('No Auth User found!')
   }), tap((profile) => this.userProfile = profile)
}