What I want to achieve: I want to share authentication state across my application using BehaviorSubject. I use the authentication state e.g. inside an auth-guard to prevent the user from visiting login/register pages when the user already is authenticated.
Problem: because the BehaviorSubject has a initial value, which is false (not logged in), it seems that the auth-guard takes this first value, instead of waiting for the uid-sync.
AuthInfo (Auth state store):
export class AuthInfo {
constructor(public uid: string) {}
isLoggedIn() {
return !!this.uid;
}
}
AuthService:
@Injectable()
export class AuthService {
static UNKNOWN_USER = new AuthInfo(null);
authInfo$: BehaviorSubject<AuthInfo> = new BehaviorSubject<AuthInfo>(AuthService.UNKNOWN_USER);
constructor(private af: AngularFire) {
this.af.auth.subscribe(auth => {
if (auth) {
console.log('got the uid');
this.authInfo$.next(new AuthInfo(auth.uid));
} else {
this.authInfo$.next(AuthService.UNKNOWN_USER);
}
});
}
logIn(email: string, password: string): Promise<FirebaseAuthState> {
return this.af.auth.login({email: email, password: password});
}
}
AuthGuard:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService,
private router: Router) {
}
canActivate(): Observable<boolean> {
return this.authService.authInfo$.map(authInfo => {
if (authInfo.isLoggedIn()) {
this.router.navigate(['/user'])
}
return !authInfo.isLoggedIn();
});
}
}
So canActivate is processed with authInfo.isLoggedIn()
being false
and after a fraction of a second I see Got the uid
in the console. Any ideas how to prevent the first false
? I think that BehaviorSubject is correctly used here, because it allows us to set an initial state. However the auth-guard will always receive false (the initial value). Right after that the
this.authInfo$.next(new AuthInfo(auth.uid));
will trigger, when the canActivate method was already finished.