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')
});
Related
In my app I have a code from official docs, except one difference: I send xsrfToken in response to POST request, not GET.
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
var app = express()
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())
app.post('/getCsrfToken', /*csrfProtection,*/ function (req, res) {
// check credentials from request.body
// and then
res.render('send', { csrfToken: req.csrfToken() }) //EXCEPTION: csrfToken is not a function
})
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
I'm facing the egg-hen problem: if I enable csrfProtection, I cannot get into the endpoint's code without the token, but if I disable it, req.csrfToken becomes undefined.
I need the gerCsrfToken endpoint to be POST, because I don't want to expose password as url parameter.
Question was answered by csurf maintainer, thanks for a quick response!
https://github.com/expressjs/csurf/issues/133
The (tricky) solution is to ignore POST method for this particular endpoint
app.post('/authenticate', csrf({ cookie: true, ignoreMethods: ['POST'] }), function (req, res) {
req.body is always empty. I'm not sure what I'm doing wrong? I tried adding content-type headers as json but that didn't do anything either. Can someone lead me in the correct direction please? Thank you
EDIT: just for clarification purposes, my Angular frontend hits the backend function successfully, but req.body is empty. If I understand everything correctly, if I'm using the 'body-parser' library, it should be passed in through post through 'req.body'. I'm just not seeing that though and I'm not sure why.
EDIT2: I have the body parser code in my app.js but the backend routing in a index.js file, does that have anything to do with it?
EDIT3: app.js http://pastebin.com/9vNgf0Nd
index.js http://pastebin.com/icLa3e2X
ANGULAR FRONTEND
service.registerAccount = function(account) {
console.log(account); //account = { userName: 'test', password: 'hello' }
return $http({
method: 'POST',
url: '/register',
data: { account: account },
headers: {'Content-Type': 'application/json'}
});
}
BACKEND (app.js)
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
BACKEND (index.js)
var express = require('express');
var router = express.Router();
router.post('/register', function(req, res) {
console.log(req.body);
};
Please remove this line
app.use(bodyParser.urlencoded({ extended: true }))
Also,
app.use(bodyParser.json());
have to be called before app.use('/', routes);
And make sure to add Content-Type: application/json to the request header
What happens if you add the content type?
service.registerAccount = function(account) {
console.log(account); //account = { userName: 'test', password: 'hello' }
return $http({
method: 'POST',
url: '/register',
data: { account: account },
headers: {
'Content-Type': 'application/json'
}
});
}
Try this
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/register', function(req, res) {
console.log(req.body);
});
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
});
and then execute this from prompt:
$ curl localhost:8081/register -v --data "{\"name\":\"test\",\"password\":\"hello\"}" --header "Content-Type: application/json"
this works for me!
There is nothing wrong in the UI code. Not sure what is router so you may try this or post the code for router.
Try this (this works for me) or you can also use your router:
app.post('/register', function (req, res) {
console.log(req.body);
res.send('welcome, ' + req.body.account.username)
});
You are missing:
var app = express();
app.use(router);
If you want to user routers refers to following example:
UPDATE with full code:
var express = require('express');
var router = express.Router();
var app = express();
app.use(router);
router.post('/register', function(req, res) {
console.log(req.body);
};
app.route('/register')
.post(function (req, res) {
console.log(req.body);
res.send('welcome, ' + req.body.account.username)
})
I'm trying to verify a signed token and extract information from it using NodeJS.
I have a token named userToken in the browser right now, it has been saved after I logged in (I use auth0 to login by the way).
I tried to verify my token here manually : http://jwt.io , it works and gives me payload data without a problem. However, I can't do the same thing with NodeJS. How can I do it?
I read the docs but I couldn't get it.
https://github.com/auth0/express-jwt
Here's my server.js
var http = require('http');
var express = require('express');
var cors = require('cors');
var app = express();
var jwt = require('express-jwt');
var dotenv = require('dotenv');
dotenv.load();
var authenticate = jwt({
secret: new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64'),
audience: process.env.AUTH0_CLIENT_ID
});
// view engine setup
var path = require('path');
app.set('views', path.join(__dirname, 'views'));
app.use(express.static(path.join(__dirname, 'public')));
app.set('view engine', 'jade');
app.configure(function () {
// Request body parsing middleware should be above methodOverride
app.use(express.bodyParser());
app.use(express.urlencoded());
app.use(express.json());
app.use(cors());
app.use(app.router);
});
app.get('/', function (req, res) {
res.render('index');
});
app.get('/test', function(req,res) {
// how do I check it?
});
var port = process.env.PORT || 3001;
http.createServer(app).listen(port, function (err) {
console.log('listening in http://localhost:' + port);
});
You dont't need to implement nothing. Since you are using this express-jwt, just pass the userProperty tag to jwt:
var authenticate = jwt({
secret: new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64'),
audience: process.env.AUTH0_CLIENT_ID,
userProperty: 'payload'
});
So, you can get all of your jwt payload data using req.payload in your controllers. You can check it with console.log(req.payload).
You can see how it works here: https://github.com/auth0/express-jwt/blob/master/lib/index.js#L121
I hope it helps, and sorry about my English.
This sample should help you, it's not tested, but sure it's right way, look at source of express-jwt, it does literally same behind the scenes
app.get('/test', function(req, res) {
var jsonwebtoken = require('jsonwebtoken'); //install this, move to declarations
var loginToken = req.headers.authentication || req.body.userToken || req.headers.Bearer; //or your own, it's just headers that pass from browser to client
jsonwebtoken.verify(loginToken, new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64'), function(err, decoded) {
if(err) {
return res.status(401).send({message: 'invalid_token'});
}
//be aware of encoded data structure, simply console.log(decoded); to see what it contains
res.send(decoded); //`decoded.foo` has your value
});
});
The thing is that you must yourself encode your data, and then decode, so be aware that auth0 returns valid data structure for you (as i'm not sure otherwise)
After setting up the Drupal as this guide says: Drupal-passport I created a simple simple node app to test how it works.
It doesn't, I get the InternalOAuthError: Failed to obtain request token error.
Going through the strategy.js, I saw that my callbackURL is logging out undefined not exactly sure why. The callbackURL is set in my Drupal app
Also preforming a curl -i -XPOST http://extranet.local/rest/system/connect/ gives me exactly what I need
Here is my node.js code (keep in mind this is just supposed to test the drupal set up).
var express = require('express');
var passport = require('passport');
var dStrategy = require('passport-drupal').DrupalStrategy;
var passportDrupal = require('passport-drupal');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser')
var session = require('express-session');
var http = require('http');
var app = express();
var server = http.createServer(app);
app.use(cookieParser());
app.use(bodyParser());
app.use(session({ secret: 'SECRET' }));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new dStrategy({
consumerKey: "emDVp7P2LZFLPcN3cNCjLmrjrhQLnNv6",
consumerSecret: "mkbc3UYEuUQLNQRwLWo3B8zEk4ZrErKa",
providerURL: "http://extranet.local",
resourceEndpoint: "rest/system/connect", // <---- optional. Defaults to `rest/system/connect`
callbackURL: 'http://33.33.33.40:8888/auth/drupal/callback'
},
function(token, tokenSecret, profile, done) {
profile.oauth = { token: token, token_secret: tokenSecret };
done(null, profile);
}
));
app.get('/', function(req, res) {
res.writeHead(200);
res.end("This is root");
});
app.get('/auth/drupal',
passport.authenticate('drupal'),
function(req, res) {
// The request will be redirected to the Drupal website for
// authentication, so this function will not be called.
});
app.get('/auth/drupal/callback',
passport.authenticate('drupal', { failureRedirect: '/error' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/signedin');
});
app.get('/error', function(req, res) {
res.writeHead(200);
res.end("Could not sign in");
});
app.get('/signedin', function(req, res) {
res.writeHead(200);
res.end("signed in");
});
server.listen(8888, '33.33.33.40');
Any clues as to why or ideas are greatly appreciated
If you look into the strategy.js code of the library passport-drupal, you will see that the DrupalStrategy constructor does not expect a callbackURL property in the options parameter object and it also does not pass it further into the OAuthStrategy.
This is the code snippet that creates the parameter for the oauth strategy:
// Determine all necessary OAuth options
var oauthOptions = {
requestTokenURL: this._providerURL + '/oauth/request_token',
accessTokenURL: this._providerURL + '/oauth/access_token',
userAuthorizationURL: this._providerURL + '/oauth/authorize',
consumerKey: options.consumerKey,
consumerSecret: options.consumerSecret
};
OAuthStrategy.call(this, oauthOptions, verify);
It should be modified to pass the callbackURL, for example like this:
// Determine all necessary OAuth options
var oauthOptions = {
requestTokenURL: this._providerURL + '/oauth/request_token',
accessTokenURL: this._providerURL + '/oauth/access_token',
userAuthorizationURL: this._providerURL + '/oauth/authorize',
consumerKey: options.consumerKey,
consumerSecret: options.consumerSecret,
callbackURL: options.callbackURL// <==== THIS LINE WAS ADDED
};
OAuthStrategy.call(this, oauthOptions, verify);
I'm not sure that will solve your issue though. But I made a pull request
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...