Trouble with Express 4 and CSRF Token posting - node.js

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();
});

Related

ExpressJS setting up SEO friendly route

I am new to NodeJS and I am experiencing a problem while setting up my routes. I am using i18next, i18next-express-middleware and i18next-node-fs-backend in order to create a multilingual test website.
I would like my URL to look like the following depending on the selected language:
/fr/index, for french,
/en/index, for english,
/jp/index, for japanese.
I am currently facing at least one problem. The default route does not send me to the correct URL. I am always directed to /.
Here is my server.js file:
'use strict';
var debug = require('debug');
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 i18next = require('i18next');
var i18nextMiddleware = require('i18next-express-middleware');
var backend = require('i18next-node-fs-backend');
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
i18next
.use(backend)
.use(i18nextMiddleware.LanguageDetector)
.init({
backend: {
loadPath: __dirname + '/locales/{{lng}}/{{ns}}.json',
addPath: __dirname + '/locales/{{lng}}/{{ns}}.missing.json'
},
ns: ["ns.common"],
defaultNS: "ns.common",
fallbackNS: "ns.common",
fallbackLng: 'en',
preload: ['en', 'fr', 'jp'],
saveMissing: true,
removeLngFromUrl: false,
detection: {
order: ['path', 'session', 'querystring', 'cookie', 'header']
},
});
app.use(i18nextMiddleware.handle(i18next));
// 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(cookieParser());
app.use(express.static(path.join(__dirname, '/public')));
app.use('/', routes);
app.use('/users', 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 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: {}
});
});
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function () {
debug('Express server listening on port ' + server.address().port);
});
Here is my index.js file:
'use strict';
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/:lng', function (req, res) {
res.header("Content-Type", "text/html; charset=utf-8");
res.render('index', {});
});
module.exports = router;
The project is articulated as follows:
locales
|_en
ns.common.json
|_fr
ns.common.json
|_jp
ns.common.json
public
|_fonts
|_images
|_javascripts
|_stylesheets
routes
index.js
user.js
views
server.js
Everything is working fine if I enter manually the URL.
Can someone help me detect what is wrong in my code?
Thanks in advance for your answers.
Edit
As pointed out by Kishan, I use the following code, in order to redirect users to the correct locale. Thus, I am using a cookie to store the locale of a user. I ddo not know if it is a safe nor the most optimized way of doing things, but it works.
Here is the index file:
'use strict';
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function (req, res) {
res.redirect(req.cookies.locale + '/index');
});
router.get('/:lng/index', function (req, res) {
res.header("Content-Type", "text/html; charset=utf-8");
res.render('index', {});
});
/* Change locale */
router.post('/locale', function (req, res) {
res.cookie('locale', req.body.locale, { maxAge: 900000, httpOnly: true });
res.json({ status: 'success', redirect: '/' + req.body.locale + '/index'});
});
module.exports = router;
You need to add a route for / (root) in the index.js.
In the above code, the route gets into index.js but not find the path for /(root).
So add the route like...
router.get('/', function (req, res) {
// YOUR LOGIC
});
in your index.js.

Passport authentication not work for one specific route with express router

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.

express.js: show toastr message

