Hi there,
I’m making a login for my app using angularfire2 for the user authentication and I’m having a bit of trouble logging out.
The login works perfectly and so does the registration, but when I come to log out I get this error:
Error: permission_denied at /profile/AhGIYnvAz4akV94D6RGy26rhCcl1: Client doesn't have permission to access the desired data.
I’m not sure why it’s happening, I’ve had a look around google, from what I can see it’s something to do with not unsubscribing from an observable? but I can’t track it down or fix it.
here is my code to call my dashboard page (leaving that page causes the error to occur.)
There’s something active on your page that is making a request of Firebase. The request continues when logged out, violating the database rules. Unsubscribe from all Observables before calling auth.signout().
Thanks for the reply! do you know if there’s a way to figure out which one? because I’ve tried unsubscribing from the initial auth observable and from the one getting the profile within that one, and I’ve not had any luck, it just stopped the page loading. I also tried it on the IonViewWillLeave method and it dodn’t seem to work either! I’m a bit stuck on this one, if you want I can provide a bit more code to give you a better idea what’s going on? Thanks
You have to unsubscribe to this before you call auth.signOut().
I usually have multiple subscribe so I have a array of them in my pages (_subs: Subscription[])
let sub: Subscription = this.profileData.valueChanges().subscribe(data=>{
this.profile = data;
});
this._subs.push(sub);
And then before you log out or go to another page you can do
for(let sub of this._subs) {
if (sub) {
sub.unsubscribe();
}
}
// call your logout function here
There might be a better way (or a cleaner) way to do it (What do you think @AaronSterling?)
Hey, thanks this is a really good way of dealing with it! Apologies, I’m a bit new to Ionic and angular in general. I’ve tried implementing it and this is what I’ve got:
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { AngularFireObject, AngularFireDatabase } from 'angularfire2/database';
import {AngularFireAuth} from 'angularfire2/auth';
import { Profile } from '../../models/profile';
import { ToastController } from 'ionic-angular';
import { LoginPage } from '../login/login';
import { FireBaseProvider } from '../../providers/firebase/firebase';
@IonicPage()
@Component({
selector: 'page-dashboard',
templateUrl: 'dashboard.html',
})
export class DashboardPage {
profileData: AngularFireObject<Profile>;
profile: any;
_subs: Subscription[] = [];
constructor(private toast: ToastController,
private afAuth: AngularFireAuth,
private afDatabase: AngularFireDatabase,
public navCtrl: NavController,
public navParams: NavParams,
private fb: FireBaseProvider) {
}
ionViewDidLoad() {
let authSub = this.afAuth.authState.take(1).subscribe(data => {
if(data && data.email && data.uid){
this.toast.create({
message: `welcome to APPNAME, ${data.email}`,
duration: 3000
}).present();
this.profileData = this.afDatabase.object(`profile/${data.uid}`);
let sub: Subscription = this.profileSub = this.profileData.valueChanges().subscribe(data=>{
this.profile = data;
});
this._subs.push(sub);
}
});
this._subs.push(authSub);
}
ionViewWillLeave(){
// authSub.unsubscribe();
// profileSub.unsubscribe();
}
logout(){
for(let sub of this._subs) {
sub.unsubscribe();
}
this.fb.logout().then(() => {
this.navCtrl.setRoot('LoginPage');
}
);
}
}
I’m probably missing something really simple, but it doesn’t seem to be working and still throws that error.
I removed “subscribe” from my pages, and only use it in providers. I use the async pipe in pages. That way, Angular handles the unsubscribes for me.
Also, I don’t directly listen to the streams from providers. Instead, I put them in a meta-observable with a gate, sort of like listenIfAuth<T>(obs: Observable<T>): Observable<T> { // return an observable that checks if af.auth is truthy, and if so, returns the value of the profile stream}. That way, it’s physically impossible to forget to unsubscribe from a stream before logout.
I bit too next level to understand what you mean even (at least for me). So if I get you right this listenIfAuth function is only used in providers and is the “in-between” firebase observables and your subscribes in your providers.
And the pseudo code for it would be
listenIfAuth<T>(obs: Observable<T>): Observable<T> {
if (isAuthed) {
return obs;
}
return ???; //What do you put here?
}
My last post was a bit vague, sorry. There are two ways the OP’s error can occur. Either you start listening to something after you’re not allowed to, or you continue listening to something after you aren’t allowed to. An approach like your subs array takes care of the second issue. But the first issue still matters, for example if you have a provider that might initiate a subscribe request asynchronously with login or logout. (Ask yourself this about your code: what if a user logs in and then logs out before your subscriptions start? Do you catch that behavior? Or are you assuming nobody logs out until your subscriptions are live and in the subs array?)
So listenIfAuth is a way to make sure that an unauthorized subscription does not start.
I return Observable.from([]) usually, but you could choose any dummy value, or a special value you recognize like Observable.from(['not authorized']) if you want you page to have the option to tell the user something.
I think the OP is making a much simpler error somewhere. I wouldn’t recommend he follow what I’m saying to you yet. I think it would be best to develop more of a feel for Observables before getting too fancy with them. No offense intended to the OP. But to answer your question, yes that looks fine.
No offense taken! no worries, that looks like a great way to deal with it so seriously thank you, I’ll keep trying, and give your method a go too and see how it goes! Thanks both of you for helping with this, I’ll update once I’ve solved it.
Hey @angusallman the way I got around this was to assign the Observable to a subscription class variable, then on the IonViewWillLeave page lifecycle, unsubscribe to this.