58

I'm trying to get my Passport local strategy working.

I've got this middleware set up:

passport.use(new LocalStrategy(function(username, password, done) {
    //return done(null, user);
    if (username=='ben' && password=='benny'){
        console.log("Password correct");
        return done(null, true);
    }
    else
        return done(null, false, {message: "Incorrect Login"});
}));

but then in here

app.use('/admin', adminIsLoggedIn, admin);

function adminIsLoggedIn(req, res, next) {

    // if user is authenticated in the session, carry on 
    if (req.isAuthenticated())
        return next();

    // if they aren't redirect them to the home page
    res.redirect('/');
}

it always fails and redirects to the home page.

I can't figure out why this is happening? Why won't it authenticate?

In my console I can see that's Password Correct is printing. Why won't it work?

CodyBugstein
  • 21,984
  • 61
  • 207
  • 363

21 Answers21

71

I had a similar issue. Could be due to the express-session middleware needed for passport. Fixed it by using middlewares in the following order: (Express 4)

var session = require('express-session');

// required for passport session
app.use(session({
  secret: 'secrettexthere',
  saveUninitialized: true,
  resave: true,
  // using store session on MongoDB using express-session + connect
  store: new MongoStore({
    url: config.urlMongo,
    collection: 'sessions'
  })
}));

// Init passport authentication 
app.use(passport.initialize());
// persistent login sessions 
app.use(passport.session());
Nitin
  • 946
  • 8
  • 15
18

FOR NEWBIES

I was facing a similar problem, where my isAuthenticated() function would return false.I lost a lot of time, hope this answer saves yours.

Some Common problems to watch out for,

  1. Middleware setup order (express-session > pass.initialize > pass.session ).
  2. Serialize and Deserialize methods needs to pass user on the request.(For more info I've posted an answer on this link.. Basics of Passport Session (expressjs)-why do we need to serialize and deserialize? ) if there's no user on request then isAuthenticated would return false.... and redirect to the PATH defined ......when false....
  3. The getUserById or findById function defined in the model(user.js) needs to have a User.findById (and not User.findOne) function defined.(this function would load user on the request in every session)
Marc Gear
  • 2,757
  • 1
  • 20
  • 19
  • Thanks buddy, I searched a lot but wasn't find any solution, finally you give me solution. – Syed Muhammad Asad Jul 16 '18 at 18:46
  • I was using findOne() in findById() and now I replaced it with find() and it's working fine. Why there was the issue with findOne() – Syed Muhammad Asad Jul 16 '18 at 18:47
  • 2
    I actually need to use findOne instead of findById because i'm trying to do a case-insensitve lookup of the user's email. According to the docs findById is just syntactic sugar over findOne. I don't think requiring findById ought to be part of the accepted solution. – sarora Apr 14 '19 at 17:16
15

This could also be an issue with your client's POST/GET calls. I had this exact same issue but it turned out that I had to give fetch (which is what I was using) the option credentials:'include' like so:

fetch('/...', {
  method: 'POST',
  headers: myHeaders,
  credentials: 'include',
  body: ...
  ...})

The reason is because fetch doesn't support passing down cookies, which is necessary in this case.

Alex
  • 3,441
  • 4
  • 18
  • 30
  • Thanks Alex! You saved me a lot of time. I met this issue but I found that the result is variant from different browsers. So I am sure this is the fetch compatibility issue! – Huiyang Shan Jun 24 '19 at 07:06
  • Thanks Alex, if using axios. One might need to add { withCredentials: true } instead – Paschal Dec 09 '20 at 05:37
  • Thanks!!! if using express you must also in the cors middleware to include the origin and set credentials to true otherwise you will receive an error – Giovanni El Zelah Feb 28 '23 at 01:22
12

My problem was that i set cookie.secure to true even if data was not over https.

app.use(require('express-session')({
    secret: process.env.sessionSecret,
    cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
    },
    store: store,
    resave: false,
    saveUninitialized: false,
    cookie: { secure: false } // Remember to set this
}));

Remember to set cookies to false if you're not using https

cookie: { secure: false } // Set to false

Also if you do believe you have https remember to trust the proxy

app.set('trust proxy', 1) // trust first proxy
Thomas Lindauer
  • 1,271
  • 1
  • 14
  • 15
  • 1
    This worked for me. To add something: it is not necessary to set `cookie: { secure: false }` since that is the default value. – fjplaurr Jan 03 '20 at 10:02
8

I had the same issue by forgetting to add

request.login()

on

app.post('/login', 
    function(request, response, next) {
        console.log(request.session)
        passport.authenticate('login', 
        function(err, user, info) {
            if(!user){ response.send(info.message);}
            else{

                request.login(user, function(error) {
                    if (error) return next(error);
                    console.log("Request Login supossedly successful.");
                    return response.send('Login successful');
                });
                //response.send('Login successful');
            }

        })(request, response, next);
    }
);

