So i managed to get passport-twitter working together with jsonwebtoken library, but in order for it to work properly I have to use express-session as the middleware. I don't want to add session because I'm using jsonwebtoken to return the token.
Here's the code
autheticate.js
router.get('/twitter', function(req, res, next){
passport.authenticate('twitter', {session: false}, function(err, user, info){
if(err){ return next(err); }
if(user){
var token = createToken(user);
console.log(token);
return res.json({token: token});
} else {
return res.status(401).json(info);
}
})(req, res, next);
});
I already added session: false as the argument, but on server.js it keeps spitting error, that i need to use express-session.
server.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var passport = require('passport');
var session = require('express-session');
var config = require('./config');
mongoose.connect('mongodb://localhost', function() {
console.log("Connected to the database");
})
require('./passport')(passport);
var app = express();
var authenticate = require('./routes/authenticate')(app, express, passport);
var api = require('./routes/api') (app, express, passport);
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
secret: config.TOKEN_SECRET,
resave: true,
saveUninitialized: true,
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use('/auth', authenticate);
app.use('/api', api);
app.get('*', function(req, res) {
res.sendFile(__dirname + '/public/app/views/index.html');
});
app.listen(3000, function(err) {
if(err) {
return res.send(err);
}
console.log("Listening on port 3000");
});
So whenever i delete app.use(session()) and try to authenticate with passport-twitter. I will get this error
error Oauth Strategy requires app.use(express-session));
I know that the obvious solution is to add that line, but I dont want to use session. Does Oauth 0.1 really need to use session?
Passports OAuth based strategies use the session middleware to keep track of the login process. You do not need to use the session middleware for anything else, just base your authentication on your token and ignore the session.
Related
The issue here is when i log in with correct details, the session should be created and cookie should be generated at client side which is happening when i use postman but not in case of web browsers.
I am using the following:
passport for authenticating login details from mongoDB using mongoose
express-session for creating session when a user logs in.
cors at the backend (react is running at port 3000 and node is running at port 4000)
Here is my app.js (backend):
var createError = require('http-errors');
var cookieParser = require('cookie-parser')
var path = require('path');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var serverRouter = require("./routes/server");
var DbRouter = require("./routes/DbRequest");
var ProductRouter = require("./routes/ProductRouter");
var CategoryRouter = require("./routes/CategoryRouter");
const session = require("express-session");
const passport = require("./passport");
var cors = require("cors");
var express = require('express');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(cors());
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser());
// USING SESSION AND PASSPORT
app.use(session({
secret: 'adsdsacoasdqwdn',
resave: false,
saveUninitialized: false,
cookie: { secure: false }
}))
app.use(passport.initialize());
app.use(passport.session());
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use("/api/admin",serverRouter);
app.use("/api/user",DbRouter);
app.use("/api/product",ProductRouter);
app.use("/api/category",CategoryRouter);
app.use(express.json({ extended:false}));
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Methods','GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
})
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// 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
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Here is my file where i am making the routes and creating sessions :
var express = require("express");
var router = express.Router();
var connectDB = require("./connect");
const passport = require("../passport");
connectDB();
router.post("/adminlogin", (req,res,next) => {
passport.authenticate("local-adminSignin",function(err,user,info){
if(err){
return res.status(500).send(err)
}
req.login(user,function(error){
if(error){
return res.status(500).json(err)
}
return res.json(user);
})
})(req,res,next);
})
router.get("/currentAdmin", (req,res,next) => {
res.json(req.user)
})
module.exports = router;
Here , when i make a get request to "/currentAdmin" after i logged in as admin, i should be getting the user object as a response which contains all details of the admin from mongoDB.
POSTMAN LOGIN (i cant post images because this is a new account, i've linked gyazo below) :
https://gyazo.com/5569415297fbe7c7178a93634d1506e6
AFTER LOGIN, MAKING REQUEST TO "/currentAdmin"
https://gyazo.com/cd2ec99e1ee8f959d0c3874b43b76454
But, when i make same request from react, it gives me empty object as a response. One thing i noticed here is cookies are generated in postman and i get response but no cookies are there in the browser after admin login.
MY PASSPORT INDEX.JS FILE
const passport = require("passport");
const User = require("../routes/AdminSchema")
passport.serializeUser(function(user, done) {
console.log('i am serializing the user');
done(null, user.username);
});
passport.deserializeUser(function(username, done) {
console.log('i am de-serializing the user');
User.findOne({username}).lean().exec((err,user) => {
done(err, user);
});
});
//import strategy
const AdminLoginStrategy = require("./AdminLoginStrategy");
passport.use('local-adminSignin',AdminLoginStrategy);
module.exports = passport;
MY LOGIN STRATEGY FILE:
const Strategy = require("passport-local").Strategy;
const User = require("../routes/AdminSchema")
const bcrypt = require("bcryptjs")
const LoginStrategy = new Strategy({passReqToCallback:true},function(req,username,password,done){
User.findOne({username}).lean().exec((err,user) => {
if(err){
return done("db error idk bro",null)
}
if(!user){
return done("user not found!",null)
}
const isPasswordValid = bcrypt.compareSync(password, user.password);
if(!isPasswordValid){
return done("username or password not valid!",null)
}
return done(null,user)
})
})
module.exports = LoginStrategy
PS: If i send text response or other json from "/currentAdmin", I am getting it in react but i am not getting the res.user object which i think is because of cookies not being generated in the browser.
How can i solve this issue? Thanks!
I've spent more time than I'd like to admit scouring countless posts online that have the same problem as me, to no avail. The majority of solutions seemed to be things like not including
passReqToCallback: true in my LocalStrategy.
passport.serializeUser() and passport.deserializeUser() in my init file.
app.use(passport.initialize()) and app.use(passport.session()) in app.js.
Requiring and using app.use(cookieParser()) in app.js.
Passing cookie parser my secret key.
Adding proxy and cookie attributes to my expressSession variable in app.js.
Invoking req.login inside passport.authenticate within my page route.
And several other little tweaks I can't remember...
My login.js is as follows;
var LocalStrategy = require('passport-local').Strategy;
var User = require('../../models/user');
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport) {
passport.use('login', new LocalStrategy({passReqToCallback: true},
function(req, username, password, done) {
//Queries MongoDB For User
User.findOne({'username': username}, function(err, user) {
//In The Event Of An Error, Throw It
if (err) {
return done(err);
}
//Username Does Not Exist, Log Error, Callback, Flash Error Message
if (!user){
console.log('User: '+ username + ", does not exist.");
return done(null, false, req.flash('message', 'User Not found.'));
}
//User Exists, But Password Is Incorrect
if (!isValidPassword(user, password)){
console.log('Invalid Password');
return done(null, false, req.flash('message', 'Invalid Password')); // redirect back to login page
}
//If No Previous Error Conditions Are Met - Username/Password Are Correct
console.log("Validated User: " + username + ".");
//req.user = user;
return done(null, user);
}); //End of User.findOne()
}) //End of new LocalStrategy
); //End of passport.use()
/*
var isValidPassword = function(user, password){
return bCrypt.compareSync(password, user.password);
}
*/
//Passwords are not currently hashed in my DB, so ignore bcrypt for now.
var isValidPassword = function(user, password) {
return user.password == password;
}
}
My passport-init.js file
var login = require('./login');
var User = require('../../models/user');
module.exports = function(passport){
//Serialise User
passport.serializeUser(function(user, done) {
console.log("Serializing User: " + user.username + "\n" + user + ".");
done(null, user._id);
});
//De-Serialise User
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
console.log("Deserializing User: " + user.username + "\n" + user);
done(err, user);
});
});
//Setting up Passport Strategy for Login
login(passport);
}
My index.js file contains the POST Login route
/* POST Login Page*/
router.post('/login', passport.authenticate('login', {
successRedirect: '/dashboard',
failureRedirect: '/login',
failureFlash: true
}));
And dashboard.js contains all the routing for /dashboard/other-pages.
These ones are protected by the isAuthenticated function.
var express = require('express');
var router = express.Router();
var database = require('../public/javascripts/db-connect.js');
var isAuthenticated = function(req, res, next) {
console.log("User: " + req.user);
console.log("Authenticated?: " + req.isAuthenticated());
if (req.isAuthenticated()) {
return next();
} else {
res.redirect('/unauthorised');
}
}
module.exports = function(passport) {
//Routes /dashboard --> dashboard.pug
router.get('/', isAuthenticated, function(req, res, next) {
database.getData("busdata", function(err, data) {
if (err) {
console.error(err);
} else {
res.render('dashboard', {title: 'Dashboard', busdata: data});
}
});
});
//Routes /dashboard/journeys --> journeys.pug
router.get('/journeys', isAuthenticated, function(req, res, next) {
database.getData("journeydata", function(err, data) {
if (err) {
console.error(err);
} else {
res.render('journeys', {title: 'Journey Graphs', journeydata: data});
}
});
});
return router;
}
So when I run the app;
I check localhost:3000/dashboard and localhost:3000/dashboard/journeys. They correctly re-direct me to /unauthorised.
Navigate to /login.
Enter a correct username and password, the console then spits out:
Validated User: TomPlum. Meaning passport.use() reached return done(null, user)
Serialising User: TomPlum + the objects properties
POST /login 302 time ms
User: undefined from isAuthenticated
isAuthenticated? false
GET /dashboard 302 time ms
Deserialising User: TomPlum + the objects properties
I'm then redirected to /unauthorised as isAuthenticated() evaluates to false.
Why is req.user undefined? Should I be using a LocalStrategy if my MongoDB is not-local? (Amazon Atlas Server). Other forum posts have suggested it could be a cookie issue but I've included the relevant cookie-parser includes in my app.js file.
app.js is here in-case it's an order issue.
//Require Variables
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
//Mongo DB Connection Settings
var dbConfig = require('./db'); //db.js contains DB URL
var mongoose = require('mongoose');
mongoose.connect(dbConfig.url); //dbConfig.url refers to the export in db.js
//Page Routing
//var index = require('./routes');
var users = require('./routes/users');
var dashboard = require('./routes/dashboard')(passport);
var app = express();
app.d3 = require('d3');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public/images', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser('urban_sensing'));
app.use(express.static(path.join(__dirname, 'public')));
//app.use('/', index);
app.use('/dashboard', dashboard);
app.use('/users', users);
//Configuring Passport
var passport = require('passport');
var expressSession = require('express-session');
app.enable('trust-proxy');
app.use(expressSession({
secret: 'urban_sensing',
resave: true,
saveUninitialized: true,
proxy: true,
cookie: {
secure: true,
maxAge: 3600000
}
}));
app.use(passport.initialize());
app.use(passport.session());
//Flash Messaging For Passport
var flash = require('connect-flash');
app.use(flash());
//Initialize Passport
var initPassport = require('./public/javascripts/passport-init');
initPassport(passport);
var index = require('./routes/index')(passport);
app.use('/', index);
//Catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
//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
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Some of the app.use(expressSession({..})); properties may currently be unnecessary as they've been added in an attempt to fix the issue.
Any help would be appreciated.
After too much time on this, turns out it was simply the order of app.js.
By moving
var dashboard = require('./routes/dashboard')(passport);
app.use('/dashboard', dashboard);
below all the passport configuration. It now works correctly. It seems that something wasn't initialised correctly during the routing of /dashboard when it was before the passport code.
Updated app.js
//Require Variables
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
//Mongo DB Connection Settings
var dbConfig = require('./db'); //db.js contains DB URL
var mongoose = require('mongoose');
mongoose.connect(dbConfig.url); //dbConfig.url refers to the export in db.js
//Page Routing
//var index = require('./routes');
var users = require('./routes/users');
var app = express();
app.d3 = require('d3');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public/images', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser('urban_sensing'));
app.use(express.static(path.join(__dirname, 'public')));
//app.use('/', index);
app.use('/users', users);
//Configuring Passport
var passport = require('passport');
var expressSession = require('express-session');
app.use(expressSession({
secret: 'urban_sensing',
resave: false,
saveUninitialized: true,
cookie: {
maxAge: 3600000 //1 Hour
}
}));
app.use(passport.initialize());
app.use(passport.session());
//Flash Messaging For Passport
var flash = require('connect-flash');
app.use(flash());
//Initialize Passport
var initPassport = require('./public/javascripts/passport-init');
initPassport(passport);
var dashboard = require('./routes/dashboard')(passport);
app.use('/dashboard', dashboard);
var index = require('./routes/index')(passport);
app.use('/', index);
//Catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
//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
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
I'm trying to access 'testpage' route. But the req.isAuthenticated() returns false only for this route. (This route was there before I started to add authentication).
I'm able to go to login page and authenticate with google. Then I can access 'signup' or 'user_profile' route without problems.
After login if I try:
localhost:8080/testpage
the server sends me to "/". But if I try:
localhost:8080/testpage#
with hash sign in the end, the page is rendered.
// routes/users.js
var express = require('express');
var router = express.Router();
module.exports = function (passport) {
router.get('/login', function (req, res) {
res.render('login', { message: req.flash('loginMessage') });
});
router.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
router.get('/auth/google/callback',
passport.authenticate('google', {
successRedirect: '/',
failureRedirect: '/'
}));
router.get('/user_profile', isLoggedIn, function (req, res) {
res.render('user_profile');
});
router.get('/signup', isLoggedIn, function (req, res) {
res.render('signup');
});
router.get('/testpage', isLoggedIn, function (req, res) {
res.render('testpage');
});
return router;
};
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
Any ideas why this is happening?
* update *
Here my app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var passport = require('passport');
var flash = require('connect-flash');
var session = require('express-session');
var db = require('./mongoose');
var app = express();
require('./config/passport')(passport);
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
// 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(session({
secret: 'secret123',
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
var users = require('./routes/users')(passport);
app.use('/', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// 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
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
It could be due to the express-session middleware that is needed for passport. you can fix it by using middleware in following order.
var session = require('express-session')
var app = express()
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: 'yoursecret',
resave: true,
saveUninitialized: true,
cookie: { secure: true },
// you can store your sessions in mongo or in mysql or redis where ever you want.
store: new MongoStore({
url: "mongourl",
collection: 'sessions' // collection in mongo where sessions are to be saved
})
}))
// Init passport
app.use(passport.initialize());
// persistent login sessions
app.use(passport.session());
See https://github.com/expressjs/session for more details.
Also I think so you have not config google strategy.
try some thing like following
var GoogleStrategy = require('passport-google-oauth').OAuthStrategy;
// Use the GoogleStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a token, tokenSecret, and Google profile), and
// invoke a callback with a user object.
passport.use(new GoogleStrategy({
consumerKey: GOOGLE_CONSUMER_KEY,
consumerSecret: GOOGLE_CONSUMER_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
},
function(token, tokenSecret, profile, done) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return done(err, user);
});
}));
Finally after one entire day I just realized that when I was typing localhost:8000/testpage in the url bar it was been changed to www.localhost:8000/testpage. And the auth dos not work with www*. Another thing is that google chrome tries to predict what url you will type and this could cause this type of error, and it is annoying at debugging. So I unchecked this options at chrome's settings, preventing prediction.
I think I'm misunderstanding how the token is supposed to post. I'm just getting a 403 every time, even though it's actually attempting to pass the token.
Here's the server code
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var redis = require('redis');
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
var ejs = require('ejs');
var csrf = require('csurf');
var util = require('./public/javascripts/utilities');
var routes = require('./routes/index');
var users = require('./routes/users');
var login = require('./routes/login');
var loginProcess = require('./public/javascripts/login.js').loginProcess;
// var loginProcess = require('./public/javascripts/login.js')
var client = redis.createClient();
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, '/public')));
app.use(cookieParser('secret'));
app.use(session(
{
store: new RedisStore({ host: 'localhost', port: 6379, client: client }),
secret: 'secret',
saveUninitialized: true,
resave: false
}
));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(csrf());
app.use(util.csrf);
app.use(util.authenticated);
app.use('/', routes);
app.use('/users', users);
app.use('/login',
login,
loginProcess);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
The login route is
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('login', {title: 'Login'});
next();
});
Here is what I've got in var util
module.exports.csrf = function csrf(req, res, next){
res.locals.csrftoken = req.csrfToken();
next();
};
I'm also using ejs, and have this after my form method='post'
<input type="hidden" name="_csrf" value="<%= csrfToken %>>"
Whenever it returns 403, the form data is at least getting the name of the input
_csrf:
username:Test
password:>9000
But as you can see, it's blank
I also wasn't sure if the res.locals.csrftoken was being passed to the login route, so I also tried adding it directly there with a router.post, but got this error
Error: Can't set headers after they are sent.
I've gone through nearly every post concerning this I could find. I'm either not making the logical connection for what I'm missing, or am wholly misunderstanding something. Both are entirely plausible, my money is on the second one. Feel free to make any, why in the world are you doing that - that way - comments, because chances are I'm doing it out of ignorance, and those comments are good for the learning process. Thanks in advance.
edit: Removing my utility function and following correct 'csurf' docs successfully passed the csrf token to my /login view.
I'm getting closer, still wrong, but this may shed some light as to where I'm getting confused.
var express = require('express');
var router = express.Router();
/* GET login listing. */
router.get('/', function(req, res, next) {
res.render('login', {title: 'Login', csrfToken: req.csrfToken() });
});
function loginProcess(req, res, next){
console.log(req.body);
res.send(req.body.username + ' ' + req.body.password);
res.json(req.csrfToken());
next();
};
router.post('/', loginProcess);
module.exports = router;
Why would this redirect me to a 404 page?
Because I didn't remove my authentication step before testing.
Also, I know this is sending un & pw in plain text along with the csrf token and that's no bueno. I'll get to that eventually.
Something I did is attempting to set headers when submitting username and password.
Error: Can't set headers after they are sent.
I thought it was my loginProcess function, but removing next(), or adding res.end(); didn't help
function loginProcess(req, res, next){
console.log(req.body);
res.send(req.body.username + ' ' + req.body.password);
res.json(req.csrfToken());
res.end();
};
edit You can't use res.send and res.json like that because they're both technically sending, and you can't send headers+body and then send headers+body again.
The token is automatically sent so I removed res.json(req.csrfToken();
But somewhere I'm not redirecting correctly on post. I'm just getting a blank page with the username and passwords that were entered.
edit:
Hokay. So everything appears to be working properly. Here is the updated code.
login.js
var express = require('express');
var router = express.Router();
/* GET login listing. */
router.get('/', function(req, res, next) {
res.render('login', {title: 'Login', csrfToken: req.csrfToken() });
});
function loginProcess(req, res, next){
var isAuth = auth(req.body.username, req.body.password, req.session)
if (isAuth){
res.redirect('/chat');
}else{
res.redirect('/login');
}
};
router.post('/', loginProcess);
router.get('/logout', out);
module.exports = router;
app.js
var routes = require('./routes/index');
var users = require('./routes/users');
var login = require('./routes/login');
var chat = require('./routes/chat');
//var loginProcess = require('./public/javascripts/login.js').loginProcess;
var client = redis.createClient();
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, '/public')));
app.use(cookieParser('secret'));
app.use(session(
{
secret: 'secret',
store: new RedisStore({ host: 'localhost', port: 6379, client: client }),
saveUninitialized: true,
resave: false
}
));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(csrf({ cookie: true }));
// app.use(util.csrf);
app.use(util.authenticated);
app.use('/', routes);
app.use('/users', users);
app.use('/login', login);
app.use('/chat', [util.requireAuthentication], chat);
I've still got a ton of cleanup, but it's at least functional.
Much thanks to #Swaraj Giri
What is app.use(util.csrf);? Guess you need to remove it.
From the docs of csurf,
You need to set csrf({ cookie: true }). This sets the crsf value in req.body._csrf.
Then you need to pass { csrfToken: req.csrfToken() } to the view of login page.
In login.js
router.get('/', function(req, res, next) {
res.render('login', {title: 'Login', csrfToken: req.csrfToken()});
next();
});
I'm using Express 4.2.0 with passport 0.2.0.
The express-session middleware that I am using is 1.2.1
I'm relatively new to node authentication so please bear with me.
I noticed that for everyone page load, passport is executing a db request:
Executing (default): SELECT * FROM "users" WHERE "users"."user_id"=7 LIMIT 1;
This doesn't make sense to me as I would think that the user has already been authenticated and serialized. And the session is now stored in the browser cookie. I also checked that I do have a cookie session stored in my browser.
Here are my app.js configuration:
var express = require('express');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
//var sys = require('sys');
var cons = require('consolidate');
/* sequelize */
var db = require('./models');
/* passport and its friends */
var passport = require('passport');
var flash = require('connect-flash');
var session = require('express-session');
/* routes */
var routes = require('./routes/index');
var users = require('./routes/users');
//filesystem middleware
//var fs = require('fs');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.engine('dust', cons.dust);
app.set('view engine', 'dust');
app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser('hashionhashion'));
app.use(require('less-middleware')(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({ secret: 'hashionhashion' })); // 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
//routing
app.use('/', routes);
app.use('/users', users);
and here is the passport serialize/deserialize function:
passport.serializeUser(function(user, done) {
done(null, user.user_id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
db.User.find({ where: {user_id: id} }).success(function(user){
done(null, user);
}).error(function(err){
done(err, null);
});
});
Executing a query on each request to fetch the authenticated user data sometimes is not a good practice(but it is commonly used in apps).
In one of my apps, i only needed id of the authenticated user rather than all data (like password,last_login_date,register_date, ...) so i changed passport configs like below:
passport.serializeUser(function(user, done) {
done(null, {id: user.id});
});
// used to deserialize the user
passport.deserializeUser(function(object, done) {
done(null, object); // object is: {id: user_id}
});
and then in your controllers you can get user id from session:
router.get('/', function(req, res, next)=> {
const userId = req.user.id;
// now do a database query only if needed :)
});
you can also define a custom middleware to fetch user from db and set to
req.user to use in next middleware:
function fetchUser (req, res, next) {
db.User.find({ where: {id:req.user.id} }).success(function(user){
req.user = user;
next(); // will trigger next middleware
}).error(function(err){
next(err); // will trigger error handler
});
}
router.get('/', fetchUser, function(req, res, next)=> {
const user = req.user;
// now user is a object that is fetched from db and has all properties
});
In this approach you'll execute query for retrieving user from db only if it's needed and not for all routes.