In my index.js file, each form action called executes properly:
routes/index.js
var express = require('express');
var async = require('async');
var common = require('./common.js');
var log = require('./log.js');
var router = express.Router();
router.post('/premise', function...
router.post('/follow', function...
views/index.jade
extends layout
block js
script(src='/javascripts/countdown.js')
script(src='/javascripts/table_filter.js')
block content
if username...
...div.followers.table_wrapper
h3.table_header.center Following
#followers_tbl.center
h4.center You are not following anyone...
form(action="follow" method="post")
input(type="text" name="follow_username" id="follow_username" placeholder="Add username here...")
button(class="btn btn-primary btn-sml" type="submit" style="margin-bottom: 15px") Follow A User
div#add_premise.textarea_wrapper
form(action="premise" name="premise" method="post")
textarea(name="premise" id="premise" placeholder="Enter your..." required)
p.countdown.black
button(class="btn btn-success" type="submit") Add Premise
But when I call from another page /signup it doesn't work. (See below EDIT file details).
The call returns a 302, so it is found, but the function doesn't execute. But when I remove action='signup/signup/' from the form in the view, then change the router.post('/signup', function... to router.post('/', function..., the function executes, after also returning a 302 in my log.
Can anyone help me? Thanks so much!
EDIT: (Adding app.js and more of routes/signup.js)
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 fileStore = require('session-file-store')(session);
var index = require('./routes/index.js');
var signup = require('./routes/signup.js');
var login = require('./routes/login.js');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
var session_options = {
name: 'server-session-cookie-id',
secret: 'secret',
saveUninitialized: true,
resave: true,
store: new fileStore(),
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365 // 1 year
}
};
// 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());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session( session_options ));
app.use( function( req, res, next ) {
try {
if ( ( req.session && req.session.username ) ||
req.originalUrl == "/login" ||
req.originalUrl == "/signup" {
next();
}
else {
res.redirect('/login');
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
app.use('/', index);
app.use('/signup', signup);
app.use('/login', login);
app.get('/logout', function(req, res, next) {
try {
req.session.destroy();
res.clearCookie(session_options.name);
res.redirect('/');
}
catch( err ) {
common.send_500( req, res, err );
}
});
// 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/signup.js
var express = require('express');
var router = express.Router();
var encrypt = require('./encrypt.js');
var common = require('./common.js');
var log = require('./log.js');
/* GET signup page. */
router.get('/', function(req, res, next) {
try {
var signup_error = req.session.signup_error;
req.session.signup_error = null;
res.render('signup', { title: 'Signup', signup_error: signup_error });
}
catch( err ) {
common.send_500( req, res, err );
}
});
router.post('/normal', function(req, res, next) {...});
router.post('/submit', function(req, res, next) {...});
module.exports = router;
views/signup.jade
extends layout
block js
script(src='/javascripts/dob_validate.js')
block content
div#signup_form
form(action="/signup/normal" name="signup" method="post")
input(type="email" name="email" id="email" placeholder="Email" required autofocus)
input(type="text" name="username" id="username" placeholder="Username" required)
input(type="password" name="password1" id="password1" placeholder="Password" required)
input(type="password" name="password2" id="password2" placeholder="Confirm Password" required)
p#dob
| Birthdate
input(type="date" name="birthdate" id="birthdate" style="margin-left=20px;" required).pull-right
label
input(style="margin-right: 5px;" type="checkbox" name="checkbox" required).pull-left
| Check here to indicate that you have read, understand and agree to the
a(href="/terms") Terms of Use,
a(href="/privacy") Privacy Policy
| and
a(href="/cookies") Cookie Policy
| .
div#signup_btns
button(class="btn btn-primary" type="submit") Sign Up
a(href='/login') Login
/signup/normal/ handler:
router.post('/normal', function(req, res, next) {
try {
var email = req.body.email;
var username = req.body.username.toUpperCase();
var password1 = req.body.password1;
var password2 = req.body.password2;
var birthdate = req.body.birthdate;
birthdate = birthdate.substring(5,7) + '/' +
birthdate.substring(8,10) + '/' +
birthdate.substring(0,4);
if (password1 === password2) {
var body = JSON.stringify({
"username" : username,
"password_hash" : encrypt.hash(password1),
"credits" : 100,
"email_address" : email,
"birthdate" : birthdate
});
var path = "/accounts/add/";
common.post( path, body, function( data ) {
try {
if ( data && !data.error ) {
console.log(JSON.parse(data.resp));
res.redirect('/login');
}
else {
req.session.signup_error = data.resp;
req.session.save( function(err) {
res.redirect('/signup');
});
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
}
else {
req.session.signup_error = "Passwords do not match";
req.session.save( function(err) {
res.redirect('/signup');
});
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
This is normal because your are using signup.js router with /signup end point:
const signup = require('./routes/signup.js')
...
app.use('/signup', signup)
Assuming that you define a route handler inside signup.js to /about route:
router.post('/about', function(req, res, next) { ... });
Then the app will be able to handle requests to /signup/about. If you create a route handler for /signup then the end point is /signup/signup
I suggest you to change the /signup route to /user like this: app.use('/user', signup) and also change the form action to action="/user/signup" this makes more sense
You have a mixup here between GET requests and POST requests, and which is on what endpoint.
If you're hitting localhost:3000/signup, then that endpoint should be returning your signup.jade file (by the way this is a server-side html rendering you're using....). You're serving dynamically generated HTML content at that endpoint.
However, By typing in localhost:3000/signup within your browser address bar, you're executing an HTTP GET request, not a POST. Very important difference here!
Thus, you now need a router endpoint of router.post('/signup/signup', function...) to actually catch the form submit (note this is now a POST) from your jade template. Honestly I would want to point out your naming scheme here, because as you can see /signup/signup is quite confusing and doesn't follow a very good convention.
Related
I am working on a NodeJS style blog and for some reason, the variable for the category name shows up as #{category.name} whereas the other variable values actually show the values. Secondly, When I submit the image for the post, it does not upload the image. Any help to see if there might be some errors in my code would be greatly appreciated. Thank you 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 session = require("express-session");
var multer = require("multer");
var upload = multer({ dest: "./public/images" });
var expressValidator = require("express-validator");
var mongo = require("mongodb");
var db = require("monk")("localhost/nodeblog");
var routes = require("./routes/index");
var posts = require("./routes/posts");
var categories = require("./routes/categories");
var app = express();
app.locals.moment = require("moment");
app.locals.truncateText = function(text, length) {
var truncatedText = text.substring(0, length);
return truncatedText;
};
// 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', '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")));
// Express Session
app.use(
session({
secret: "secret",
saveUninitialized: true,
resave: true
})
);
// Express Validator
app.use(
expressValidator({
errorFormatter: function(param, msg, value) {
var namespace = param.split("."),
root = namespace.shift(),
formParam = root;
while (namespace.length) {
formParam += "[" + namespace.shift() + "]";
}
return {
param: formParam,
msg: msg,
value: value
};
}
})
);
// Connect-Flash
app.use(require("connect-flash")());
app.use(function(req, res, next) {
res.locals.messages = require("express-messages")(req, res);
next();
});
// Make our db accessible to our router
app.use(function(req, res, next) {
req.db = db;
next();
});
app.use("/", routes);
app.use("/posts", posts);
app.use("/categories", categories);
// 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.pug
extends layout
block content
if posts
each post, i in posts
.post
h1
a(href='/posts/show/#{post._id}')
=post.title
p.meta Posted in
a(href='/categories/show/#{post.category}') #{post.category} by #{post.author} on #{moment(post.date).format("MM-DD-YYYY")}
img(src='/images/#{post.mainimage}')
!=truncateText(post.body,400)
a.more(href='/posts/show/#{post._id}') Read More
addpost.pug
extends layout
block content
h1=title
ul.errors
if errors
each error, i in errors
li.alert.alert-danger #{error.msg}
form(method='post', action='/posts/add', enctype="multipart/form-data")
.form-group
label Title:
input.form-control(name='title', type='text')
.form-group
label Category
select.form-control(name='category')
each category, i in categories
option(value='#{category.name}') #{category.name}
.form-group
label Body
textarea.form-control(name='body', id='body')
.form-group
label Main Image:
input.form-control(name='mainimage', type='file')
.form-group
label Author:
select.form-control(name='author')
option(value='Erik Robles') Erik Robles
option(value='John Doe') John Doe
input.btn.btn-default(name='submit',type='submit',value='Save')
script(src='/ckeditor/ckeditor.js')
script
| CKEDITOR.replace('body');
addcategory.pug
extends layout
block content
h1=title
ul.errors
if errors
each error, i in errors
li.alert.alert-danger #{error.msg}
form(method='post', action='/categories/add')
.form-group
label Name:
input.form-control(name='name', type='text')
input.btn.btn-default(name='submit',type='submit',value='Save')
posts.js
var express = require("express");
var router = express.Router();
var multer = require("multer");
var upload = multer({ dest: "./public/images" });
var mongo = require("mongodb");
var db = require("monk")("localhost/nodeblog");
router.get("/add", function(req, res, next) {
var categories = db.get("categories");
categories.find({}, {}, function(err, categories) {
res.render("addpost", {
title: "Add Post",
categories: categories
});
});
});
router.post("/add", upload.single("mainimage"), function(req, res, next) {
// Get Form Values
var title = req.body.title;
var category = req.body.category;
var body = req.body.body;
var author = req.body.author;
var date = new Date();
// Check Image Upload
if (req.file) {
var mainimage = req.file.filename;
} else {
var mainimage = "noimage.jpg";
}
// Form Validation
req.checkBody("title", "Title field is required").notEmpty();
req.checkBody("body", "Body field is required").notEmpty();
// Check Errors
var errors = req.validationErrors();
if (errors) {
res.render("addpost", {
errors: errors
});
} else {
var posts = db.get("posts");
posts.insert(
{
title: title,
body: body,
category: category,
date: date,
author: author,
mainimage: mainimage
},
function(err, post) {
if (err) {
res.send(err);
} else {
req.flash("success", "Post Added");
res.location("/");
res.redirect("/");
}
}
);
}
});
module.exports = router;
index.js
var express = require("express");
var router = express.Router();
var mongo = require("mongodb");
var db = require("monk")("localhost/nodeblog");
/* GET home page. */
router.get("/", function(req, res, next) {
var db = req.db;
var posts = db.get("posts");
posts.find({}, {}, function(err, posts) {
res.render("index", { posts: posts });
});
});
module.exports = router;
categories.js
var express = require("express");
var router = express.Router();
var mongo = require("mongodb");
var db = require("monk")("localhost/nodeblog");
router.get("/show/:category", function(req, res, next) {
var posts = db.get("posts");
posts.find({ category: req.params.category }, {}, function(err, posts) {
res.render("index", {
title: req.params.category,
posts: posts
});
});
});
router.get("/add", function(req, res, next) {
res.render("addcategory", {
title: "Add Category"
});
});
router.post("/add", function(req, res, next) {
// Get Form Values
var name = req.body.name;
// Form Validation
req.checkBody("name", "Name field is required").notEmpty();
// Check Errors
var errors = req.validationErrors();
if (errors) {
res.render("addpost", {
errors: errors
});
} else {
var categories = db.get("categories");
categories.insert(
{
name: name
},
function(err, post) {
if (err) {
res.send(err);
} else {
req.flash("success", "Category Added");
res.location("/");
res.redirect("/");
}
}
);
}
});
module.exports = router;
If I am missing anything, please let me know so I can make the appropriate edits. Again, Thank you.
P.S. By structure is:
routes
categores.js
index.js
posts.js
views
addcategory.pug
addpost.pug
error.pug
index.pug
layout.pug
app.js
The code I was using is old (circa 2016) and no longer works. In calling the variable name, you must remove the curly braces, hash and quotes from the first option variable. Here is how the code should read in the categories options section:
addpost.pug
.form-group
label Category
select.form-control(name='category')
each category, i in categories
option(value=category.name) #{category.name}
I hope this answer helps someone struggling with the same issue.
When a logged-in user gets to a page through the browser using EJS I'm able to get the function to do what it's supposed to but when I use the API with Ionic using a logged in user with JWT, the async.parallel function doesn't "wait" to do things in order.
Here is my function:
console.log('1');
async.parallel([
function(callback){
buildAlertButtonsArray.getRealTestAlerts(req,function(arrayAlerts) {
console.log('2');
callback(null, arrayAlerts);
});
},
function(callback) {
if(req.decoded) //API
callback('API');
else //EJS
functions.aclSideMenu(req, res, function (acl) {callback(null, acl);}); //aclPermissions sideMenu
}
],function(err, results){
console.log('3');
})
when I login through the browsed on my console.log() is 1, 2, 3 but when I login through the API using JWT I get 1, 3, 2.
Here is 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 app = express();
var bluebird = require('bluebird');
//me
var mongoose = require('mongoose');
var db = mongoose.connection;
var cors = require('cors');
var session = require('client-sessions');
var flash = require('express-flash');
//.js file
var routesApi = require('./routes/api');
var routesEjs = require('./routes/ejs');
var routes = require('./routes/index');
//var login = require('./routes/authentication/login');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(cookieParser());
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({ extended: true })); //was FALSE by default. was TRUE for auth Template
// middleware
app.use(session({
cookieName: 'session',
secret: 'mysecret',
duration: 30 * 60 * 1000,
activeDuration: 30 * 60 * 1000,
httpOnly: true, //doesn't let javascript access cookies ever
secure: true, // only use cookies over https
ephemeral: true // delete this cookie when the browser is closed (nice when people use public computers)
}));
app.use(flash());
app.use(function(req, res, next){
res.locals.success_messages = req.flash('success_messages');
res.locals.error_messages = req.flash('error_messages');
next();
});
// use cors
app.use(cors());
app.use('/public', express.static(path.join(__dirname, 'public')));
app.use('/api', routesApi);
app.use('/', routes);
app.use('/', routesEjs);
//bluebird
mongoose.Promise = require('bluebird');
//connecting to database
mongoose.connect('mongodb://myip:2999/SMECS_database', { useMongoClient: true });
//if we connect successfully or if a connection error occurs
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function (callback) {
// yay!
});
// error handlers
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// 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;
Here is my Login function for both EJS using CSRF and API using JWT:
module.exports.postLogin = function(req, res, next) {
if (req.body.pushToken) { // run SMECS API
models.Users.findOne({
email: req.body.email.toLowerCase()
}, function (err, user) {
if (err) throw err;
if (!user) {
res.json({success: false, message: 'Authentication failed. User not found.'});
} else if (user) {
//check if password matches
if (!bcrypt.compareSync(req.body.pin, user.pin)) {
res.json({success: false, message: 'Authentication failed. Wrong password.'});
} else {
// if user is found and password is right
// create a token
var token = jwt.sign({user: user}, config.secret, {
//expiresIn: 1440 // expires in 24 hours
});
user.save(function (err) {
if (err) {
res.json({
success: false,
message: 'contact your system administrator. pushToken not saved'
});
} else {
// return the information including token as JSON
res.json({
success: true,
message: 'Welcome aboard!',
token: token,
userRoleID: user.userRoleID,
userRoleName: user.userRoleName,
userPrivilegeID: user.userPrivilegeID,
userPrivilegeName: user.userPrivilegeName,
firstName: user.firstName,
lastName: user.lastName,
email: user.email
});
}
});
}
}
});
}
else { //run SMECS EJS
models.Users.findOne({email: req.body.email.toLowerCase()}, function (err, user) {
if (!user || user.softDeleted !== null) {
//Parent Self Registration Login
models.ParentSelfRegistration.findOne({email: req.body.email.toLowerCase()}, function (err, parentSelfRegistration) {
if (!parentSelfRegistration) {
res.render('login', {error: "ERROR: Incorrect email or pin.", csrfToken: req.csrfToken()});
} else {
if (req.body.pin == parentSelfRegistration.pin) {
req.session.user = parentSelfRegistration;
res.redirect('/parentsSelfRegistration/registerParentStep1');
} else {
res.render('login', {error: "ERROR: Incorrect email or pin.", csrfToken: req.csrfToken()});
}
}
});
//END OF checks for users in UtilityUsers database
} else {
if (bcrypt.compareSync(req.body.pin, user.pin)) { // if user is found and password is right
req.session.user = user;
res.redirect('/dashboard');
//}
} else {
//res.status(400).send('Current password does not match');
res.render('login', {error: "ERROR: Incorrect email or pin.", csrfToken: req.csrfToken()});
//res.render('login', { error: "ERROR: Incorrect email or pin."});
}
}
});
}
};
Here is my ejs.js file:
//Dependencies
var express = require('express');
var routerEjs = express.Router();
var login = require('./authentication/login');
var auth = require('./authentication/auth');
var chooseAlert = require('./alerts/sendingReceiving/1.chooseAlert');
var login = require('./authentication/login');
var csrf = require('csurf');
routerEjs.use(csrf());
/* GET login page. */
routerEjs.get('/login', login.getLogin, function(req, res) {});
routerEjs.post('/login', login.postLogin, function(req, res) {});
routerEjs.get('/logout', login.getLogout, function(req, res) {});
module.exports = routerEjs;
and my api.js file:
//Dependencies
var express = require('express');
var routerApi = express.Router();
var login = require('./authentication/login');
var auth = require('./authentication/auth');
var chooseAlert = require('./alerts/sendingReceiving/1.chooseAlert');
routerApi.post('/login', login.postLogin, function(req, res) {});
routerApi.get('/chooseGroup', auth.auth, chooseAlert.showGroups, function(req, res) {});
routerApi.get('/alerts/sending/chooseAlert', auth.auth, chooseAlert.showAlerts, function(req, res) {});
/* Update pushToken ------------------------------------*/
routerApi.post('/updatePushToken', auth.auth, auth.pin, function(req, res) {});
module.exports = routerApi;
I figured out my problem. I was missing a NULL on my callback...
console.log('1');
async.parallel([
function(callback){
buildAlertButtonsArray.getRealTestAlerts(req,function(arrayAlerts) {
console.log('2');
callback(null, arrayAlerts);
});
},
function(callback) {
if(req.decoded) //API
callback(NULL, 'API');
else //EJS
functions.aclSideMenu(req, res, function (acl) {callback(null, acl);}); //aclPermissions sideMenu
}
],function(err, results){
console.log('3');
})
I am trying to implement csrf protection into my project but I can't make it work with jQuery Ajax. (It works with normal posts requests, though)
If I tamper the token using chrome dev tools before I send the form, I still see "data is being processed" text rather than invalid csrf token error.
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 csrf = require('csurf');
var bodyParser = require('body-parser');
var router = express.Router();
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
//app.set('strict routing', true);
app.set('view options', {layout: false});
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
var csrfProtection = csrf({ cookie: true });
var parseForm = bodyParser.urlencoded({ extended: false });
app.use(express.static(path.join(__dirname, 'public')));
app.get('/form', csrfProtection, function(req, res) {
// pass the csrfToken to the view
res.render('send', { csrfToken: req.csrfToken() });
});
app.post('/form', parseForm, csrfProtection, function(req, res) {
res.send('data is being processed');
});
// 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;
send.jade
html
head
meta(name='_csrf', content='#{csrfToken}')
body
form(action='/form', method='POST')
| Favorite color:
input(type='text', class="favori", name='favoriteColor')
button(type='submit') Submit
script(src="javascripts/frontend/jquery/jquery-3.0.0-alpha1.js")
script(src="javascripts/test.js")
test.js
$(document).ready(function () {
$.ajaxSetup({
headers: {'X-CSRF-Token': $('meta[name="_csrf"]').attr('content')}
});
$('button').click(function (e) {
e.preventDefault();
var text = $('.favori').val();
alert(text);
$.post(
"/form",
{
text: text
}, function (data) {
console.log(data);
});
});
});
Send the CSRF token inside the payload message:
$('button').click(function (e) {
e.preventDefault();
var text = $('.favori').val();
alert(text);
$.post(
"/form",
{
text: text,
_csrf : $('meta[name="_csrf"]').attr('content')
}, function (data) {
console.log(data);
});
});
To facilitate your work I think you can create a Jquery plugin to do it, something like this:
(function( $ ) {
$.postCSRF = function(to, message, callback) {
message._csrf = $('meta[name="_csrf"]').attr('content');
$.post(to, message, callback);
};
}( jQuery ));
// Usage example:
$.postCSRF('/form',{text:'hi'},function(res) {
console.log(res);
});
Example: http://jsfiddle.net/w7h4Lkxn/
Check the header to see if its passing the tampered token in the cookie or as part of the form data. It looks like your setup is for using cookies. So changing it on the form shouldn't affect the cookie. Lemme know if that helps reveal the issue.
your doing everything exactly right but you have to disable checking for the cookie entirely!
var csrfProtection = csurf({ cookie: false });
the author mentions it here
https://github.com/expressjs/csurf/issues/52
thanks for the above code with the header post as this was crucial for validating the express session token and i have shared it with others
For certain pages I have custom 500, 404 and 403 error handling in my app. So for instance after an unsuccessful database query I'd go:
return next({status: 404, message: 'Record not found'});
or
return next(new Error('Bad things have happened')});
In my middleware I have an error handler:
app.use(function (err, req, res, next) {
// handle error
});
Problem is that the error handler is never called, instead the error callstack is being printed into the browser. I want the handler to render a custom error page.
app.js
var express = require('express')
, app = express()
, swig = require('swig')
, config = require('./lib/config')
, env = process.env.NODE_ENV || 'development'
, path = require('path');
config.configure(env);
app.engine('html', swig.renderFile);
app.set('view cache', false);
swig.setDefaults({
cache: config.get('swigCache')
});
app.set('view engine', 'html');
app.set('views', __dirname + '/lib/views');
require('./lib/util/swig');
require('./lib/initialisers/mongodb')();
require('./lib/initialisers/aws')();
require('./lib/middleware')(app); // first load middleware
require('./lib/routes')(app); // then routes
var server = app.listen(config.get('port'), function() {
console.info('config: ' + JSON.stringify(config.getCurrent()));
console.info('NODE_ENV: ' + env);
console.info('server running: ' + JSON.stringify(server.address()));
});
routes.js
module.exports = function(app){
app.get('/', require('./views/').index);
app.get('/blog', require('./views/blog').index);
app.get('/blog/:slug', require('./views/blog').getBySlug);
app.route('/report/:slug')
.get(require('./views/report/').index)
.post(require('./views/report/').doReport);
// Very long file with tons of routes. Simplified version.
middleware.js
var express = require('express')
, app = express()
, path = require('path')
, logger = require('morgan')
, cookieParser = require('cookie-parser')
, bodyParser = require('body-parser')
, passport = require('passport')
, session = require('express-session')
, mongoStore = require('connect-mongo')(session)
, compression = require('compression')
, favicon = require('serve-favicon')
, config = require('./config')
, flash = require('connect-flash')
, multer = require('multer')
, csrf = require('csurf');
module.exports = function(app) {
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json());
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.use(express.static(path.join(__dirname, config.get('staticContentPath')), {
maxAge: (60 * 60 * 24) * 1000
}));
app.use(session({
resave: true,
saveUninitialized: true,
secret: 'da755fc0-6882-11e4-9803-0800200c9a66',
cookie: {
maxAge: 24 * 60 * 60 * 1000 // 24 hrs
},
store: new mongoStore({
url: config.getMongoConn()
})
}));
app.use(logger('dev'));
app.use(flash());
/**
* 301 redirects
*/
app.use(function(req, res, next) {
var host = req.get('host');
// AWS IP --> http
if (host == 'xx.xxx.xxx.xxx') {
return res.redirect(301, config.get('url') + req.originalUrl);
}
// AWS origin --> http
if(host == 'xxx-xxx-xxx-xxx-xxx.ap-southeast-2.compute.amazonaws.com'){
return res.redirect(301, config.get('url') + req.originalUrl);
}
// www --> http
if (/^www\./.test(host)) {
host = host.substring(4, host.length);
return res.redirect(301, req.protocol + '://' + host + req.originalUrl);
}
// Trailing slash --> http
if (req.path.substr(-1) == '/' && req.path.length > 1) {
var query = req.url.slice(req.path.length);
return res.redirect(301, req.path.slice(0, -1) + query);
}
next();
});
// Delete expired Mongo sessions from DB
app.use(function (req, res, next) {
req.session._garbage = new Date();
req.session.touch();
next();
});
/**
* Setting Cache control header for Ajax requests to 30 minutes
*/
app.use(function (req, res, next) {
if(req.xhr){
res.header('Cache-Control', 'max-age=' + 1800 + ', public');
}
next();
});
app.use(compression());
app.use(
multer({
dest: config.get('uploads').folders.temp
})
);
app.use(passport.initialize());
app.use(passport.session());
var initPassport = require('./passport/init');
initPassport(passport);
app.use(function (req, res, next) {
res.locals = {
root : 'http://' + req.headers.host,
sitename : require('./config').get('sitename'),
config: config.get('env'),
url : config.get('url'),
user : req.user,
flash : req.flash()
};
next();
});
app.use(function (err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN'){
return next(err);
}
if(req.xhr){
return res.ok({payload: null}, '403 invalid csrf token');
}
// TODO handle CSRF token errors here
res.status(403);
res.send('form tampered with')
});
// This is never called when throwing errors like
// next(new Error('some error') or
// next({status: 500, message:'server error'});
app.use(function (err, req, res, next) {
console.error(err.stack);
// render an error page
});
};
The problem is, that your error handlers should always be at the end of your application stack. This means, that you can either move the error handler from your middleware to your app.js and use them after your requires (app.use()) or include your routes before your middleware.
Note: your error handler middleware MUST have 4 parameters: error, req, res, next. Otherwise your handler won't fire.
I had the same issue, spend the whole day on troubleshooting the issue. Finally, found a simple fix. This worked for me perfectly. You need to place the customer error handler right before the listener handler as below on the server instance file (App.js / server.js).
Good luck :)
app.use((error, req, res, next) => {
if (res.headersSent) {
return next(err)
}
res.status(500).send('INTERNAL SERVER ERROR !')
});
app.listen(3000, function() {
console.log('Node app is running on port 3000');
});
module.exports = app;
For handling errors that are thrown during asynchronous code execution in Express (versions < 5.x), you need to manually catch and invoke the in-built error handler (or your custom one) using the next() function.
app.get('/', (req, res, next) => {
setTimeout(() => {
try {
console.log('Async code execution.')
throw new Error('Unexpected error on async!')
} catch (error) {
// manually catching and propagating to your error handler
next(error)
}
}, 100)
})
// as a last declaration in your entrypoint
app.use((err, req, res, next) => {
// do some error handling here. if you do not call next, you must return a response from here.
})
Your error handler should always be at the end of your application stack.
Apparently it means not only after all app.use() but also after all your app.get() and app.post() calls.
If you don't want to write three parameters for every async router handler to be able to catch errors globally:
npm install express-async-errors
import 'express-async-errors';
app.get('/api/endpoint', async (req, res) => {
const user = await User.findByToken(req.get('authorization'));
if (!user) throw Error("access denied"); //will propagate to global error handler
});
i get 404 error when rooting with param , whereas all other rootings defined on my rootes/users.js file work perfectly , for example i get the desire result when i call :
localhost:3000/users/users .
but get 404 when i call localhost:3000/users/users/12315454 which should correspond to the rooter /users:user_id in my users.js (you can find it below)
var express = require('express');
var router = express.Router();
var User = require('../models/user');
router.route('/users:user_id')
.get(function(req, res) {
console.log("attempting user");
User.findById(req.params.user_id, function(err, place) {
if (err)
res.send(err);
res.json(place);
});
})
.put(function(req, res) {
console.log("attempting to update user");
User.findById(req.params.user_id, function(err, place) {
if (err)
res.send(err);
user.username = req.body.name;
user.visitedPlaces = req.body.visitedPlaces;
user.likedItems = req.body.likedItems;
//user.local.email= req.body.email;
user.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'user updated!' });
});
});
})
.delete(function(req, res) {
User.remove({
_id: req.params.user_id
}, function(err, bear) {
if (err)
res.send(err);
res.json({ message: 'Successfully deleted' });
});
});
router.route('/users')
// get all the users (accessed at GET http://localhost:8080/api/users)
.get(function(req, res) {
User.find(function(err, places) {
if (err)
res.send(err);
res.json(places);
});
});
router.route('/adduser')
.post(function(req, res) {
var user = new User();
user.password = user.generateHash (req.body.password); // set the users name (comes from the request)
user.username = req.body.username;
console.log(req.body)
console.log("user name :"+req.body.username);
user.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'user created!' });
});
});
module.exports = router;
my app.js config
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 session = require('express-session');
var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
var mongo = require('mongoskin');
var mongoose = require('mongoose');
var configDB = require('./config/database.js');
var port = process.env.PORT || 3030;
var router = express.Router();
// configuration ===============================================================
mongoose.connect(configDB.url); // connect to our database
//Facebook app credentials
var FACEBOOK_APP_ID = '******09';
var FACEBOOK_APP_SECRET = '9a*******3';
//app secret for dev = 9adfcaa6d7989d8adc12852badcf69f3
// app ifd for dev = 492502667544609
var app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'app')));
//var routes = require('./routes/index');
require('./config/passport')(passport); // pass passport for configuration
var users = require('./routes/users');
var places = require('./routes/places');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// set up our express application
app.use(logger('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser()); // get information from html forms
// required for passport
app.use(session({ secret: 'ilovescotchscotchyscotchscotch' })); // session secret
app.use(passport.initialize());
app.use(passport.session());
app.use(favicon());
//app.use(flash()); // use connect-flash for flash messages stored in session
// Make our db accessible to our router WARNING THIS MUST BE PUT before the rooting stuff above
app.use('/api', router);
app.use('/places', places);
app.use('/users', users);
// routes ======================================================================
require('./routes/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport
app.get('/', function(req, res, next) {
res.sendfile('./app/index.html');
});
/// catch 404 and forwarding 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: {}
});
});
// test authentication
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { console.log("is authenticated");
return next(); }
console.log("not authenticated");
res.redirect('/')
}
// launch ======================================================================
console.log('The magic happens on port ' + port)
module.exports = app;
Add a slash in your route, between the users and :user_id:
router.route('/users/:user_id')
^---------here