-1

EDITED BELOW

I don't want to make a call to determine if a user is authenticated every time the route changes, but I do want to check that if they refresh the page on an authenticated route, they can access it. I can't seem to make this work.

I have a service that simply checks the session with the api, like this:

    authenticate(): Observable<boolean> {
        return this.http.post<boolean>(this._authUrl, {}, {})
    }

Endpoint literally returns a true or false. I run this on the app.component so it's the first thing that happens and it never needs to request that again.

   ngOnInit() {
        this.userService.authenticate().subscribe((response) => {
            this.userService.loggedIn = response;
        })

    }

I then have a route defined like this:

    {
        path: 'dashboard',
        component: DashboardComponent,
        canActivate: [AuthGuardService],
        pathMatch: 'full'
    },

Then in my authguard service, I have this:

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): any{
    return this.userService.loggedIn
    })
}

This works just fine if you land on / and navigate to /dashboard. But if you're logged in and refresh the page on /dashboard, the component never loads. It loads the navigation and whatnot, but doesn't load the dashboard component or template. /authenticate does run but seems the route guard already looks at loggedIn before the endpoint responds.

I tried putting the loggedIn variable into an observable using of, that didn't seem to make any difference, end result was the same.

Everything I've looked up seems to be for checking authentication via a URL and using observables that way, but I really don't want to have to make an http request when it already knows the user is authenticated from the first initial request.

Edit: I think a better way to think about this is, I want the /authenticate response to be ready before loading anything else. With route guard, I'd have to lock the landing page behind being logged in, and I don't want to do that. But I also want the response of /authenticate to be accessible by the root app.component because thats where my navigation is as well as an indicator to the user that they're logged in.

So it seems to make sense to me that if I could tell angular to wait on /authenticate before doing anything at all, then even if they refresh the page on /dashboard, the /authenticate response will already be there so I don't have to worry about this obnoxious race condition. Can I do that?

Jer_TX
  • 465
  • 4
  • 20

1 Answers1

-1

I'll do something like this:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean>  {
    return new Promise<boolean>((resolve, reject) => {
      if (this.getUser) this.getUser.unsubscribe();
      this.getUser = this.authService.getUser().subscribe(r => {
        resolve(true);
      }, (err) => {
        reject(err);
      });
    });
  }

and this:

getUser(): Observable<UserDTO> {
    if (this.user) return new Observable(o => o.next(this.user));
    return this.http.get<UserDTO>(URL, { withCredentials: true })
      .pipe(
        map(u => new UserDTO(u)),
        tap(u => {
          this.user = u;
          this.isLogined = true;
        })
      );
  }

and remove logic from component. this must be in guard.

  • Can you explain what exactly this is doing? A lot of your code here isn't even what I use. Tried to get this working with what I have and it isn't working. – Jer_TX Jul 18 '19 at 23:19