Ctfrancia
Ctfrancia

Reputation: 1588

Ionic 4 - AuthGuard page routing permission delayed

I have an ionic 4 app that is storing information about the user natively. When the user opens the app then if his information is there then I want him to be sent to the Home Page. If he is not then I want him to be routed to the Login Page, pretty normal.

The problem that I am running into is that for about 500ms you will see the Login Page and then upon reading the information then it will redirect you to the home page.

What I would like is for the application to WAIT On the response from the storage before it directs you to the Login Page or the Home Page.

App.component.ts

  public initializeApp(): void {
    this.platform.ready().then((): void => {
      this.statusBar.styleDefault();
      this.splashScreen.hide();
      this.authService.authState.subscribe((state: boolean): void => {
        if (state) {
          this.router.navigate(['home']);
        } else {
          this.router.navigate(['login-register']);
        }
      });
      this.permissionService.requestCameraPermission();
    });
  }

AuthService.service.ts

public readonly authState: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false)

  private _ifLoggedIn(): void {
    // just sees if his info is saved, if so then he has been logged in
    this.userService.getUserInfo().then((response: UserData): void => {
      if (response) {
        this.authState.next(true);
      }
    });
  }

  public isAuthenticated(): boolean {
    return this.authState.value;
  }

AuthGuard

export class AuthGuardService {
  private authService: AuthService;

  public constructor(authService: AuthService) {
    this.authService = authService;
  }
  public canActivate(): boolean {
    return this.authService.isAuthenticated();
  }
}

app-routing.module.ts

const routes: Routes = [
  {
    path: '',
    redirectTo: 'login-register',
    pathMatch: 'full',
  },
  {
    path: 'login-register',
    loadChildren: () => import('../pages/login-register/login-register.module').then(m => m.LoginRegisterPageModule),
  },
  {
    path: 'home',
    loadChildren: () => import('../pages/home/home.module').then(m => m.HomePageModule),
    canActivate: [AuthGuardService],
  },
];

if there is anything other information you need let me know. Thanks guys!

Upvotes: 4

Views: 663

Answers (1)

Al-Mothafar
Al-Mothafar

Reputation: 8219

The first problem that you have is BehaviorSubject got initialized so it will return a result directly when you ask for a result.

The second problem is that isAuthenticated() will return the value because it is sync method, but you need anybody who calls it to wait.

The solution is to use ReplySubject and make isAuthenticated() async.

AuthService.service.ts

public readonly authState: ReplaySubject<boolean> = new ReplaySubject<boolean>(1)

  private _ifLoggedIn(): void {
    // just sees if his info is saved, if so then he has been logged in
    this.userService.getUserInfo().then((response: UserData): void => {
        this.authState.next(!!response);
    }, (reason) => this.authState.next(false));
  }

  public async isAuthenticated(): Promise<boolean> {
    return this.authState.asObservable().pipe(take(1)).toPromise();
  }

Note that this.authState.next(false) is to return a result and not stuck forever waiting, so if error returned like error 401, it will consider it not logged in.

And this.authState.next(!!response) is just shorten format for your previous condition, and will handle the case if response returned with null or undefined if you implemented your .getUserInfo() like that the user is not logged in.

AuthGuard

export class AuthGuardService implements CanActivate {
  private authService: AuthService;

  public constructor(authService: AuthService) {
    this.authService = authService;
  }
  public async canActivate(): Promise<boolean> {
    return this.authService.isAuthenticated();
  }
}

Note that canActivate() support async implementation already, also, I added implements CanActivate so it does make more sense.

You can learn more about the types of Subject here: Understanding rxjs BehaviorSubject, ReplaySubject and AsyncSubject

Upvotes: 2

Related Questions