Hopefully that might help for others that ended up here same reason as I did.

ozgeneral
  • 6,079
  • 2
  • 30
  • 45
  • In passport.authenticaticate user is coming false and info as missing credentials. can you know the mistake. please refer my question for explanation http://stackoverflow.com/questions/42898027/passport-js-local-strategy-custom-callback-showing-user-as-false-and-info-as-mi – charan tej Mar 20 '17 at 07:54
  • @OE1, Thank you. You saved me. – Vikram Jul 09 '17 at 12:38
  • 2
    Note: passport.authenticate() middleware invokes req.login() automatically. This function is primarily used when users sign up, during which req.login() can be invoked to automatically log in the newly registered user. – Epirocks Sep 13 '17 at 21:46
  • Thanks a lot man. It worked. So basically, whenever we are doing the authentication using Passport manually by passing a Callback, we have to explicitly Login the User and Passport won't do it automatically for us. – Ankur Thakur Apr 06 '23 at 07:25
3

There's a kink in passport.js that nobody really mentions but I found out. This is why you can create an account or sign in and it authenticates fine at first but later on you find out req.user is undefined or req.isAuthenticated() is false throughout the app.

After authenticating, passport.js requires you to reroute/redirect. That's how passport initializes the actual session.

  signIn(req, res, next) {
    passport.authenticate("local")(req, res, function() {
      if (!req.user) {
        console.log("User not found!");
      } else {
        res.redirect("/")
        console.log("signed in")
      }
    })
  }

If you don't reroute after authenticating, it won't even start your session as a req.user and req.isAuthenticated() will be false.

