0
votes

I am creating a custom login service with AuthGuard from Angular. I have a SessionService which have methods such a login() or onUserLoggedIn() which basically returns BehaviorSubject with the current value of user status (logged in/not logged in). Based on this service I created an implementation of AuthGuard implements CanActivate service. I call onUserLoggedIn method and check the current value in subject. The problem here is that in login method value is changed after the AuthGuard call onUserLoggedIn. Therefore in a first attempt I receive false value from BehaviorSubject but when I try the second time, I received a true value.

How can I change the implementation of those two services to achieve current value in a first attempt?

AuthGuard:

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

    console.log('auth');

    return this.sessionService.onUserLoggedIn()
      .pipe(
        map(res => {
          console.log('res', res);
          return res;
        }),
        take(1),
        tap(allowed => {
          console.log(allowed);
          if (!allowed) {
            this.router.navigate(['/login']);
          }
        })
      );
  }

SessionService:

private isLoggedIn = new BehaviorSubject(false);

  constructor(private apiService: ApiService) {
  }

  login(user: User): Observable<any> {
    console.log('hello before');
    return this.apiService.post(`${BASE_URL}/login`, user)
      .pipe(
        tap((token: Token) => {
          this.isLoggedIn.next(true);
          localStorage.setItem('token', token.token);
          console.log('hello');
        })
      );
  }

  onUserLoggedIn() {
    return this.isLoggedIn;
  }

First attempt goes with printing value:

  • SessionService.ts:19 hello before
  • AuthGuard.ts:17 auth
  • AuthGuard.ts:22 res false
  • AuthGuard.ts:27 false
  • SessionService.ts:25 hello

The second one (which I expect to be first):

  • SessionService.ts:19 hello before
  • AuthGuard.ts:17 auth
  • AuthGuard.ts:22 res true
  • AuthGuard.ts:27 true
  • SessionService.ts:25 hello
2
When does your login component redirect?Tzannetos Philippakos

2 Answers

1
votes

The nature of BehaviorSubject is, it emit the given value immediately whenever you subscribe.

So based on your use case use Subject. Also use skip operator to skip the first value.

return this.sessionService.onUserLoggedIn()
      .pipe(
        take(1),
        map(res => {
          console.log('res', res);
          return res;
        }),
        tap(allowed => {
          console.log(allowed);
          if (!allowed) {
            this.router.navigate(['/login']);
          }
        })
      );
  }

use startsWith(false) in other components.

0
votes

Can you try returning it asObservable?

onUserLoggedIn() {
        return this.isLoggedIn.asObservable();
      }