I'm trying to get Toastr library to work in my ExpressJS app! I scaffolded the app with the yeoman 'standard' Express Generator...
I've required the lib express-toastr and did the following:
in app.js:
const cookieParser = require('cookie-parser');
const session = require('express-session');
const flash = require('connect-flash');
const toastr = require('express-toastr');
app.use(cookieParser());
app.use(session( {secret: 'xxx', saveUninitialized: true, resave: true} ));
app.use(flash());
app.use(toastr());
in index.js
const express = require('express');
const router = express.Router();
const httpntlm = require('httpntlm');
router.post('/', function (req, res, next) {
// parse inputs
let user = req.body.user || "";
let password = req.body.password || "";
// save in session
req.session.user = {user: user, password: password};
// appropriate response to login attempt
if (!req.session.user) {
res.status(401).send();
}
else {
req.toastr.success('Successfully logged in.', "You're in!");
res.render('groups', {
req: req
});
}
});
module.exports = router;
In index.jade
#{req.toastr.render()}
I'm loading these files in my <head> section:
link(rel='stylesheet', href='//cdnjs.cloudflare.com/ajax/libs/toastr.js/2.0.2/css/toastr.min.css')
script(src='/components/jquery/dist/jquery.min.js')
script(src='//cdnjs.cloudflare.com/ajax/libs/toastr.js/2.0.2/js/toastr.min.js')
Nothing is showing. What am I missing???
-- UPDATE! --
Here is my complete app.js file. I now try to use express-flash and making a dedicated route for showing a flash message. Still not working. Please help!
'use strict';
const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const expressSanitizer = require('express-sanitizer');
const login = require('./routes/login');
const apply = require('./routes/apply');
const admin = require('./routes/admin');
var session = require('express-session');
var flash = require('express-flash');
var app = express();
// 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: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
cookie: { maxAge: 60000 },
store: new session.MemoryStore,
saveUninitialized: true,
resave: 'true',
secret: 'secret'
}));
app.use(flash());
// Route that creates a flash message using the express-flash module
app.all('/express-flash', function( req, res ) {
req.flash('success', 'This is a flash message using the express-flash module.');
res.redirect(301, '/');
});
// sanitize inputs
app.use(expressSanitizer());
app.use('/', apply);
app.use('/apply', apply);
app.use('/login', login);
app.use('/admin', admin);
// 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;
Try using this middleware in your app.js, I found this here
app.use(function (req, res, next)
{
res.locals.toasts = req.toastr.render()
next()
});
and then access locals in your view as follows:
#{toasts}
This worked for me.
So I am not familiar with your syntax in your index.jade file(!=). What does it do? If you change that line in your index to #{req.toastr.render()} it should work.

Browser doesn't show index page made by index.js and index.jade on Node.js

I'd like to show index page by index.js and index.jade on Node.js, however browser returns the page only show index.html as attached picture.
I set the index.js as routes as following, thus I think the root path should return index page.
var routes = require('./routes/index');
app.use('/', sessionCheck, routes);
The following is my present code. Could you tell me what is the problem?
###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 session = require('express-session');
var routes = require('./routes/index');
var users = require('./routes/users');
var login = require('./routes/login');
var app = express();
// 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(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 30 * 60 * 1000 // 30min.
}
}));
var sessionCheck = function(req, res, next) {
if (req.session.user) {
next();
} else {
res.redirect('/login');
}
};
app.use('/login', login);
app.use('/', routes);
app.use('/users', 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 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;
###routes/index.js
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
###views/index.jade
extends layout
block content
h1= title
p Welcome to #{title}
I've found the problem.
Becuase I set the root directory '/public', I could not access index.js.Thank you for you kindeness.
app.use(express.static(path.join(__dirname, 'public')));

can no longer load express app webpages

So i've been working on getting a user authentication system going and then I encountered a problem where my webpages would no longer load. npm start works fine and the server appears to be running. going to localhost just shows that the bar keeps loading and never eventually loads (before the webpages loaded instantly). I have no idea what I changed but tried to revert as much of the code to the original but still no dice. let me know if any other files are needed. thanks in advance
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 mongoose = require('mongoose');
var passport = require('passport');
var session = require('express-session');
var LocalStrategy = require('passport-local').Strategy;
var flash = require('connect-flash');
// var users = require('./routes/users');
var routes = require('./routes/index');
var app = express();
// 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(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser('ground glass'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(flash);
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
app.use('/', routes);
//app.use('/users', users);
// passport config
// var Account = require('./models/accounts');
//passport.use(new LocalStrategy(Account.authenticate()));
//passport.serializeUser(Account.serializeUser());
//passport.deserializeUser(Account.deserializeUser());
// mongoose
//mongoose.connect('mongodb://localhost/test');
// Using the flash middleware provided by connect-flash to store messages in session
// and displaying in templates
//var flash = require('connect-flash');
//app.use(flash());
//require('./routes/index')(app);
/*
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));*/
// 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;
index.js
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { title: 'test' });
});
module.exports = router;

Resources