3
votes

I am using Ionic2 with Angularfire2 to access Firebase Authentication.

I access the following rxjs/Observable:

chats.ts

  this.firelist = this.dataService.findChats();
  this.subscription = this.firelist.subscribe(chatItems => {
     ...
  });

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

dataService.ts

findChats(): Observable<any[]> {
        this.firebaseDataService.findChats().forEach(firebaseItems => {
           ...
           observer.next(mergedItems);
        });
}

firebaseDataService.ts

findChats(): Observable<any[]> { // populates the firelist
    return this.af.database.list('/chat/', {
        query: {
            orderByChild: 'negativtimestamp'
        }
    }).map(items => {
        const filtered = items.filter(
            item => (item.memberId1 === this.me.uid || item.memberId2 === this.me.uid)
        );
        return filtered;
    });
}

Also, in Firebase Authentication, I have the following Realtime Database Rules:

  "rules": {
      ".read": "auth != null",
      ".write": "auth != null",
  }

Problem

This works perfectly while a user is logged in (i.e. auth != null), but a soon as a user logs out, and auth == null, I get the following error:

ERROR Error: Uncaught (in promise): Error: permission_denied at /chat: Client doesn't have permission to access the desired data. Error: permission_denied at /chat: Client doesn't have permission to access the desired data.

Question

As you can see in the above code, I try to unsubscribe from the Firebase Authentication Service, but must be doing it incorrectly. If anyone can advise how I should unsubscribe in order to stop my app querying the Firebase database, I would appreciate the help.

2
Why don't you do this.firelist.unsubscribe()Vamshi
Hi Skeptor, thanks for the feedback. this.firelist is a rxjs/Observable<any>, and the api does not have an unsubscribe function. It does however, return a Subscription that does have one, so that's what I unsuccessfully try as shown above. reactivex.io/rxjs/class/es6/Observable.js~Observable.htmlRichard
I never unsubscribe so not sure about the syntax . But in my opinion you need to watch auth object status for every http call , instead of unsubscribing. I will provide a different way to write the same code and how i do in my app. ( I use aws, but they are almost same )Vamshi
Thank you, appreciate the assistance.Richard
What happens if a hacker logs in, and tries to access another users messages? That's where the Firebase Rules come in handy. You can only allow access to the messages that match the users uid. You can do something like: ` "chat": { "$key": { ".read": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid || true", ".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid || true" } },`Richard

2 Answers

1
votes

This is not an exact answer to the question, but my argument is we will never come to this issue if we do this other way.

P.S : I never used firebase , I use AWS so the firebase code might not be precise, we discuss about it .

Here is my version:

findChats(): Observable<any[]> { // populates the firelist
    return 
      this.authService.getCurrentUser()
        .flatMap(user => user === null ? 
          Observable.empty():        
          this.af.database.list('/chat/', {
             query: {
                orderByChild: 'negativtimestamp'
             }
           })
          .map(items => 
             return items.filter(
               item => (
                  item.memberId1 === this.me.uid || 
                  item.memberId2 === this.me.uid
               )
             ) 
          )
     );
}

Auth Service

public currentUser = new BehaviorSubject(null);

constructor(){

    firebase.auth().onAuthStateChanged(function(user) {
        if (user) {
           // User is signed in.
           currentUser.next(user);
        } else {
         // No user is signed in.
           currentUser.next(null);
        }
    });

}    

The code is very raw but I hope it conveys the idea. Here I am observing on the user authentication status and doing flatMap to fetch data. If user logs out, auth status change to null so in the UI data changes to empty list. Even if the user is not logged out , still he cannot see the data.


UPDATE :

Refactoring other answer:

findChats(){
   return Observable.merge(
       this.firebaseDataService
           .findChats()
           .flatMap(chats => Observable.from(chats)),
       this.localDataService
           .findChats()
           .flatMap(chats => Observable.from(chats))
   )
   .filter(chat => !!chat)
   .toArray()
   .map(chats => [
      ...chats.sort(
        (a, b) => 
           parseFloat(a.negativtimestamp) - parseFloat(b.negativtimestamp))
      ]
   );  
}

Except when you are caching response using Subject never use subscribe inside service.

0
votes

The unsubscribe is working correctly. The problem turns out that the following function I used, is causing it to subscribe twice for some reason:

findChats(): Observable<any[]> {
    return Observable.create((observer) => {
        this.chatSubscription2 = this.firebaseDataService.findChats().subscribe(firebaseItems => {
            this.localDataService.findChats().then((localItems: any[]) => {
                let mergedItems: any[] = [];
                if (localItems && localItems != null && firebaseItems && firebaseItems != null) {
                    for (let i: number = 0; i < localItems.length; i++) {
                        if (localItems[i] === null) {
                            localItems.splice(i, 1);
                        }
                    }
                    mergedItems = this.arrayUnique(firebaseItems.concat(localItems), true);
                } else if (firebaseItems && firebaseItems != null) {
                    mergedItems = firebaseItems;
                } else if (localItems && localItems != null) {
                    mergedItems = localItems;
                }
                mergedItems.sort((a, b) => {
                    return parseFloat(a.negativtimestamp) - parseFloat(b.negativtimestamp);
                });
                observer.next(mergedItems);
                this.checkChats(firebaseItems, localItems);
            });
        });
    });
}

For some reason the 3rd line above is called twice (even though the function is only called once). This is creating a second subscription, and the first subscription is never unsubscribed.

So it appears that the creation of the Observable is incorrect. I'm not sure what;s the correct way to create the Observable though.