I'm currently encountering the following problem during implementation of passport js with the passport local mongoose plugin. Account creation and logging in is working correctly. However, after I have logged in passport never identifies me as a user that is logged in.
I have used the following pieces of code:
In my user model:
User.plugin(passportLocalMongoose);
In app.js (this order of inclusion is correct?):
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser('keyboard cat'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
In my routes:
router.post('/login', passport.authenticate('local'), function(req, res) {
res.json({ loggedIn: true });
});
which returns true, but the following keeps returning false (after logging in):
req.isAuthenticated()
Can anyone enlighten me what the cause may be?
Thanks!
You probably want to try an Express middelware, as was suggested in the comments.
For example:
function isAuthenticated(req, res, next) {
if(req.isAuthenticated()) {
return next()
} else {
// redirect users to login page
}
}
app.get('/anypage', isAuthenticated, function(req, res) {
// some reoute logic
})
From the looks of it, your order is fine. The most important part of the order is to have passport.initialize() and passport.session() come after your express-session configuration.
As for the issue with the initial authentication working, but subsequent requests showing an unauthenticated user, the issue could very well be because of cookies. I have run into a similar issue before, and the problem was in the way the HTTP requests were being made from the client.
If you are using the ES6 fetch API, then you will want to make sure to pass in an key credentials to the options object with a value of "include".
For example:
fetch('/restricted', {
method: 'get',
credentials: 'include'
});
The fetch API will not send credentials in cookies unless you specify it to. Hope this helps.
Additional resources: https://developers.google.com/web/updates/2015/03/introduction-to-fetch
Related
I am using react with express.js (inside router/user.js)
router.get("/", function (req, res) {
console.log("this is u" , req.user)
console.log(req.isAuthenticated());
if (req.user) {
res.json({ user: req.user })
} else {
res.json({ user: 'does not exsist' })
}
});
Here the console.log show the value against them as always undefined
console.log("this is u" , req.user)
console.log(req.isAuthenticated());
The above code always console.log false, Now I went through the other examples where they mentioned the problem can be due to the way you put things in server.js(or app.js) and hence I checked and think that my problem is not because of that reason, Anyway this is how I am adding stuff
app.use(session({
secret: 'keyboard cat', // -> used to encode and decode the session, the seceret we used will be used to encode to decode
resave: true,
saveUninitialized: true,
}));
app.use(passport.initialize());
app.use(passport.session());
//Session
passport.use(new LocalStrategy(user.authenticate()));
passport.serializeUser(user.serializeUser());
passport.deserializeUser(user.deserializeUser());
Here is my complete proper repository if someone wants to view: https://github.com/irohitb/litifier-
Now, Can anyone please guide me about fixing this problem?
This is because the login process is not being executed.
This results into cookie not being sent, which results into cookie not being read in the next request, which results into req.isAuthenticated() returning false
From the passport.js docs,
Note that when using a custom callback, it becomes the application's
responsibility to establish a session (by calling req.login()) and
send a response.
Solution:
As this looks like your signup page, I dont think you need to do passport.authenticate here. That is for login. Remove that.
After you register the new user, do the following:
req.logIn(user, function (err) {
if (err) {
return next(err);
}
return res.json(user) // send whatever you want or redirect
});
Try adding {credentials: 'include'} in the route
Example
get('/test', {credentials: 'include'})
I am building out a RESTful app using node.js, mongoose, express & passport using .ejs as a front end. I'm trying to add authentication back in with passport. With the current code I can register a new user, but that new user does not remain logged in, and after creating the user, I cannot login to do activities requiring authorization. There is no persistent user it seems. I'm using express-session as well.
I've searched for similar issues but any "fixes" have not seemed to help. I have nearly identical code in a similar app with the same stack...the main difference being the names of the resources (ie races instead of projects for example)
This will be the 3rd application I've used passport with, so it's not 100% new and with similar / same code, it has worked on my local installation. in this case, it was working fine including showing and hiding the login/logout buttons in my .ejs file.
Then I refactored the routes and now I cannot get passport to work at all.
I have compared code, placed the auth routes back into the app.js file, tried to console.log the req.user with and without logging in and it just won't work anymore...none of the passport routes seem to function.
I've reinstalled the node modules from my package.json file, copied & pasted the passport setup from previously working files, and the req.user is always undefined, and I'm unable to register a new user.
I've essentially reinstalled all of the passport stuff, short of deleting the auth file and app.js lines and restarting...but I should be able to trouble shoot this without deleting content.
Sadly I did not save a version prior to refactoring. :(
Any suggestions on why this may have occurred AFTER I refactored the routes files when it was working well just prior? As far as I can tell I have reconstructed things as they were prior to refactoring.
Here is my app.js passport setup
//setup passport
app.use(require("express-session")({
secret: "Fig is the best puppy ever",
resave: false,
saveUninitialized: false
}))
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.use(function(req, res, next){
console.log("Current User: " + req.user)
res.locals.currentUser = req.user;
next();
});
and my auth routes:
//Authentication Routes
//show signup form
app.get('/register', function(req,res){
res.render("register")
})
//user signup
app.post("/register", function(req,res){
//res.send("Signing you up")
console.log(req.body)
var newUser = new User({username: req.body.username})
User.register(newUser, req.body.password, function(err, user){
if(err) {
console.log(err);
return res.render('register')
} else {
passport.authenticate("local")(req,res, function(){
console.log("Created new user")
res.redirect("/projects")
})
}
})
})
//LOGIN ROUTES
//render login form
app.get("/login", function(req,res){
res.render("login")
})
app.post("/login", passport.authenticate("local",
{
successRedirect: "/projects",
failureRedirect: "/login"
}), function(req,res) {
// console.log("Logged in" +currentUser)
// res.redirect("/projects")
})
//Logout Routes
app.get("/logout", function(req,res) {
req.logout()
res.redirect("/projects")
})
It looks like you're missing the req.login call on authenticate. Try adding this to your else in your register middleware.
passport.authenticate("local")(req, res, function() {
req.login(user, function(err) {
if (err) {
console.log(err);
res.redirect('/register');
} else {
res.redirect('/projects')
}
})
})
Solution by OP.
Solved by reordering my route requirements and the passport set up. The correct sequence is below.
//setup passport
app.use(require("express-session")({
secret: "Fig is the best puppy ever",
resave: false,
saveUninitialized: false
}))
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
//I MOVED THESE LINES TO BELOW THE PASSPORT SETUP
// Route setup
app.use("/users", userRoutes)
app.use(authRoutes)
I am working on a MEAN application, I am using Angular 4 for my project. For authentication, I have implemented the Passport js Local-strategy. And I am maintaining persistent session using Express-session. Things are working fine till here.
The Problem
In the same domain session works fine and I am able to authenticate the user. But in cross-domain, I am not able to maintain the session. It generates a new session id for each new request in cross-domain.
I then tried Passport-jwt but the problem with it is I don't have the control over user session. I mean I can't logout the user from the server if he is inactive or even on server re-start also the token don't get invalid.
So in simple words, I am looking for an authentication solution in Node js (Express js) in which I can manage authentication in cross-domain.
I have already seen some blog post and SO questions like this, but it doesn't help.
Thank you.
EDIT
Should I write my own code to achieve this? If so I have a plan.
My basic plan is:
The user will send credentials with the login request.
I will check for the credentials in the database. If credentials are valid, I will generate a random token and save it to the database, in the user table and the same token I will provide to the user with success response.
Now, with each request user will send the token and I will check the token for each request in the database. If the token is valid then I will allow the user to access the API otherwise I will generate an error with 401 status code.
I am using Mongoose (MongoDB) so I will be ok to check the token in each request (performance point of view).
I think this is also a good idea. I just want some suggestions, whether I am thinking in right direction or not.
What I will get with this:
The number of logged in user in the application (active sessions).
I can logout a user if he is idle for a certain interval of time.
I can manage multiple login session of the same user (by doing an entry in the database).
I can allow the end user to clear all other login sessions (like Facebook and Gmail offers).
Any customization related to authorization.
EDIT 2
Here I am shareing my app.js code
var express = require('express');
var helmet = require('helmet');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var dotenv = require('dotenv');
var env = dotenv.load();
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var session = require('express-session');
var cors = require('cors');
var databaseUrl = require('./config/database.js')[process.env.NODE_ENV || 'development'];
// configuration
mongoose.connect(databaseUrl); // connect to our database
var app = express();
// app.use(helmet());
// required for passport
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) {
res.send(200);
} else {
next();
}
});
app.use(cookieParser());
app.use(session({
secret: 'ilovescotchscotchyscotchscotch', // session secret
resave: true,
saveUninitialized: true,
name: 'Session-Id',
cookie: {
secure: false,
httpOnly: false
}
}));
require('./config/passport')(passport); // pass passport for configuration
var index = require('./routes/index');
var users = require('./routes/user.route');
var seeders = require('./routes/seeder.route');
var branches = require('./routes/branch.route');
var companies = require('./routes/company.route');
var dashboard = require('./routes/dashboard.route');
var navigation = require('./routes/navigation.route');
var roles = require('./routes/role.route');
var services = require('./routes/services.route');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
require('./routes/auth.route')(app, passport);
app.use('/', index);
app.use('/users', users);
app.use('/seed', seeders);
app.use('/branches', branches);
app.use('/companies', companies);
app.use('/dashboard', dashboard);
app.use('/navigation', navigation);
app.use('/roles', roles);
app.use('/services', services);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
res.status(404).send({ status: 'NOT_FOUND', message: 'This resource is not available.'});
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
let errorObj = {
status: 'INTERNAL_SERVER_ERROR',
message: 'Something went wrong.',
error: err.message
};
res.status(err.status || 500).send(errorObj);
});
module.exports = app;
EDIT 3
For those who don't understand my problem. Explaining the problem in
simple words:
My Express server is running on port 3000.
In order to consume any API from the server, a user must be logged in.
When a user gets logged in from localhost:3000, the server checks the credentials(using Passport-local) and returns a token in the response header.
Now after login, when a user hits any API from localhost:3000, a predefined Header comes with passport-session and then passport verifies the user session using req.isAuthenticated() and all the things works as expected.
When a user gets logged in from localhost:4000 and the server send a token in response header (same as localhost:3000).
When after successful login, the user hits any API from localhost:4000 the passport js function req.isAuthenticated() returns false.
This was happening because in cross-domain the cookie doesn't go to the server we need to set withCredentials header to true at the client side.
I have set withCredentials header to true but still at the server the req.isAuthenticated() is returning false.
One possible solution to get around CORS/cookie/same-domain problems is to create proxy server that will mirror all requests from localhost:3000/api to localhost:4000, and then use localhost:3000/api to access the API instead of localhost:4000.
Best way for production deployment is to do it on your web server (nginx/apache).
You can also do it in node via express and request modules, or use some ready made middleware like this one:
https://github.com/villadora/express-http-proxy
Solution with this middleware is pretty straightforward:
var proxy = require('express-http-proxy');
var app = require('express')();
app.use('/api', proxy('localhost:4000'));
If you want to use sessions (ie. instead of jwt, etc) I think by default they are just in-memory so it will not work as your application scales to multiple hosts. It is easy to configure them to persist though.
See
https://github.com/expressjs/session#compatible-session-stores
You might have tried with passport-jwt. It generates tokens as per the JWT protocol on login. Your requirement is to blacklist the generated token when you logout. To achieve that, you can create a collection in mongodb named "BlacklistToken" with fields userid and token. When the user logs out, you can insert the token and userid in the collection. Then write a middleware to check whether the token is blacklisted or not. if it is redirect to login page.
did you already take a look here:
In this case, responses can be sent back based on some considerations.
If the resource in question is meant to be widely accessed (just like any HTTP resource accessed by GET), then sending back the Access-Control-Allow-Origin: * header will be sufficient,[...]
You may try this (allow any public IP) :
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', '*'); // add this line
// res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
It is normal that the second server re-create a new session, because assuming that you use Express-session, and according to the documentation:
Session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.
Which mean that you need to find a way to synchronize servers session data ...
Assuming that you find a method to do that, when you will try to connect, both server will retrieve the same user session data and the second will not have to create a new session...
If I understand the problem correctly here, you want the user's session to be stateless on the server. So that whenever the user logs in, the session can be re-used in any instance of the server when you scale your application, or even if you were to just reboot your application.
To achieve this, what you need is to configure the express-session with a database solution. You can do this with mongo using this package https://github.com/jdesboeufs/connect-mongo.
However, best practice is to use something a bit more robust for this sort of use-case, like redis using this package https://github.com/tj/connect-redis.
I have a simple application that uses an express session. Here is my entry script where I initiate the session.
export const app = express();
export const server = Server(app);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(session({
secret: "changed to nonsense string for this question",
resave: true,
saveUninitialized: true,
cookie: { maxAge: 60000 }
}));
app.use('/api/v1/auth', require('./users'));
According to the documents of express-session, I should be able to access this via req.session. I want to get the secret key for use creating a json token within one route. Here is what I do:
export const routes = Router();
routes.post('/login', function (req, res, next) {
// ...
const token = jwt.sign(user, req.session.secret, { expiresIn: '5m' });
});
export default routes;
However, it consistently says that the req.session.secret is not defined. What am I doing wrong? How do I access the session secret within a route?
secret is part of the configuration of express-session, but it's not accessible through req.session (I think this is done on purpose).
How to access it from your route depends on where you're getting the secret from in the first place. Usually this is from an environment or configuration variable, and I don't see why you can't access those from your route handler directly.
If the secret is hardcoded, and you want to keep it that way, you can create a separate module for it that can be require()'d wherever you need the secret.
I have an Angular app built on the MEAN stack, using Passport to log in and authenticate users. I'm a little confused about how authentication works.
I have a route that Angular passes an $http call to to check if a user is logged in (and can thus access certain pages). The route looks like this:
// route to test if the user is logged in or not
app.get('/loggedin', function(req, res) {
res.send(req.isAuthenticated() ? req.user : '0');
});
From what I've read (which is very little, I can't find isAuthenticated() anywhere in the Passport docs...), Passport should be creating a persistent session for my users.
This works for the most-part, but if I close my Chrome application / reset my computer I have to log in again. I assumed that using Passport would mean that I don't need to create a hashed cookie to store login information. Is this not the case?
Other potential cause: I'm in development at the moment and am restarting the server often. Will the Passport sessions not persist through a server restart?
Edit: Here is my session config in app.js:
var session = require('express-session');
app.use(session({ secret: 'heregoesasupersecretsecret' })); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
If you are using passport for authentication in Node.js, you can use a middleware to check the authentication. I use the below code for this.
passport.use(new LocalStrategy({passReqToCallback : true}, function(req, username, password, done) {
models.Accounts.findOne({ where: { username: username}}).then(function(user) {
if (!user) {
console.log('Unkown User');
return done(null, false, {message: 'Unknown User'});
}
bcrypt.compare(password, user.password, function(err, isMatch) {
if (err) throw err;
if (isMatch) {
req.session.username = req.body.username;
return done(null, user);
} else {
return done(null, false, {message: 'Invalid Password'});
}
});
}).catch(function(err) {
return done(null, false);
});
}));
You can use express-session library in Node.js to handle user sessions. You can add a middleware in your code like below.
app.use(session({
secret: 'track_courier_application_secret_key',
cookie: {
maxAge: 300000
},
saveUninitialized: true,
resave: true
}));
In the above code I'm setting maxAge to 300K milli seconds. That is 5 minutes. After this a session object will be attached in every request to the server. You can access this session object using
req.session
Now you can write a get request like below to check if a user is logged in or not.
app.get('/accounts/isloggedin', function(req, res) {
if(req.session.username)
res.status(200).send('Hurray!');
else
res.status(401).send('User not logged in.');
});
You need to use a proper session store to put the sessions in to persist them between requests. Since you're using MongoDB, you could use connect-mongo.
Then you could do something like:
var session = require('express-session'),
MongoStore = require('connect-mongo')(session);
app.use(session({
secret: 'heregoesasupersecretsecret',
store: new MongoStore()
}
));
app.use(passport.initialize());
app.use(passport.session());