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.
Guard's canActivate method, as its name suggests, resolves upon the attemption to activate specific route.
As I understand from the code provided, you're trying to redirect user to the
/user
route upon retrieving authuid
from the server. To achieve that, you need to initiate redirecting to the desired route once the authuid
is retrieved - e.g. after logging in, and let your guard do its job, enable or deny the access to the route.After sorting things out, here is the walk-through the changed code and structure:
AuthInfo class:
AuthService:
AuthGuard: