7

So I'm really close to having this figured out. Basically, I have login working and I have the code setup to watch for onAuthStateChanged, but the problem is that when I refresh the page, even if the user is currently logged in, it still redirects to the /login page because the auth guard that I have setup, checks if the user is logged in.

I have an auth service setup that does all the authentication checks.

In my auth service constructor, that's where I have my onAuthStateChanged code setup:

constructor(
    private auth: AngularFireAuth,
    private router:Router,
    private db:AngularFireDatabase,
  ) {

    firebase.auth().onAuthStateChanged(function(user) {
     if (user) {
       // What do I need to put here to ensure that the auth guard goes to the intended route?
     }
    });

  }

Here is my auth guard's canActivate() method:

 canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean> {

      if(this.authService.getAuthenticated()) {
        return Observable.of(true);
      } else {
        this.router.navigate(['/login']);
      }
    }

And here is the getAuthenticated() method:

getAuthenticated() {
    return firebase.auth().currentUser;
  }

Update:

Here are the methods that are called when the user actually logs in. In the actual component, here is the login method:

login(data, isValid) {
    if(isValid) {

      this.authService.login(data.email, data.password)
          .then(
              (data) => {
                console.log('data', data);
                this.router.navigate(['/projects'])
              }, error => {
                this._utilities.createToasty({
                  msg: "The email/password combination is incorrect.",
                  type: "error"
                });
              }
          );
    }
  }

And in the auth service, here's the login method:

login(email, password) {
    return firebase.auth().signInWithEmailAndPassword(email, password);
  }

So right now, I'm not entirely sure what code I need to put where to ensure that when my user refreshes the page, the auth guard picks up on that and actually allows the user to go to the route instead of always redirecting to the /login route.

Thoughts?

KENdi
  • 7,576
  • 2
  • 16
  • 31
Stevie Star
  • 2,331
  • 2
  • 28
  • 54
  • Could you post your authentication method ? IE the method that is called when user press login – Ced May 21 '17 at 15:47
  • Absolutely. I've gone ahead and added them. Do you know what I need to be doing here? – Stevie Star May 21 '17 at 23:30
  • Hi Stevie, did you mange to solve this Problem, if so can you please post the answer. thx – iFadi Jun 25 '17 at 08:31
  • Possible duplicate of [How do I keep a user logged into a firebase app after refresh?](https://stackoverflow.com/questions/32253413/how-do-i-keep-a-user-logged-into-a-firebase-app-after-refresh) – Makah Nov 20 '17 at 21:54

5 Answers5

6

I don't really know the answer since I don't use firebase so those are more pointers

Guess 1: the user is in an intermediate state

The function that runs the authentication in the background hasn't run yet when the canActivate method does. Thus the current user isn't set yet. I guess firebase simply goes to your local storage and check if the token is valid. If it's what it does then simply make sure that this happens before canActivate.

However it could run before and have the current user still undefined if it's asynchronous which is quite possible. Then you'll have to make sure the canActivate runs after it resolves.

Actually, better yet: use the observable in your can activate method as seen:

code:

 canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.af.auth.map((auth) => {
        if (!auth) {
          this.router.navigateByUrl('login');
          return false;
        }
        return true;
    }).take(1);
  }

Guess 2: You have to store something in local storage.

Those authentication services usually requires to store a token in localstorage, this token is then used to authenticate the user upon page refresh. Of what I can read this is done in the background with firebase. You could still check the local storage/ session storage just to be sure you see info's there.

If you don't see anything relevant in localStorage/sessionStorage, chances are you have to put in there something upon login.

Community
  • 1
  • 1
Ced
  • 15,847
  • 14
  • 87
  • 146
  • 1
    Thankyou so much for looking into that. What's funny is one of those links was actually an older post of mine, but it reminded me of something I could try, based on your recommendation. So once, again thankyou :) – Stevie Star May 22 '17 at 01:01
3

I had the same problem as yours, i did the following to solve it with localStorage:

login(email, password): Observable<AuthInfo> {
    return this.fromFirebaseAuthPromise(this.afAuth.auth.signInWithEmailAndPassword(email, password));
}

I just added localStorage.setItem("user", this.afAuth.auth.currentUser.uid); to the login response and localStorage.removeItem("user"); to the logout method.

fromFirebaseAuthPromise(promise): Observable<any> {

const subject = new Subject<any>();

promise
  .then(res => {
      const authInfo = new AuthInfo(this.afAuth.auth.currentUser.uid);
      this.authInfo$.next(authInfo);
      subject.next(res);
      subject.complete();
      localStorage.setItem("user", this.afAuth.auth.currentUser.uid);
    },
    err => {
      this.authInfo$.error(err);
      subject.error(err);
      subject.complete();
    });

    return subject.asObservable();
}



logout() {
    this.afAuth.auth.signOut();
    localStorage.removeItem("user");
    this.authInfo$.next(AuthService.UNKNOWN_USER);
    this.router.navigate(['/login']);
}

and changed the canActivate() method to the following:

canActivate() {
        //you can check token is exists or not here
        if (localStorage.getItem('user')) {
            console.log("user ist logged in ....", localStorage.getItem('user'));

            return true;
        } else {
            console.log("user is not logged in");

            this.router.navigate(['/login']);
            return false;
        }
    }
iFadi
  • 915
  • 2
  • 12
  • 20
  • 1
    This was my answer of choice. Thank you, iFadi. I'd take it a step further and use momentjs and a expiration date of choice. I made this stay in local storage for 3 days with minimal additional code. – Kirby Mar 13 '18 at 01:53
0
firebase.auth().onAuthStateChanged(function (user) {
   console.log('onAuthStateChanged: ', user);
});
Dani
  • 3,128
  • 2
  • 43
  • 91
0

Let me try to solve this by saving user data in local storage. My logic is pretty straightforward when the user is logged in we will save the user data in local storage, by doing this we'll have the user state even after the page is reloaded.

userData: any;

constructor(
    public afAuth: AngularFireAuth
) {    
    this.afAuth.authState.subscribe(user => {
      if (user) {
        localStorage.setItem('user', JSON.stringify(this.userData));
        JSON.parse(localStorage.getItem('user'));
      } else {
        localStorage.setItem('user', null);
        JSON.parse(localStorage.getItem('user'));
      }
    })
  }
Makyen
  • 31,849
  • 12
  • 86
  • 121
0

Write your code inside setTimeout. Because this problem happens after refreshing page.

setTimeout(()=>{
console.log("currentUser", firebase.auth().currentUser);
},1000)
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Yakupas
  • 96
  • 5