User is not authenticated after res.redirect() - node.js

I'm having an issue where despite logging in successfully (as evidenced by the successRedirect handler triggering), the request is still not authenticated, so my authentication middleware sends us back to the login page again.
My routes look as follows:
// ROUTES
module.exports = function(app, passport) {
app.get('/home', isLoggedIn, function(req, res) {
res.sendfile('imconfusedindex.html'); // this never gets sent.
});
app.post('/login', passport.authenticate('ldap-auth', {
successRedirect: '/home',
failureRedirect: '/login'
}));
}
// route middleware to make sure a user is logged in
function isLoggedIn(req, res, next) {
// if user is authenticated, we'll all float on OK
if (req.isAuthenticated()) {
return next();
}
// otherwise, redirect them to the login page
res.redirect('/login');
}
And my passport configuration looks like this:
passport.serializeUser(function(user, done) {
done(null, user.user_id);
});
passport.deserializeUser(function(id, done) {
connection.query("select * from users where user_id = " + id, function(err, rows) {
done(err, rows[0]);
});
});
passport.use('ldap-auth', new LocalStrategy(
function(username, password, done) {
done(null, {user_id: 2, username: 'bob'});
})
);
As you can see in the passport configuration, I'm returning a dummy user every time. Doing more debugging shows that the request is being authenticated, but after the redirect, it is no longer authenticated.
Unsure what to do, any ideas would be appreciated.

