middleware not working when placed in if statement - javascript

Can someone tell me why this doesn't work:
app.use((req, res, next) => {
if (req.originalUrl === '/stripewebhook') {
next();
} else {
bodyParser.json();
bodyParser.urlencoded({
extended: true })(req, res, next);
}
});
But this works!
app.use((req, res, next) => {
if (req.originalUrl === '/stripewebhook') {
next();
} else {
bodyParser.json()(req, res, next);
bodyParser.urlencoded({
extended: true });
}
});
My original code was just the two bodyParsers middleware:
......
......
bodyParser.json();
bodyParser.urlencoded({
extended: true });
.....
I needed to add the if statement so that the webhook endpoint is skipped but I noticed that I had to set the statements like the above working example...what I am not sure about is if the .urlencoded() ever get executed??

I suspect that the Content Type of the Header may be not matching. You may take a look at this documentation: https://expressjs.com/en/resources/middleware/body-parser.html.
and also regarding the implementation: Trying to parse JSON Object from POST Request in Express v4 using body-parser

In node.js, middlewares like bodyParser.json() return functions. If the returned functions are not called, their behaviour does not apply.
The usual way to use them is to give their return values to app.use().
To have a conditional use of a middleware based on the route url, you can do something like:
const express = require('express')
const app = express()
const router = express.Router()
// specific router handling only your conditional middlewares
router.use(bodyParser.json())
router.use(bodyParser.urlencoded({ extended: true }))
// only call this router if the url is not /stripewebhook
app.use(/^(?!\/stripewebhook)/, router)
// [...] all your app-related middlewares
Of course this may not be the solution to your root issue regarding the stripe web hook.

Related

Express API Trying To Serve Public Files Gives 404

I currently have an API setup like so...
index.js
require('dotenv').config();
const index = require('./server');
const port = process.env.PORT || 5000;
index.listen(port, () => console.log(`Server is live at localhost:${port}`));
module.exports = index;
server/index.js
const express = require('express');
const routes = require('../routes');
const bodyParser = require('body-parser');
const helmet = require('helmet');
const morgan = require('morgan');
const path = require('path');
const server = express();
server.use(express.json());
// enhance your server security with Helmet
server.use(helmet());
// use bodyParser to parse server application/json content-type
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true }));
// log HTTP requests
server.use(morgan('combined'));
server.use(express.static(path.normalize(__dirname+'/public')));
server.use('/api', routes);
// Handle 404
server.use(function(req, res) {
res.send('404: Page not Found', 404);
});
// Handle 500
server.use(function(error, req, res, next) {
res.send('500: Internal Server Error', 500);
});
module.exports = server;
routes/index.js
const router = Router();
// enable all CORS requests
router.use(cors());
router.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
router.get('/', (req, res) => res.send('Welcome to Timelapse Videos API'));
....
For some reason my public directory always returns 404 and i don't know why. If i add this to the routes/index.js :
router.get('/public', function(req, res) {
res.sendFile(path.join(path.normalize(__dirname+'/../public'), 'index.html'));
});
It will return the static file but the issue is, there could be number of customer directories that have multiple images i want to return.
There is clearly an issue with my setup but i can for the life of me see whats going on. If i have an API all in an index.js and not split the router it seems to work.
Any help would fantastic and if you need more information please ask.
Try change this line:
server.use(express.static(path.normalize(__dirname+'/public')));
For:
server.use(express.static(__dirname + '/public'));
or
server.use(express.static('public'));
From your code, it appears the 'public' folder is at the root.
Just change
server.use(express.static(path.normalize(__dirname+'/public')));
to
server.use(express.static(path.normalize(__dirname+'./../public')));
OK, i'm not sure if i found the issue but i found a work around.
On the file server/index.js:
server/index.js
server.use(express.static(path.normalize(__dirname+'/public')));
server.use('/api', routes);
If changed the top line to:
server.use('/api', express.static(path.normalize(__dirname+'/public')));
server.use('/api', routes);
The below line is overriding the top line (I think, it wasn't working so thats my thought process).
Now if change it to this:
server.use('/api/media', express.static(path.normalize(__dirname+'/public')));
server.use('/api', routes);
Works perfectly.
Hope this helps someone else as i spent a whole day on this lol.

Express Body Parser with both JSON and binary data passing capability

In my express app router, I've routes for accepting POST request of JSON data as well as binary data. The problem is when I use body parser for passing JSON data, it also considers the binary data as JSON and gives error while POSTing binary data. i.e. when I use:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false}));
And When I remove this, It only works for binary data. Following is my route for POSTing binary file.
router.post('/file', function (req, res) {
var gridfs = app.get('gridfs');
var writeStream = gridfs.createWriteStream({
filename: 'file_name_here'
});
writeStream.on('close', function (file) {
res.send(`File has been uploaded ${file._id}`);
});
req.pipe(writeStream);
});
I've also tried moving this file route to other router. In that case, when I don't set anything regarding body parser, it still gives the same error.
One fix that works correctly is placing this file route in my main app.js prior to setting body parser. But I think this would not be good approach. I want these routes to be in separate files.
So what I'm missing here? Any alternatives will also be appreciated.
EDIT
As per the answer, I've first separated out my routers which requires body parsing and which do not. Also removed the bodu parser from my main app.use() Now in the router in which, I need body parser, I've added those 2 lines. But the behavior is same.
When I add those 2 lines, only JSON reqquest works and when I remove, only binary POST req. works.
Here is my updated code:
app.js
const express = require('express');
const app = module.exports = express();
const bodyParser = require('body-parser');
const port = 8080;
// //parsing incoming requests using body-parser middlewares
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({ extended: false}));
//adding routes
app.use(require('./routes/additionRouter'));
app.use(require('./routes/mediaRouter'));
//catch 404 file not found here
app.use((req, res, next) => {
const err = new Error('Page Not Found');
err.status = 404;
next(err);
});
//Error Handler
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.send(err.message);
});
app.listen(port, () => {console.log('Server listening on port: ' + port)});
additionRouter.js
const express = require('express');
const router = express.Router();
var exported = require('../config/dbConnection');
const bodyParser = require('body-parser');
// parsing incoming requests using body-parser middlewares
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: false}));
//Endpoint for adding new challenge
router.post('/endpoint1', (req, res, next) => {
});
module.exports = router;
and mediaRouter.js
const express = require('express');
const mediaRouter = express.Router();
const exported = require('../config/dbConnection');
exported.cb((gridfs) => {
//For adding media files to database named 'mediadb'
//POST http://localhost:8080/file
mediaRouter.post('/file', function (req, res) {
// var gridfs = app.get('gridfs');
var writeStream = gridfs.createWriteStream({
filename: 'file_name_here'
});
writeStream.on('close', function (file) {
res.send(`File has been uploaded ${file._id}`);
});
req.pipe(writeStream);
});
//GET http://localhost:8080/file/[mongo_id_of_file_here]
mediaRouter.get('/file/:fileId', function (req, res) {
// var gridfs = app.get('gridfs');
gridfs.createReadStream({
_id: req.params.fileId // or provide filename: 'file_name_here'
}).pipe(res);
});
});
module.exports = mediaRouter;
By specifying
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false}));
your entire app uses the body parser middleware. You could create another middleware to handle whether or not the body parser is used. For example:
const bodyParse = bodyParser.json();
app.use((req, res, next) => {
if(req.originalUrl == "restOfUrl/file") next();
else bodyParse(req, res, next);
});

