Search code examples
angularauthenticationaccess-tokensession-storage

Log out issue from second tab when log out from first tab in Angular


I am using sessionStorage to hold accessToken. My steps are as follows:-

  • Login to website
  • Click on duplicate option of tab. It will show the website in second tab.
  • Log out from first tab.
  • Expecting to logout from another tab when I refresh/hit any action on the second tab.

Actually it is not logging me out from another tab.

I added below code but it is not working as expected.

@HostListener('window:storage', ['$event'])
    onStorageChange(sv:StorageEvent) {       
      if(sv.storageArea == sessionStorage) 
      {
        let token = sessionStorage.getItem('accessToken');
        if(token == null || token == undefined)
            this.router.navigate(['/login']);
      }
    }

Please let me know if I am doing any mistake. I am currently using this code in home page. Is it the right location?


Solution

  • To communicate between tabs, you can make use of localStorage and still keep sessionStorage to store your sensitive data.

    Here is a small implementation of an authentication service which uses:

    • sessionStorage to store your user data (token or whatever)
    • localStorage to communicate with other opened tabs.
    • rxjs to trigger events from the service

    The idea is that your application listens for storage events. Then when you sign out, it sets a flag in localStorage, which other opened tabs can capture.

    import { Injectable } from "@angular/core";
    import { Subject, Observable } from "rxjs";
    
    @Injectable({providedIn:'root'})
    
    export class AuthenticationService{
    
      private eventSubject: Subject<boolean> = new Subject<boolean>();
    
      public readonly statusChanged$: Observable<boolean> = this.eventSubject.asObservable();
    
      private loggedIn = false;
    
      constructor()
      {
        window.onstorage = () => { //
        {
          let loggedIn = sessionStorage.getItem('accessToken') !== null;
    
          if(localStorage.getItem('signOut'))
          {
              loggedIn = false;
          }
    
          if(this.loggedIn !== loggedIn)//Don't trigger event if no change
          {
            this.loggedIn = loggedIn;
            this.eventSubject.next(loggedIn);
          }
        };
      }
    }
    
      public logIn()
      {
        //Do your business to login and obtain a token before here...
        localStorage.removeItem('signOut');//clear flag in local storage
        sessionStorage.setItem('accessToken', 'token');//save token in session storage
      }
    
      public logOut()
      {
        localStorage.setItem('signOut', 'true'); //trigger flag
        sessionStorage.removeItem('accessToken'); //Remove token from session
      }
      public isLoggedIn()
      {
        return this.loggedIn;
      }
    }
    

    You can just use that service in your components

    constructor(private authSvc:AuthenticationService)
    {
        this.authSvc.statusChanged$.subscribe(isLoggedIn=>
        {
          //Do whatever you want
    
        });
    }
    

    Here is a stackblitz demo.