Monday, 22 October 2018

AngularFirebaseAuth : Calling server api just after firebase auth?

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



from AngularFirebaseAuth : Calling server api just after firebase auth?

No comments:

Post a Comment