Sigh I'm really stupid...the answer lurked in another question but when I read it I didn't quite put 2 and 2 together.
I'm lacking an express-session (didn't even include my server config here so even harder to debug). As soon as I set up an session, it worked.
In case someone else has this same issue, make sure that in your app config, you include something like:
var session = require('express-session');
app.use(session({secret: 'dontderplikeme'});
When you add in passport and include app.use(passport.session()), this is the session that it will be using to store credentials into.

Related

PassportJS authenticates user but returns 401 Unauthorized on subsequent requests

I'm writing one of my first applications in NodeJS so please bear with me. I've managed to successfully authenticate a user to our Active directory and I can see the connect.sid cookie being set and used on the subsequent requests.
Upon debugging the application by dumping the req object I can also see that the user variable has been set successfully. From the documentation I've read that seems to be a criteria for a successful session match?
However, the request is still getting a 401 Unauthorized.
To summarize:
The user is successfully authenticated after posting credentials /login.
Upon successful authentication the user is redirected to "/".
The "/" path replies with 401 Unauthorized.
Any ideas much appreciated. Code below.
const express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var passport = require('passport')
var ActiveDirectoryStrategy = require('passport-activedirectory')
// Setup the authentication strategy
passport.use(new ActiveDirectoryStrategy({
integrated: false,
ldap: {
url: 'ldap://myad.company.com',
baseDN: 'DC=domain,DC=company,DC=com',
username: 'user',
password: 'password'
}
}, function (profile, ad, done) {
ad.isUserMemberOf(profile._json.dn, 'Group', function (err, isMember) {
if (err) return done(err)
return done(null, profile)
})
}));
passport.serializeUser(function(user, done) {
done(null, JSON.stringify(user));
});
passport.deserializeUser(function(user, done) {
done(null, JSON.parse(user));
});
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(session(
{ secret: "password" }
));
app.use(passport.initialize());
app.use(passport.session());
// For debugging purposes
app.use(function (req, res, next) {
console.log(req)
next()
})
// The login page posts a form containing user and password
app.get("/login", (req, res) => {
res.sendFile(__dirname + '/public/index.html');
})
// Handler for the login page. Receives user and password and redirects the user to /
app.post('/login',
passport.authenticate('ActiveDirectory', {
failWithError: true,
successRedirect: "/",
failureRedirect: "/login"
}
), function(req, res) {
res.json(req.user)
}, function (err) {
res.status(401).send('Not Authenticated')
}
)
// This is where the issue happens. The page returns "Unauthorized".
// Using console.log(req) shows that the user property has been set to the req object.
// However, for some reason it still fails.
app.get('/',
passport.authenticate('ActiveDirectory', {
failWithError: true,
}
), function(req, res) {
res.send("test")
}, function (err) {
res.status(401).send('Not Authenticated')
})
Found what I did wrong!
The .authenticate method is only used to validate credentials, not to validate a session.
So this:
app.get('/',
passport.authenticate('ActiveDirectory', {
failWithError: true,
}
), function(req, res) {
res.send("test")
}, function (err) {
res.status(401).send('Not Authenticated')
})
Should become:
app.get('/', function(req, res, next) {
// This is verifying that the user part has been populated,
// which means that the user has been authenticated.
if (req.user) {
res.send('Returning with some text');
} else {
// If the user property does no exist, redirect to /login
res.redirect('/login');
}
});
Another thing that I changed was the serialize/deserialize functions:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
This removes redundant serializing/deserializing.
These articles really helped me understand the flow:
http://toon.io/understanding-passportjs-authentication-flow/
https://www.airpair.com/express/posts/expressjs-and-passportjs-sessions-deep-dive
Hope it helps someone else!
/Patrik

Passport throws a 500 internal server error at every request

I'm at a complete loss with this. I can only assume it's passport because when I comment out its initialization everything magically works again. I have 2 routes currently setup. One is a get request that requires a user to be logged in and the other is a post request that actually does the logging in.
in my app.js file my passport setup looks like this:
var sessionStore = new MySQLStore(options);
//handles cookie/session creation
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: config.sessionKey,
resave: false,
store:sessionStore,
saveUninitialized: false,
cookie: {
//secure: true,
maxAge:24*60*60*1000 //1 day in milliseconds
}
}));
app.use(passport.initialize());
app.use(passport.session());
require('./services/passport');
//initiate route handlers
app.use('/login', require('./routes/authRoutes'));
app.use('/tiles', require('./routes/tileRoutes'));
I am using a local strategy as my users won't be using any kind of social app to login. I configured another file passport.js to actually handle the passport setup. I am using the sequelize database in order to verify users. This whole process looks like this:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
passport.serializeUser((user, done)=>{
done(null, user.id);
});
passport.deserializeUser((user, done)=>{
done(null, user.id);
});
passport.use(new LocalStrategy(function (username, password, done) {
const db = require('../models');
db.user.findOne({ where: {username: username} })
.then((user)=>{
if(!user){
return done(null, false);
}else{
// if(user.dataValues.hashed_password === password)
// return done(null, user);
bcrypt.compare(password, user.dataValues.hashed_password, function(err, res) {
if(res === true){
return done(null, user);
}else{
return done(null, err);
}
});
}
});
}));
signing people up, creating the session, and storing the session id all seem to be happening, however when I start making get/post requests on my front end with axios (I'm using react) I continually get a internal server error. I've tried catching this in everyway I can think of, breaking at definite points, an console.logging out but I just get the same message:
[0] GET /tiles 500 8.049 ms - 2
[0] GET /tiles 500 2.757 ms - 2
or from the console
GET http://localhost:3000/tiles/ 500 (Internal Server Error)
the actual get request looks like this (i havent' put much in till I know it works):
const express = require('express');
const router = express.Router();
router.get('/', isLoggedIn, (req, res)=>{
debugger;
res.send({hi:'there'})
});
function isLoggedIn(req, res, next) {
debugger;
if (req.isAuthenticated()) return next();
console.log('failure');
res.redirect('/login')
}
module.exports=router;
The deserializeUser should call the function to find user by id here, then pass it to done, the first param is userId as serializeUser return the user.id to session store. Example:
passport.deserializeUser((id, done)=>{
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(null, user);
}).catch(done);
});
});

Passport js serializeUser & deserializeUser

