My auth is based on 2 things :
- firebase auth (email/password)
- call on a server API to retrieve full customer entity from BDD and from firebaseID (user must exists) So a user will be "authenticated" if these two conditions are met.
I also have authGuards based on a isAuthenticated() returning an Observable (because on a page refresh, guard must wait for the auth to be finished before redirecting the user anywhere).
Problem : I can't find a way to make that work with all the async and rxjs mess/hell .. Currently it's working but each time isAuthenticated is called, the serverAPI auth is called every time...
How can I refactor that in order to call server only once and all the async/reload stuff still works ?
AuthService :
export class AuthService {
public userRole: UserBoRole;
public authState$: Observable<firebase.User>;
constructor(
private afAuth: AngularFireAuth,
private snackBar: SnackBarService,
private translate: TranslateService,
private router: Router,
private grpcService: GrpcService
) {
this.authState$ = this.afAuth.authState.pipe(
take(1),
mergeMap(user => {
if (!user) {
return of(user);
}
// User is successfully logged in,
// now we need to check if he has a correct role to access our app
// if an error occured, consider our user has not logged in, so we return null
return this.checkProfile().pipe(
take(1),
map(() => {
this.test = true;
return user;
}),
catchError(err => {
console.error(err);
return of(null);
})
);
})
);
// Subscribing to auth state change. (useless here because access logic is handled by the AuthGuard)
this.authState$.subscribe(user => {
console.log('authState$ changed :', user ? user.toJSON() : 'not logged in');
});
}
checkProfile() {
return this.callAuthApi().pipe(
map((customer) => {
if (!customer || customer.hasRole() === "anonymous") {
return Promise.reject(new Error(AuthService.AUTH_ERROR_ROLE));
}
this.userRole = customer.getRole();
})
);
}
isAuthenticated(): Observable<boolean> {
return this.authState$.pipe(map(authState => !!authState));
}
}
AuthGuard :
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private authService: AuthService, private router: Router) {}
check(): Observable<boolean> {
return this.authService.isAuthenticated().pipe(
catchError(err => {
// notifying UI of the error
this.authService.handleAuthError(err);
// signout user
this.authService.signOut();
// if an error occured, consider our user has not logged in
return of(false);
}),
tap(isAuthenticated => {
if (!isAuthenticated) {
// redirecting to login
this.router.navigate(['login']);
}
})
);
}
canActivateChild(): Observable<boolean> {
return this.check();
}
canActivate(): Observable<boolean> {
return this.check();
}
}
Thanks