My app is a React and Node app but this is true for both Node apps and React/Node apps.

  • this is an intriguing answer. Is this requirement documented anywhere? it doesn't add up to me! I've actually tried the res.redirect("/") in my code and it fails in the browser (it doesn't like the response) – Andy Lorenz Jun 04 '20 at 17:19
  • can you provide the source of info in which you found out that a redirect is necessary. i feel like the passport docs isnt the best :( – benwl Jul 01 '22 at 09:01
2

I also had the same problem, could not find any solution on the web but i figured it out.

app.use(require("express-session")({
secret: "This is the secret line",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({extended: true}));

express-session requirement and use should be before any other use. Try this i am sure this would work, worked for me!!

karan525
  • 187
  • 1
  • 7
  • possibly duplicate: https://stackoverflow.com/questions/29111571/passports-req-isauthenticated-always-returning-false-even-when-i-hardcode-done – Siful Islam May 11 '19 at 08:12
1

I know its late, but I face this issue with FB login strategy. It was working fine, until suddenly it stopped working and that too just in Safari. I broke my head around all of the above solutions and nothing seemed to work. Finally chrome web console gave away a clue, wherein it still worked on chrome, then. The warning was this:

A cookie associated with a cross-site resource at http://www.facebook.com/ was set without the SameSite attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with SameSite=None and Secure.

Only then i realized that i shouldn't set Samesite: true in the express session as it will not set the facebook cookie for login. After days of hacking, I fixed this issue by changing the samesite to "none".

Hope it helps someone, who encounters this issue in the future.

  • It helped me! Thanks. I fixed my https site not having cross site req authentication with this. My code here: https://github.com/vcvcchaturvedi/job-helper-be/blob/master/index.js – vinaych Jul 17 '21 at 08:17
  • Also used withCredentials: true in client side invocation of requests! – vinaych Jul 17 '21 at 08:30
1

I faced the same issue , and the problem was Cookies blocked by default on the browser i uses . To fix it i add my app URL to the allowed sides for use cookies .

Al-Mustafa Azhari
  • 850
  • 1
  • 9
  • 24
1

For me, I did what all the answers said, but it wouldn't log in, at least most of the time. However, it was strange because if I throttled my network to 3G in Chrome developer tools, the login did work. My problem was that the session was not saving. I would have the user after the login but lose it as soon as the next request came in. My solution was to use the req.session.save callback (as suggested here) AND the req.logIn() callback:

router.post("/login", (req, res, next) => {
    passport.authenticate("local", {
        failureFlash: true,
        //failureRedirect: /login", // these don't work!
        //successRedirect: "/home", // because they redict too fast
    }, (err, user, info) => {
        if (err !== null || user === false) {
            req.session.save(() => {
                // failureRedirectin the callback here
                res.redirect("/login");
            });
        } else {
            req.logIn(user, err => {
                // save session before redirecting!
                req.session.save(() => {
                    // successRedirectin the callback here
                    res.redirect("/home");
                });
            });
        }
    })(req, res, next);
});

The race conditions were fixed by waiting until the passport session was saved before allowing the next request through the redirect.

Not sure if this is just for the local strategy. Using express-session.

Elijah Mock
  • 587
  • 8
  • 21
0

I fixed this issue by fixing my passport.deserializeUser. I'm using mongo native and since most of the examples use Mongoose i fell in to the _id trap once again.

So remember to make the _id a mongo ObjectID when reading the user in deserializeUser

passport.deserializeUser(function(user, done) {
    const collection = db.get().collection('users')
    const userId = new mongo.ObjectID(user);
    collection.findOne({_id : userId}, function(err, user) {
        if (err) done(err, null);
        done(null, user);
    });
});

My query was not finding the user since I did not make the id an ObjectID, and there was no errors indicated anywhere.

Mikko
  • 1,877
  • 1
  • 25
  • 37
0

I also was facing same problem, but @PVThomas gives me solution, as in here in Answers. My problem was with findById() method in deserialize(). I was using findOne() in findById() and then I replaced it with find() and now req.isAuthenticated() is working fine. My app wasn't saving req.session.passport.user, It was returning undefined and then after replacement of findOne() with find() it's saving user id in req.session.passport.user.

Syed Muhammad Asad
  • 208
  • 1
  • 2
  • 13
0

I also faced the same problem even though logging in was happening. The mistake I did was calling the middleware isLoggedIn before initializing the passport. So the sequence in which you write the code is quite important.Please see to it that the sequence is written in the right order. I had written in the following sequence

app.use(require('express-session')({
    secret:'short' ,
    resave:false,
  saveUninitialized:false,
  cookie:{secure:false}
}))
app.use(passport.initialize())
app.use(passport.session())
passport.use(new localstrategy(function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      if (user.password!=password) { return done(null, false); }
      return done(null, user);
    });
  }
))
passport.serializeUser(User.serializeUser())
passport.deserializeUser(User.deserializeUser())
app.use(isLoggedIn); 
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
0
app.use(
    session({
        secret: 'Our little secret.',
        resave: false,
        saveUninitialized: true,
        cookie: { secure: true } << it was extra for me
    })
);
depperm
  • 10,606
  • 4
  • 43
  • 67
  • Thanks for contributing an answer! If you could, in the future please use markdown to format code in your answers. Also, I'm not entirely sure this answers the question. You're using an entirely different middleware here. Please let us know how and why this answers the question. – btomw Mar 18 '20 at 15:44
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. You can find more information on how to write good answers in the help center: https://stackoverflow.com/help/how-to-answer . Good luck – nima Nov 07 '21 at 13:53
0

Resolved in my case, I also faced the same problem, but resolved just by reordering the code as mentioned below:

//--------------------------------

previous code :

app.use(flash())
app.use(session({
    secret: 'somesecret',
    resave: false,
    saveUninitialized: false
}))
// using the custom middleware for storing variable in response
app.use((req, res, next) => {
    res.locals.isAuthenticated = req.isAuthenticated()
    next()
})
app.use(passport.initialize())
app.use(passport.session())

//--------------------------------

Refactored code : (which fixed the problem):

app.use(flash())
app.use(session({
    secret: 'somesecret',
    resave: false,
    saveUninitialized: false
}))
app.use(passport.initialize())
app.use(passport.session())

// using the custom middleware for storing variable in response
app.use((req, res, next) => {
    res.locals.isAuthenticated = req.isAuthenticated()
    next()
})

//--------------------------------

0

I have to agree with @karan525. However, I made sure to test the portion of my code at different locations on my app page. Where it finally worked for me was towards the start but after any configuration files.

// Session 
app.use(session({
secret: 'not so secret',
resave: true,
saveUninitialized: true,
}));

// Passport middleware
app.use(passport.initialize());
app.use(passport.session());
AnEmptyBox
  • 11
  • 1
0

I believe your error is from the view, either ejs, pug or react. there is no name property in your form group corresponding to the arguments passed to the localstrategy callback function. This is what fixed it for me.

Prospee
  • 1
  • 3
0

I was facing the similar problem. I fixed it by changing the sequence of function calls.

// Call session middleware first
expressApp.use(session(....blah))

// Then initialize passport
expressApp.use(passport.initialize());
expressApp.use(passport.session());
Dinesh Verma
  • 585
  • 5
  • 8
0

Hey guys I would like to share my mistake here.

app.use(session({
    secret: process.env.SECRET,
    resave: false,
    saveUninitialized: true,
    cookie: { secure: true }    
  })

If you use this code make sure to change the code as follows,

cookie:{ secure :false}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Spark
  • 219
  • 2
  • 13
0

Set the secure key in the cookie to false

Leksyking
  • 322
  • 3
  • 5
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 05 '22 at 22:24
-2

If you wrap your routes like so:

module.exports = function(){

router.get('/',(req,res)=>{
 res.send('stuff');
  }

}

You have to pass "app and passport" to your routes like so:

module.exports = function(app,passport){

//routes n stuff

}
metal_jacke1
  • 405
  • 4
  • 10