Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logout permission error related to subscribed observable

I have a component where I've subscribed to an observable to pull data from Firebase. The relevant subscription code looks something like:

[Note: updated/corrected code below.]

// in my component
ngOnInit() {
  // this.af is an injected AngularFire2 instance
  this.tasks = this.af.database.list('/tasks')
    .subscribe(tasks => this.tasks = tasks);
}

The user must be authenticated to view this component, and I have a route guard in place that ensures this.

However, when I logout I get this error:

Client doesn't have permission to access the desired data

I'm logging out with something like:

// this.auth is an injected FirebaseAuth instance
this.auth.logout();
this.router.navigate(['/login']);

The error occurs before the router navigate method is called. The problem is that I have this active tasks observer subscription with an open web socket connection that errors out right after the logout method is called because the user is no longer authenticated.

The tasks subscription and the logout functionality are in different parts of my app. If they were in the same place, I guess I could call the unsubscribe method on the subscription when I logout. But even so, it seems messy to stick this subscription canceling logic in my logout method. I'm not quite sure how to deal with this.

How do I handle canceling the subscription to the tasks observer on logout?

Updated code after Alexander Leonov's comment about duplicate assignment

Alexander pointed out that I was assigning to the this.tasks property twice. Good point! This bit of code was incorrect. I wasn't assigning the subscription to a property. The code should have looked like this:

ngOnInit() {
  // this.af is an injected AngularFire2 instance
  this.af.database.list('/tasks')
    .subscribe(tasks => this.tasks = tasks);
}

Thanks for pointing that out, Alexander.

Solution: What I ended up doing

I was able to resolve this partly with Alexander Leonov's comment about unsubscribing with ngOnDestroy in my component.

Using his code in my tasks component:

ngOnInit() {
  this.subscription = this.af.database.list('/tasks')
    .subscribe(tasks => this.tasks = tasks);
}

ngOnDestroy() {
  this.subscription.unsubscribe();
}

The other part of the solution involved subscribing to the AngularFire auth observable in my top level component, the AppComponent. If authenticated, it redirects to one route; if not, it redirects to another route:

this.authService.auth$.subscribe((currentUser) => {
  if (currentUser) {
    this.router.navigate(['/tasks']);
  } else {
    this.router.navigate(['/login']);
  }
});

When logging out, this subscription is notified and the user is redirected to the login route. This sequence of events ends up triggering the ngOnDestroy method on my component, which unsubscribes from the tasks AngularFire observable.

like image 989
Elliot Larson Avatar asked Dec 01 '16 00:12

Elliot Larson


1 Answers

You're messed up a bit. You use this.tasks both to keep a subscription:

this.tasks = this.af.database.list(...)

and later as a task storage:

subscribe(tasks => this.tasks = tasks)

You need to choose one side of the force. :)

To answer your question it would take a bit more information about how your application works. If you initialize connection in ngOnInit() when user logs in then my guess is that on logout your component get killed, so you can just implement OnDestroy as well and kill subscription there, something like this:

// in my component
ngOnInit() {
  this.subscription = this.af.database.list('/tasks')
    .subscribe(tasks => this.tasks = tasks);
}

ngOnDestroy() {
    this.subscription.unsubscribe();
}
like image 71
Alexander Leonov Avatar answered Sep 20 '22 17:09

Alexander Leonov