Using CSRF in NodeJS

I am trying to use csrf in my NodeJS application.
You can see the code below. When I run this code I am getting "TypeError: req.csrfToken is not a function" error.
I want to create csrf token for all requests and want to check csrf tokens in ajax calls. As I said I can not create csrf token, I am getting error. Also how can I check csrf token in ajax calls?
Can you help me ?
Thanks
Server Side:
var express = require('express');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var csrf = require('csurf');
var bodyParser = require('body-parser');
/*this line commented*/
//var csrfProtection = csrf({ cookie: false });
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
var parseForm = bodyParser.urlencoded({ extended: false });
app.use(cookieParser());
/*this line added*/
app.use(csrf({ cookie: false }));
app.use(session({
genid: function (req) {
return "lkgktktgjknvfndkj-dfgjnkdfkjgn-dfgdfg";
},
name: "mySecret",
resave: false, // don't save session if unmodified
saveUninitialized: false, // don't create session until something stored
secret: 'thisIsASecret'
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(function (req, res, next) {
res.locals.csrfToken = req.csrfToken();
next();
});
app.get('/', /*csrfProtection,*/ function (req, res) {
res.render('index')
});
app.post('/process', parseForm, /*csrfProtection,*/ function (req, res) {
res.send('data is being processed')
});
Index.jade
meta(name="csrf-token", content="#{csrfToken}")
block content
input(type="hidden" name="_csrf" value="#{csrfToken}")
|Favorite color: <input type="text" name="favoriteColor">
button(type="submit" id="sbmt") Submit
script(src= "/javascripts/jquery-2.2.1.js")
script.
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
var token;
if (!options.crossDomain) {
token = $('meta[name="csrf-token"]').attr('content');
if (token) {
return jqXHR.setRequestHeader('X-CSRF-Token', token);
}
}
});
$("#sbmt").click(function (e) {
e.preventDefault();
$.post(
"/process",
{
//text: text,
_csrf : $('meta[name="csrf-token"]').attr('content')
}, function (data) {
console.log(data);
});
});
You have to add:
app.use(session({ ... });
// Add this after session
app.use(csrfProtection);
You need to add this AFTER the session as stated here:
If you are setting the "cookie" option to a non-false value, then you
must use cookie-parser before this module. Otherwise, you must use a
session middleware before this module. For example: express-session
cookie-session
Calling csrf() returns a function (source). You need to use it in order to have it. What you've missed in the tutorial is:
var csrfProtection = csrf({ cookie: true })
app.get('/form', csrfProtection, function(req, res) {
// pass the csrfToken to the view
res.render('send', { csrfToken: req.csrfToken() })
})
Here, the csrfProtection is actually being called, and it adds csrfToken method to req. In the other example there is:
app.use(csrf({ cookie: true }))
Which means all routes will use the protection and therefore no post without it would be possible.
It depends on your usage - if you want to secure all routes - use it globally (app.use), otherwise use it per request (as in the first example).
If you try using it in your index route you will have it, because you've used it as middleware:
app.get('/', csrfProtection, function (req, res) {
res.render('index')
});

How to retrieve form data sent over HTTPS in node?

I'm a bit of a back-end security n00b, so please be gentle if I'm missing something obvious:
When I get values over HTTP in node, the form data is in the request object req.body.{name of input element}
Over HTTPS, req.body doesn't seem to exist. I've tried logging out the req object but I can't see it anywhere in there. What am I doing wrong?
function checkUser(req, res) {
console.dir(req);
//if passwords don't match
if (req.body.password !== req.body.confirm_password) {
return false;
}
return true;
}
app.post('/register', function(req, res) {
if (checkUser(req, res)) {
createUser(req, res)
res.redirect('/browse?p=0');
}
res.render('register', {
error: 'Passwords did not match'
})
});
As soon as it goes to the checkUser method it crashes saying that req.body is not defined. So where is the data kept?
Any help will be appreciated...
Thanks
James
req.body only exists if you link in the appropriate middleware to parse the request body. I recommend the following:
app.use(express.urlencoded());
app.use(express.json());
You often see express.bodyParser() being used, but I recommend avoiding this because it also includes express.multipart(), which has been deprecated, and will disappear when Express updates its dependency on Connect. If you need to parse multipart form data, look into Busboy or Formidable.
I don't think your issue has anything to do with HTTPS: parsing the request body is the same process in HTTP and HTTPS.
OK, I got it...
I'd called things in the right order, but I'd included all the passportjs stuff (and the corresponding middlewares) in a module file. Because of probably scope or race conditions it didn't register the middleware before the route and controller were executed.
in web.js:
app = express();
app.settings.env = 'development';
app.engine('dust', dustjs.dust({
cache: false
}));
app.set('view engine', 'dust');
app.set('views', __dirname + '\\views');
//Middleware
app.use(express.methodOverride());
app.use(express.favicon(__dirname + '/public/images/favicon.ico', {
maxAge: 2592000000
}));
app.use(app.router);
//Environment Variables
//app.configure('development', function() {
app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
}));
dustjs.isDebug = true;
auth = require('./modules/auth/auth').auth(app);
in auth.js
module.exports.auth = function(app) {
//verification using passport.js
passport = require("passport");
express = require('express');
LocalStrategy = require('passport-local').Strategy;
FacebookStrategy = require('passport-facebook').Strategy;
TwitterStrategy = require('passport-twitter').Strategy;
app.use(express.cookieParser());
app.use(express.urlencoded());
app.use(express.json());
app.use(express.session({
secret: 'SECRET'
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(
etc...

Handling redirection in ExpressJS while avoiding redirect loop

This is my first time using Express' app.all(). When a user signs up through an outside oAuth provider, I still need them to provide an email after returning to the site. I'm basically setting them as inactive in the database and checking for req.session.active.
What I'm doing is
app.all('*', function(req, res, next) {
if(!req.session.active) {
if(req.path == '/complete_signup') {
next();
} else {
return res.redirect('/complete_signup');
}
}
});
But this doesn't seem to be working. How can I correctly check if the user is already redirected?
If you can suggest a method other than app.all(), that would work, too.
EDIT:
On second look, this is working, but none of the external resources (stylesheets, javascripts, etc.) seem to be loading since they don't match req.path.
You can use the express-redirect-loop middleware (which uses sessions since HTTP Referrer header is unreliable). This will only work for requests that support cookie storage/jar (e.g. browser).
const express = require('express');
const session = require('express-session');
const redirectLoop = require('express-redirect-loop');
const app = express();
app.use(
session({
secret: 'test',
resave: false,
saveUninitialized: true
})
);
app.use(redirectLoop());
app.get('/', (req, res) => res.sendStatus(200));
app.get('/bar', (req, res) => res.redirect('/foo'));
app.get('/foo', (req, res) => res.redirect('/foo'));
app.get('/baz', (req, res) => res.redirect('/bar'));
app.listen(3000);

Categories

Resources