0

As the title suggests, I want to protect routes via canLoad. This works to some extent. However, I have two issues with the code.

Here are my code snippets

AppComponent

ngOnInit(): void {
    this.authServ.reauthenticate().subscribe()
}

AuthService

user = new BehaviorSubject<User>(null as any);
reauthenticate(): Observable<User> {
    // return HTTP call here and pipe the user data into user BehaviorSubject
}

AuthGuard

canLoad(){
      return this.authServ.user.pipe(
        map((user) => {
          if (!!user?._token) {
            return true;
          }
          this.router.navigate(['/login']);
          return false;
        })
      );
}

As you can guess, the router can access the user but still redirects to the /login path. Knowing it doesn't work, I brute-forced my way, making two HTTP calls to the server (which is generally bad, I know) to trick the system into acknowledging a persistent user. Here is the code snippet.

AuthGuard

canLoad() {
      return this.authServ.user.pipe(
        switchMap((user) => {
          if (!!user && !!user._token) {
            return of(user);
          }
          return this.authServ.reauthenticate();
        }),
        map((user) => {
          if (!!user?._token) {
            return true;
          }
          this.router.navigate(['/login']);
          return false;
        })
      );
}

So my question boils down to either of these two things:

  1. How can I make sure that I only call the re-tokenizer endpoint only once but still not continue to not be routed upon refresh?
  2. How can I make the BehaviorSubject work until I receive a user object?

I have checked the following links: AuthGuard router angular and What is the difference between Subject and BehaviorSubject?. However, upon using a Subject, I may need to reauthenticate the user every time I visit a guarded route which is counterintuitive.

2
  • You don’t really get a second change in your canLoad.. the first emission is the only thing. Does it help when you start (in your pipe, before switchMap) with a filter that specifically checks for null? Commented Jun 14, 2021 at 19:08
  • Doesn't the if statement on the SwitchMap work the same way? But if I were to apply a filter operator before SwitchMap, what would I do with it? Commented Jun 15, 2021 at 3:13

1 Answer 1

0

Okay, so I've solved this problem. What I did was to create a new Subject at the AuthService. The guard checks if the BehaviorSubject inside the AuthService returns a null, and if it does, I will switchMap into using the Subject. Here is the code.

AuthGuard

canLoad() {
    return this.authServ.user.pipe(
        switchMap((user) => {
          if (!!user && !!user._token) {
            return of(user);
          }
          return this.authServ.initialLogin;
        }),
        map((user) => {
          if (!!user?._token) {
            return true;
          }
          this.router.navigate(['/login']);
          return false;
        })
}

AuthService

user = new BehaviorSubject<User>(null as any);
initialLogin = new Subject<User>();
reauthenticate(): Observable<User> {
    // return HTTP call here and pipe the user data into user BehaviorSubject
    return this.http.get<User>(URL, {withCredentials: true}).pipe(tap((user) => {
        this.initialLogin.next(user);
        this.user.next(user);
    }))
}

AppComponent

ngOnInit(): void {
    this.authServ.reauthenticate().subscribe()
}

This way, I only authenticate once upon the start of the project

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.