1

I'm attempting to validate if a user is logged in or not as soon as they load the page, but the execution order of the code is making this extremely difficult. Here's the code that checks if the user is logged in:

ngOnInit() {
    this.authService.authState.subscribe((user) => {
      this.user = user;
      this.loggedIn = (this.user != null);
      this.getUserProfile(user);
      console.log(this.loggedIn);
    });
    console.log(this.loggedIn);
    if (this.loggedIn) {
      console.log("Don't Redirect")
    } else {
      console.log("Redirect");
    }
  }

When running this code, this code block executes first:

console.log(this.loggedIn);
    if (this.loggedIn) {
      console.log("Don't Redirect")
    } else {
      console.log("Redirect");
    }

Then if the user is logged in this block will execute second:

this.authService.authState.subscribe((user) => {
      this.user = user;
      this.loggedIn = (this.user != null);
      this.getUserProfile(user);
      console.log(this.loggedIn);
    });

If the user is not logged in then the code in the subscribe does not execute at all, so I'm having trouble working out how I can redirect users away from the page to the login if they are logged out and keep them where they are and get the user profile if they are logged in. Maybe I've just been staring at it for so long now I just can't get my head round it, but either way can anyone help?

Web Develop Wolf
  • 5,996
  • 12
  • 52
  • 101

1 Answers1

3

UPDATE:

Since we found out that you want to prevent user accessing a route if not logged in, I suggest using a route guard.

Also if you want to persist the user on page refresh, you need to use something like localStorage

So when you log in, set user to localstorage:

this.authService.authState.subscribe((user) => {
  this.user = user;
  localStorage.setItem('user', this.user)
});

To grab the user when you need it, use:

this.user = localStorage.getItem('user');

When the user logs out, use either clear() or removeItem:

localStorage.removeItem('user');

Now you have access to the user whenever you need it. Though, in this case, use a route guard to protect your route. Follow the steps in the docs

and this is then how your guard would look like:

export class AuthGuard implements CanActivate {
  constructor(private router: Router) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean {
    let url: string = state.url;

    return this.checkLogin(url);
  }

  checkLogin(url: string): boolean {
    const user = localStorage.getItem('user');
    if (user) {
      return true;
    }
    // route where you want to route and return false
    this.router.navigate(['/login']);
    return false;
  }
}

Now with this, you don't even need the check in the component, so remove this:

if (this.loggedIn) {
  console.log("Don't Redirect")
} else {
  console.log("Redirect");
}

... since the guard now takes care of this!

As a last comment, you could also look into state management, for example with ngrx-store, there is a plugin which also connects localStorage to that.


ORIGINAL ANSWER:

Reading up on the plugin, indeed you are only notified when user has logged in or logged out, nothing in between. I guess you could leverage your own variable/Behaviorsubject. I like BehaviorSubject ;)

So in a service, here called MyService:

private appUser = new BehaviorSubject(null);
public appUser$ = this.appUser.asObservable();

modifyStatus(appUser: any) {
  this.appUser.next(appUser);
}

Then, where you are logging in user, add in the callback:

this.authService.authState.subscribe((user) => {
    this.user = user;
    this.myService.modifyStatus(user);
});

And when logges out, you again just call this.myService.modifyStatus(null).

In your component you can then subscribe to that...

this.myService.appUser$.subscribe((user: any) => {
  if (user) {
    console.log("Don't Redirect")
  } else {
    console.log("Redirect");
  }
});
AT82
  • 71,416
  • 24
  • 140
  • 167
  • edited to use the user object instead, makes more sense, you'd probably want to get hold of the user instead of just a boolean, but with any case, you can completely choose what makes sense for you, a boolean, the user or something else :D – AT82 Feb 16 '19 at 21:59
  • Still hits the redirect even when logged in :( - user is coming back null even when logged in – Web Develop Wolf Feb 16 '19 at 22:04
  • How does the flow go here. Where do you login in? My first suspicion here is that you get `null` (the initial value of the behaviorsubject). Is this a race condition? Don't know your code, so just guessing here. – AT82 Feb 16 '19 at 22:06
  • So I have a login page - I've signed out so I know my state isn't just cached - I hit the login button (which is where it executes your second block of code) - then I manually navigate to the page that does the check (third block of code) – Web Develop Wolf Feb 16 '19 at 22:08
  • What gets me is that there's no way to check which sort of defeats the object of the add on really? – Web Develop Wolf Feb 16 '19 at 22:10
  • Hmm... what's the value of the `user` when you login? Console log that, and also console log the user inside `this.myService.appUser$`? What are the values, and are they in the correct order? I.e the first console log is first? – AT82 Feb 16 '19 at 22:17
  • Oh wait... with `manually`, do you mean that you type the url? That would mean that the app is reinitialized, which then would make the behaviorsubject have the initial value, i.e `null`. If that is the case you need to use for example localstorage to persist the value. – AT82 Feb 16 '19 at 22:23
  • Yeah I type the URL – Web Develop Wolf Feb 16 '19 at 22:24
  • I'm trying to stop someone navigating to the URL and getting access to the page – Web Develop Wolf Feb 16 '19 at 22:24
  • We have guards for such scenarios: https://angular.io/guide/router#milestone-5-route-guards But anyway, if you need to persist the state, use localstorage for example. – AT82 Feb 16 '19 at 22:26
  • My head is completely spent tonight - Is there any chance you could help me out show me the code with the local storage? I'd be super grateful :) – Web Develop Wolf Feb 16 '19 at 22:28
  • I'm actually going to bed now :D But it's very simple: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage Idea is the same as above in my answer, when you login, `setItem` the user in localstorage, when you need to get the user for checking if exists, call `getItem`, and when user logs out `clear` – AT82 Feb 16 '19 at 22:38
  • I can modify my answer tomorrow to fit your needs, but my cats are already giving me the evil eye as to why I'm not sleeping yet :P :P – AT82 Feb 16 '19 at 22:43
  • @WebDevelopWolf Heeey there! I updated my answer, hopefully suits your needs, let me know if you run into some issue :) :) – AT82 Feb 17 '19 at 09:00