Ionic 4: Log in as a different user, previous user's data still shows

My Ionic 4 app uses Firebase to authenticate, and data is stored in local storage after logging. Here’s the flow:

  • User A logs in
  • User A’s info is retrieved from Firebase, then stored locally in storage
  • User A logs out, and his info is cleared from storage by calling storage.clear()
  • User B logs in, but can still see User A’s info

How can I completely clear a user’s info after logging out, so that it doesn’t show when another user logs in???

The code that reads data from the storage to display is in the ngOnInit() function. I noticed that when logging in again as a different user, ngOnInit() isn’t called, so could that be why it’s not showing the new user’s data?

hi @obinnae please show your code

Here’s the code…

the authentication/login service

///auth.service.ts
loginUser(value){
   return new Promise<any>((resolve, reject) => {
     firebase.auth().signInWithEmailAndPassword(value.email, value.password)
     .then(
       res => resolve(res),
       err => reject(err))
   })
  }

logoutUser(){
    return new Promise((resolve, reject) => {
      if(firebase.auth().currentUser)
      {
        this.storage.clear()
        .then(()=>{
          console.log("storage cleared. logging out");
          return firebase.auth().signOut()
        })
        .then(() => {
          resolve(true);
        }).catch((error) => {
          reject(error);
        });
      }
    })
  }

After logging, the following retrieves the user’s business info…

///home.ts

//The following code used to be in ngOnInit(), but wasn't called after relogging in as a different user, so I moved it here. It gets called each time, but .getFromLocal() doesn't call the service's constructor after the 2nd login
ionViewDidEnter()
  {console.log("businessaccount ionViewDidEnter");
    this.segment = 'requests';
    this.businessSrv.getFromLocal().subscribe((b)=>{
      this.business=b;
      console.log(this.business);
      if (this.business) this.getInfo();
    })
  }
//business.service.ts

 private businessSubject:BehaviorSubject<Business> = new BehaviorSubject<Business | undefined>(undefined);
//this constructor is called after the first login, but not after logging out and logging in as a different user
  constructor(private fbServ: FirebaseService, private _storage: Storage) 
  {
     console.log("LOGGED IN USER: ",this.fbServ.getLoggedinUser());
   this.fbServ.get('businesses', ['userId|==|'+this.fbServ.getLoggedinUser().email]) //gets the business info from Firebase
    .subscribe((biz)=>{
      if (biz == undefined) return null;
      console.log(biz[0]);
      this.businessSubject.next(biz[0] as Business);
      return this._storage.set('business',biz[0]);
    })
  }

getFromLocal(): Observable<Business | undefined>
  {
    return this.businessSubject.asObservable();
  }

  setInLocal(biz: Business): void
  {
    this.businessSubject.next(biz);
    this._storage.set('business',biz);
    console.log("biz saved:", biz);
  }

Thanks.

I’m going to start with the smaller problems and work towards the bigger ones:

The good news is that you’ve already got the infrastructure in place for communicating externally-initiated changes to the active Business to places like HomePage. However, the chain seems to be broken by the fact that FirebaseService.getLoggedInUser doesn’t itself expose an Observable (that can be triggered upon logout). If you augment getLoggedInUser with a watchLoggedInUser that exposes an Observable<User>, and somehow have logoutUser cause it to emit undefined, then BusinessService can subscribe to that in its constructor, instead of simply taking a snapshot.

hi @obinnae try this example

for User a clear data

User A logs in
User A’s info is retrieved from Firebase, then stored locally in storage.
User A logs out, and his info is cleared from storage by calling storage.clear()

this.storage.set('LocalStorageKeyName',this.emptyarray).then(
      (res)=>{
        console.log(res);
  });

for User B store data
User B logs in, but can still see User A’s info

this.storage.set('LocalStorageKeyName',this.UserB_retrieved_from_Firebase).then(
      (res)=>{
        console.log(res);
  });

what would be the best strategy… Localstorage or cookies modules to store the user data for security purposes

I prefer JWT to cookies for everything, because they’re stateless and are built from well-tested cryptographic blocks. As for how to store them client-side, I have yet to encounter a situation where I was tasked to worry about multiple users sharing a physical device, so I just save them using Ionic Storage. If you do care about that situation (white hat and black hat using the same Ionic app on the same device), you could look into something like NativeStorage, although I tend to simply assume that anybody with physical access to a device can get at anything stored on it.

I hope this won’t be considered hijacking @obinnae post… I’ve implemented JWT for an e-commerce ionic app am building. Is it necessary to have refresh tokens and what would necessitate them… Or what strategy would you implement

Thanks for your help, @rapropos. I’ve followed your bulleted suggestion, though yet to fix the leaking subscriptions. I’m currently unsubscribing from ngOnDestroy(). How else could you unsubscribe the subscriptions besides the until-destroy plugin?

I also updated the authservice with the follownig

export class AuthenticateService {
 
  user: Observable<firebase.User>;
  constructor(private storage: Storage, private firebaseAuth: AngularFireAuth)
  {
    this.user = firebaseAuth.authState;
  }
///...etc

so I can just call the following instead of the snapshot of getLoggedInUser()

this.authService.user.subscribe((u)=>{
console.log(u) /*displays firebase user object if logged in, null if logged out*/
})

I got the idea from this article.

That’s fine; the UntilDestroy decorator does basically that behind the scenes.

I did something a bit hackier than this:

this.afAuth.signOut().then(() => {
    Storage.clear();
    location.reload();
});

Not that you (or anybody else) need care about my opinion, but I stand by my characterization of this as sending your car to the junkyard when the “check engine” light comes on.

Yeah, that’s totally fair. My decision on this was that:

  1. It’s pretty rare for a user to logout of an application so this will run extremely infrequently.
  2. If a user wants to logout of a installed app I’m assuming the REALLY want to reset everything and clear everything out, reload does that for sure.
  3. It works and I’ll come back later and refactor if needs be.

If I ever ship this app as an I’m browser web app then I would change it.