I have read everything on the argument but still cannot understand it. The documentation on the Passport Js web site is very vague.
I am using Passport JS with the passport-ldapauth Strategy. I do not have a Database. I obviously don't want to hit the LDAP server on each request. I would like to authenticate the user the first time on the POST /login route using the passport strategy with LDAP, store the user in the session and on each subsequent requests I just want to check if the user is already logged in.
I am trying to use the session but I cannot understand how to use Passport + session with the serialize/deserialize flow. Every example I checked use a User.findOne in the deserializeUser function.
As of now I disabled the use of the session for Passport and I am using a custom middleware where I check if req.session.user != null. If that's the case the user is already logged in and I hit next(). Otherwise redirect to login.
Here is some code (for sake of simplicity I deleted the code not related to the question):
Passport configuration:
var express = require('express'),
session = require('express-session'),
passport = require('passport'),
LdapStrategy = require('passport-ldapauth');
var app = express();
var LdapStrategyOptions = {
server: {
url: '<url>',
bindDN: '<dn>',
bindCredentials: "<pwd>",
searchBase: '<searchBase>',
searchFilter: '<filter>'
}
};
passport.use(new LdapStrategy(LdapStrategyOptions));
app.use(cookieParser());
app.use(session({
store: new LokiStore({autosave: false}),
resave: false,
saveUninitialized: false
secret: env.get("SESSION_SECRET")
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
Routes:
// LOGIN ROUTE
app.get('/login',
function(req, res) {
res.render('login');
});
// LOGIN HANDLER ROUTE
app.post('/login',
passport.authenticate('ldapauth', { session: false }),
function(req, res) {
req.session.userId = req.user.cn;
req.session.user = {
"userId": req.user.cn,
"displayName": req.user.displayName
};
res.redirect('/');
});
// LOGOUT ROUTE
app.get('/logout',
function(req, res) {
req.session.destroy(function(err) {
req.logout();
res.redirect('/');
});
});
// HOME ROUTE
app.get('/', isLoggedIn, function(req, res) {
res.render('home');
});
IsLoggedIn Middleware:
var isLoggedIn = function(req, res, next) {
if (req.session.user != null){
console.log("is auth ok '" + req.session.user.userId +"'");
return next();
}
console.log("redirect to auth/login");
res.redirect('/auth/login');
}
What am I missing? Is there any security fault in my setup?
Any help is appreciated.
From passportjs docs:
In a typical web application, the credentials used to authenticate a user will only be transmitted during the login request. If authentication succeeds, a session will be established and maintained via a cookie set in the user's browser.
Each subsequent request will not contain credentials, but rather the unique cookie that identifies the session. In order to support login sessions, Passport will serialize and deserialize user instances to and from the session.
Basically serializeUser is supposed to return a unique user identifier so you can deserializeUser back into JSON later.
So for your implementation you should probably do something along these lines:
DISCLAIMER: I have no experience with LDAP.
passport.serializeUser(function(user, done) {
//We can identify the user uniquely by the CN,
//so we only serialize this into the session token.
done(null, user.cn);
});
passport.deserializeUser(function(cn, done) {
//Directly query LDAP.
//I'm not sure passport caches the result (only calls deserializeUser for new sessions)
//but worst case you can cache the result yourself.
somehowLoadUserFromLDAPByCN(cn, function(err, user) {
done(err, {
userId: user.cn,
displayName: user.displayName
});
});
});
If you only need an id and a display name, it's totally fine to keep them in session. You should only load the full user profile when you need more fields.

passport-github how to extract session cookie to know that the user already logged in

I am building a passport-github auth to my application. but I think currently I don't know how to extract the cookie from request that would say user is already logged in. so everytime When i go to home page i get redirected to /login.
My code roughly looks like this:
passport.use(new GitHubStrategy({
clientID: authConfig.GITHUB_CLIENT_ID,
clientSecret: authConfig.GITHUB_CLIENT_SECRET,
callbackURL: "http://127.0.0.1:8080/auth/github/callback"
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
return db.user.findOne({where:{github_id:profile.id}})
.then(data=>{
if (data) {
return done(null,data);
} else {
return db.user.build({ github_id: profile.id }).save()
.then(()=>{
return db.user.findOne({where:{github_id:profile.id}})
})
.then(data=>{
return done(null,data);
})
}
});
}
));
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing
passport.serializeUser(function(user, done) {
console.log("serialize>>>>>", user.github_id);
done(null, user.github_id);
});
passport.deserializeUser(function(id, done) {
console.log("deserialize>>>>", id);
db.user.findOne({where:{github_id: id}})
.then(user=>{
done(null, user.toJSON());
})
});
I have established the session :
app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
And I have an isAuthenticated function that checks for req info:
function isAuthenticated (req, res, next) {
// If the user is logged in, continue with the request to the restricted route
console.log("req.user is>>>>", req);
if (req.isAuthenticated()) {
return next();
}
// If the user isnt' logged in, redirect them to the login page
return res.redirect("/index");
}
I am using this passport-github lib. I cannot get some useful information from reqseems
updated to include routes:
Here is the routes:
const isAuthenticated = require('./middleware/isAuthenticated.js');
router
.get('/index', query.renderIndex)
.get('/', isAuthenticated, query.displayRepos)
.post('/', query.queryRepoTopic)
.post('/trending', query.addRepo)
.post('/addTopic', query.addTopic)
.get('trending', query.updateScore);
router.get('/login', auth.loginPage)
.get('/auth/github',
passport.authenticate('github', { scope: [ 'user:email' ] }),
function(req, res){}
)
.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/login' }),
auth.signInRedirect
)
.get('/logout', auth.logout);
Here is the controller function that does the logic:
const loginPage = (req, res) => {
res.render('index');
}
// signin a user in
const signInRedirect = (req, res) => {
console.log("here in callback>>>");
console.log("req.user is>>>>", req.user);
//res.json("you have successfully logged in!");
res.redirect('/');
}
const logout = (req, res) => {
req.logout();
res.redirect('/index');
}
I see you have this route configuration:
const isAuthenticated = require('./middleware/isAuthenticated.js');
router
.get('/index', query.renderIndex)
.get('/', isAuthenticated, query.displayRepos)
...
If you want to call localhost:3000, and be redirected to auth/github when you are not logged in, you could change isAuthenticated function like this:
function isAuthenticated (req, res, next) {
// If the user is logged in, continue with the request to the restricted route
console.log("req.user is>>>>", req);
if (req.isAuthenticated()) {
return next();
}
// If the user isnt' logged in, redirect them to the github login page.
return res.redirect("/auth/github");
}
Wich means, when you try to call the '/', the isAuthenticated will check if the req.user was set (if (req.isAuthenticated())), if not, redirect to the /auth/github route.
Have you tried this?
Have it can help!

Prevent user authentication after signup with passport

I have set up a login/signup interface and mechanism using passport and express.js. The problem that I have is that after the signup of the user, we are redirected to the login page, but we can eventually change the URL and enter immediately in the user profile, but this is of course not expected and wanted. I would like that the user is not authenticated after the signup and that he needs to enter his/her credentials manually in the login page before entering to its profile.
router.get('/', isAuthenticated, function(req, res) {
res.render('library', {
// passing the id of and username the connecting user to the dust
userid: req.user._id,
username: req.user.userName
});
});
router.get('/library', isAuthenticated, function(req, res) {
res.render('library', {
// passing the id of and username the connecting user to the dust
userid: req.user._id,
username: req.user.userName
});
});
/* GET login page. */
router.get('/login', function(req, res) {
// Display the Login page with any flash message, if any
res.render('login', {
message: req.flash('message')
});
});
/* Handle Login POST
password.authenticate is used to delegate the authentication
to the login strategy when a HTTP POST is made to /login.
*/
router.post('/login', passport.authenticate('login', {
successRedirect: '/library',
failureRedirect: '/',
failureFlash: true
}));
/* GET Registration Page */
router.get('/signup', function(req, res) {
res.render('signup', {
message: req.flash('message')
});
});
/* Handle Registration POST
password.authenticate is used to delegate the authentication
to the signup strategy when a HTTP POST is made to /signup.
*/
router.post('/signup', passport.authenticate('signup', {
successRedirect: '/login',
failureRedirect: '/signup',
failureFlash: true
}));
and the isAuthenticated function/middleware is defined as follows
var isAuthenticated = function(req, res, next) {
// if user is authenticated in the session, call the next() to call the next request handler
// Passport adds this method to request object. A middleware is allowed to add properties to
// request and response objects
if (req.isAuthenticated()) {
return next();
} else {
res.redirect('/login');
}
}
What am I doing wrong?
Basically, after signup, I have a button which redirects to /, and if we are redirected to library (like it's happening to me), then the user should already be authenticated, but I don't want this...
There's at least two solutions to this:
Add session: false to the config object you pass to passport.authenticate('signup', {...}) as described in the passportjs documentation.
Don't use passport for signups. The main use case for passport is for authenticating (and establishing sessions) and DIY signup logic is more or less just a matter of copying the code from your signup passport middleware.

Resources