I'm having an issue with Passport.js using the ActiveDirectory strategy where I'm successfully authenticating but fail to maintain a session on subsequent requests. After authentication, any call to req.isAuthenticated() returns false. Additionally, no cookie is being set on the client's browser. I've spent the day pouring over similar posts but so far none of the suggested solutions have worked.
Some observations to note:
-Authentication is always successful and returns the req.user to client.
-The console.log in serializeUser() always fires and correctly logs the user record.
-The console.log in deserializeUser() never fires, I'm not sure when this function is supposed to be called but it doesn't appear to be?
-After logging in, each time I make a call to my /test endpoint the non-authenticated condition fires.
// Passport Requires
const passport = require('passport');
const session = require('express-session');
var ActiveDirectoryStrategy = require('passport-activedirectory');
// Setup
app.use(session({
secret: 'secret',
resave: false,
saveUninitialized: false,
cookie: { secure: false, maxAge: 600000 }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log('userStrategy -- serialized:', user)
done(null, user);
});
passport.deserializeUser(function(user, done) {
console.log('userStrategy -- deserializeUser', user)
done(null, user);
});
passport.use(new ActiveDirectoryStrategy({
integrated: false,
passReqToCallback: true,
ldap: {
url: "url",
baseDN: "baseDN",
username: `username`,
password: `password`
}
}, function (req, profile, ad, done) {
ad.isUserMemberOf(profile._json.dn, 'Access Group', function (err, isMember) {
console.log('isMember:', isMember)
if (err) {
return done(err)
} else {
return done(null, profile)
}
})
}))
// Login Route
app.post('/login',
passport.authenticate('ActiveDirectory', { failWithError: true }),
function (req, res) {
console.log('Authenticated');
return res.status(200).send(req.user);
}, function (err) {
console.log('Not Authenticated');
return res.sendStatus(401).send(err);
}
)
// Test endpoint to check whether user is authenticated
app.get('/test', function(req, res) {
if (req.isAuthenticated()) {
res.send('Youre authenticated!')
} else {
res.send('Youre not authenticated!')
}
})
Appreciate any thoughts on the problem -- thanks!
I've figured out the issue which has nothing to do with Passport or server setup.
In my client-side API call to login I've been using fetch() but I did not have credentials: 'include' option on the initial POST to login. Once I added this option, a cookie was sent back to the client browser and future API calls to isAuthenticated() returned true.
Make sure to have credentials: 'include' on ALL of your API calls, including the initial POST to login.
Related
I'm a begginer with node js and I'm trying to check if the user is authenticated after he registers.
I'm using passport, passport-local and passport-local-mongoose to save the user in a mongoDB database.
This is how the user gets registered:
app.post("/register", function (req, res) {
const username = req.body.username;
const password = req.body.password;
User.register({username: username}, password, function (err, user) {
if (err) {
console.log(err);
res.redirect("/register");
} else {
passport.authenticate("local")(req, res, function () {
res.redirect("/drive");
});
}
});
});
And this is how I'm trying to check if the user is authenticated:
app.get("/drive", function (req, res) {
if (req.isAuthenticated()) {
res.render("drive");
} else {
res.redirect("/login");
}
});
If I'm using isAuthenticated() as a function call, this logic will not work and I'll always get redirected to the login page, if I'm using isAuthenticated (not a function call), this logic will work.
I don't understand what is the difference between these two (in the passport package context) and why one works and the other doesn't.
Managed to get this fixed, it was a pretty simple problem.
When I'm declaring my session options:
app.use(session({
secret: 'Our little secret.',
resave: false,
saveUninitialized: false,
cookie: {secure: true}
}))
I'm using cookie: {secure: true}, and since I'm working on a local enviroment with no HTTPS, as soon as I authenticate, I get deauthenticated due to the fact that the cookie that is supposed to be generated can only work under HTTPS.
Solution is to just set the cookie option to false and call isAuthenticated().
I'm working on integrating Auth0 into a MERN Stack app. The flow should look like this:
User clicks the login button which triggers Auth0Lock.show()
User fills in their credentials and clicks the submit button
The callback URL of the API is hit which logs the user in and redirects them back to the front-end app
(everything looks like it's working fine up to this point)
The front-end requests user information from the API
The front-end receives the information and redirects
This seems to be a fairly standard authentication flow. The problem is that when the front-end asks the back-end for user information, there's an error:
UnauthorizedError: No authorization token was found
My setup looks essentially like this:
// client-side config
const lock = new Auth0Lock(clientID, domain, {
auth: {
responseType: 'token',
audience: 'https://${domain}/userinfo',
redirectUrl: API_URL + '/api/users/callback',
params: {
scope: 'openid profile email' // no change
}
}
})
// server.js
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// [DB setup]
var sessConfig = {
secret: "[random string]",
cookie: {
sameSite: false
},
resave: false,
saveUninitialized: true
};
if(app.get('env') === 'production') sessConfig.cookie.secure = true;
app.use(session(sessConfig));
const {domain, clientID, clientSecret, callbackURL} = require('./config/auth0');
const passportStrategy = new Auth0Strategy(
{domain, clientID, clientSecret, callbackURL},
(accessToken, refreshToken, extraParams, profile, done) => done(null, profile)
)
passport.use(passportStrategy);
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));
app.use(passport.initialize());
app.use(passport.session());
// [routing]
// routes/users.js
router.get('/callback', (req, res, next) => {
passport.authenticate('auth0', (err, user, info) => {
if(err) return next(err);
if(!user) return next(info);
req.logIn(user, err => {
if(err) return next(err);
const returnTo = req.session.returnTo;
delete req.session.returnTo;
res.redirect(returnTo || clientRootURL + '/callback');
})
})(req, res, next);
})
router.get(
'/current',
require('cors')(),
authenticate,
(req, res) => {
res.json({
id: req.user.id,
name: req.user.name,
email: req.user.email
});
}
);
// authenticate.js
module.exports = jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${domain}/.well-known/jwks.json`
}),
audience: clientID,
issuer: `https://${domain}/`,
algorithms: ['RS256']
});
The vast majority of comes straight out of the Auth0 documentation.
I'm trying to get the user info from the /users/current endpoint after logging in and it says it can't find authorization. Does anyone have any idea how to get this to work?
You should be calling the /userinfo endpoint to get the user profile, or getting the info from the id_token. Take a look at this doc
https://auth0.com/docs/api/authentication#get-user-info
Every authenticated frontend call should contain:
headers: {
Authorization: `Bearer ${token}`,
},
where token should be:
const token = await getAccessTokenSilently();
getAccessTokenSilently is a public function of auth0 lib.
See: getAccessTokenSilently doc
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
Recently I've found tricki bug in my express js project and I would be grateful for help with debugging it.
This is related question, but answer is not resolving problem, because I want to specify maxAge and deleting this parameter from session configuration is not solution for me.
I am using Passport js + express-session
This is my session configuration:
var passport = require('passport');
var session = require('express-session');
var cookieParser = require('cookie-parser');
app.use(cookieParser());
app.use(session({
secret: 'MySecret',
resave: false,
saveUninitialized: false,
cookie : {
secure: false, // If it's true, login is not working as well
maxAge: new Date(Date.now() + (60 * 1000 * 30))
}
}));
app.use(passport.initialize());
app.use(passport.session());
To check that user is authenticated I'm using simple middleware:
middlewareObj.isLoggedIn = function(req, res, next) {
if (req.isAuthenticated()){
return next();
} else {
return res.redirect('/signin');
}
};
Passport local-signin:
passport.use('local-signin', new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function (req, email, password, done) {
var User = user;
var isValidPassword = function (userpass, password) {
return bCrypt.compareSync(password, userpass);
}
User.findOne({
where: {
email: email
}
}).then(function (user) {
if (!user) {
return done(null, false, req.flash('error', 'User does not exists'));
}
if (!isValidPassword(user.dataValues.userPassword, password)) {
return done(null, false, req.flash('error', 'Incorrect password.'));
}
return done(null, {user});
}).catch(function (error) {
return done(null, false, req.flash('error',error.message));
});
}
));
This is my logout route:
router.get('/logout',(req,res) => {
req.session.destroy(function(err) {
return res.redirect('/');
});
});
Standard user login is working fine, after submitting sign in form user is serialized than deserialized and isAuthenticated() method returns true.
But cookie configuration(maxAge exactly) is making some problems. When cookie lifetime expired, next user's login is not working anymore. isAuthenticated() method is returning false (user object does't exist inside req).
After restarting server, user can login normally, but when cookie maxAge is reached, next login is not working again. Passport is not showing any errors.
It likely has something to do with your maxAge value. The expressjs/sessions package docs point out that maxAge is "the number (in milliseconds) to use when calculating the Expires Set-Cookie attribute".
You're passing a Date to maxAge; it's expecting the number of milliseconds until the cookie expires.
I am new to using Passport authentication with express between my
server and client (on two different ports), and am having great
trouble understanding both the concept of maintaining session state
with Passport and also trying to retrieve req.session.passport.user
in order to determine if a request is sent from a user that has
successfully logged on.
Checking Persistent Session State with Passport
In my code, I have a route to my profile page that uses my own
authenticated function. My login route uses
passport.authenticate('local'), however. Should I be applying this
to ALL subsequent routes (like for my profile), or is my separate
authentication expected?
req.session.passport undefined
For some reason, the cookie that is sent with the /profile GET request is missing the passport object after a successful login (by
passport.use('local-login')) It looks like the following if I
console.log(req.session) in authenticated. Why is this?
Session {
cookie:
{ path: '/',
_expires: 2017-07-11T01:38:30.087Z,
originalMaxAge: 14400000,
httpOnly: true,
secure: false } }
Server Code
// server.js
let app = express();
app.use(cors());
import passportConfig from '../config/passport';
passportConfig(passport); //apply passport configuration
var jsonParser = bodyParser.json();
app.use(jsonParser);
app.use(cookieParser('mySecret'));
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
cookie: {secure: false, maxAge: 4*60*60*1000 }
}));
app.use(passport.initialize());
app.use(passport.session());
function authenticated (req, res, next) {
// req.session.passport is undefined
if (req.session.passport.user) {
next();
}
else {
res.redirect('/signup');
}
}
app.get('/profile', authenticated, (req, res) => {
// server code handling route
}
app.post('/login', passport.authenticate('local-login', {
session: true
}), (req, res) => {
res.json({
email: req.user.local.email
});
});
// passport.js
import User from '../models/User';
export default function passportConfig(passport) {
passport.serializeUser((user, done) => {
console.log('serializing user');
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser((id, done) => {
console.log('deserializing user');
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
}, (req, email, password, done) => {
// if verification is successful
var newUser = new User();
// set credentials of newUser...
return done(null, newUser);
}));
}
Client Code
// profile.js (client action code with react-redux)
export function profile(terms, location) {
return (dispatch) => {
return fetch('profile',
{credentials: 'include'})
.then(response => response.json())
.then(json => { dispatch(profileSuccess(json)); })
.catch(error => dispatch(profileError(error)));
}
}
I am aware there are actually quite a lot of articles/posts available
of similar issues, but applying many of the different solutions have
all failed for me. Any help to my problem specifically is greatly
appreciated.