Search code examples
node.jsexpresspassport.jspassport-local

How to redirect to home page in second tab after session is destroyed in first tab if the application is running in 2 tabs in express js server


I have created a basic login page ,after successful login ,the user is redirected to to success page . In the success page their is an a tag which redirects to /logout via get request and destroy the session and redirects to home page . My question is that if a user has opened 2 instances of the application and did logout from one tab then another instanace(tab) is working normally until i refresh the page then it shows forbidden . So what can i do to achive the logout from both the tabs simontaniously like facebook or amazon does here is the code :

const express = require('express');
const url= require('url');
const db=require('./dao.js');
var bodyParser = require('body-parser')
const session = require("express-session");
const app= express();
const nunjucks=require("nunjucks");
const path = require('path');
const Pin=require('./models/pin');
const User=require('./models/users');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
app.use(bodyParser.urlencoded({ extended: false }));

app.use(express.static(path.resolve(__dirname,'public')));
nunjucks.configure(path.resolve(__dirname,'views'),{
    express:app,
    autoscape:true,
    noCache:false, 
    watch:true
}); 
app.use(session({
    secret: "session",
    resave: false,
    saveUninitialized: true,
    cookie: { secure: false }
  }));
  
//passoprt consfig
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function (user, done) {
    done(null, user.id);
  });
  passport.deserializeUser(function (user, next) {
    next(null, user);
  });

  

passport.use('local', new LocalStrategy((username, password, done) => {
  
    User.findOne({ username: username }, (err, user) => { 
      if (err) { return done(err); }
      if (!user) { return done(null, null, { message: 'No user found!' }); }
      if (user.password !== password) {
        return done(null, null, { message: 'Username or password is incorrect!' });
      }
  
      return done(null, user, null);
    });
  }
  ));
  function isAuthenticated(req, res, next) {
    if (req.isAuthenticated()) {
      next();
    } else {
        req.session.destroy();
      res.status(403).send('Forbidden');
    }
  }  
   
 

app.get("/",(req,res)=>{
    console.log("hi"); 
    res.render("validate.html",{});
    });
    app.post('/login', (req, res) => {
        // passport.authenticate('local', { successRedirect: '/admin', failureRedirect: '/login' }
        passport.authenticate('local', function (err, user, info) {
          //  console.log(err,user,info);
          if (err) {
            res.render('validate.html', { error: err });
          } else if (!user) {
            res.render('validate.html', { errorMessage: info.message });
      
          } else {
            //setting users in session
            req.logIn(user, function (err) { 
              if (err) {
                res.render('/', { error: err });
              } else {
                res.redirect('/success');
              }
            })
          }
        })(req, res);
      });
      
app.get('/success',isAuthenticated,(req,res)=>{
    res.render("index.html",{});
})
app.get("/logout", function (req, res) {
    req.session.destroy();
    res.redirect('/');
});

app.get("/pincode",(req,res)=>{
    console.log(req.query.pincode);
    let pin=req.query.pincode;
    let pattern= /^[0-9]*$/;
    
if(pattern.test(pin)){
    Pin.find({pincode:pin},(err,data)=>{
        if(err)
        {
            console.log("error");
        }
        else
        {
            console.log(data);
            //res.render('index.html',{data:data}); 
            res.status(200).send(data);
            //res.status(200).json(data);
        }
    });
}
else{
   res.status(200).send("Nan");
   //res.render('index.html',{nomessage:"Not a no"}); 
                            
} 
    
    // res.redirect('http://google.com');
   
});
app.listen(80,()=>{
    console.log("App running at port 80");
});

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog==" crossorigin="anonymous" />
    <link rel="stylesheet" href="css/style.css">
    <title>Hello, world!</title>
  </head>
  <body>
      <div class="div1">
        <div class="container-sm">
            <div class="row">
                <form action="/login" method="POST" class="col-12" id=form autocomplete="off">
                  <p class="text-danger">{{message}}</p>
                  <p class="text-danger">{{error}}</p>
                  <p class="text-danger">{{errorMessage}}</p>

                    <input type="text" name="username" id="pin" placeholder="enter email" >
                    <input type="password" name="password" placeholder="enter passowrd">
                    <button type="submit" id=btn>login</button>
                  
                </form>
                
            </div>
            

        </div>
    </div>
        
    
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" 

<!-- begin snippet: js hide: false console: true babel: false -->


Solution

  • This blog might help,

    https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/

    Based on the blog,

    What will happen if I am logged in on different tabs?

    One way of solving this is by introducing a global event listener on localstorage. Whenever we update this logout key in localstorage on one tab, the listener will fire on the other tabs and trigger a "logout" too and redirect users to the login screen.

    window.addEventListener('storage', this.syncLogout) 
    
    //....
    
    
    syncLogout (event) {
      if (event.key === 'logout') {
        console.log('logged out from storage!')
        Router.push('/login')
      }
    }
    

    These are the 2 things we now need to do on logout:

    Nullify the token Set logout item in local storage

    async function logout () {
      inMemoryToken = null;
      const url = 'http://localhost:3010/auth/logout'
      const response = await fetch(url, {
        method: 'POST',
        credentials: 'include',
      })
      // to support logging out from all windows
      window.localStorage.setItem('logout', Date.now())
    }
    view rawlogout2.js hosted with ❤ by GitHub
    

    In that case whenever you log out from one tab, event listener will fire in all other tabs and redirect them to login screen.