express csurf (csrf middleware) not working with XSRF in angularjs - javascript

Express and Angular both have their own csrf middleware. I cannot get them to work at all and there does not seem to be any coherent guide on the internet about this. My understanding is that express 4.0 uses csurf as its csrf middleware, and I have to set X-XSRF-TOKEN on angularjs.
There are scattered pieces about how to do this, with information that sometimes conflict:
How to test endpoints protected by csrf in node.js/express
angular, django and csrf
CSURF Angular Implementation
But I have tried them and they do not work. My _csrf is always undefined on the Angular clientside, and csurf always gives success despite no csrf token was given from the client.
Furthermore, I am using express-jwt to keep user session, so I am unsure if this will interfere with cookie-session (required by curf).
Here is my simple angular/express app for handling register with csrf (not working):
angular app.js
var app = angular.module('app', ['ngCookies', 'ui.router']);
app.config(function($stateProvider) {
$stateProvider.state('register', {
url: '/register',
controller: 'RegisterCtrl',
templateUrl: 'views/register.html'
});
});
// register csrf
app.run(['$http', '$cookies', function($http, $cookies) {
$http.defaults.headers.post['X-XSRF-TOKEN'] = $cookies.csrftoken;
}]);
// controller
app.controller('RegisterCtrl', ['$scope', '$cookies', '$http',
function($scope, $cookies, $http) {
$scope.data = { email: "", password: "", _csrf: $cookies._csrf};
$scope.submitForm = function() {
// This will alert 'undefined'
alert("This is csrf token: " + $scope.data._csrf);
$http.post('/register', data).success(function(done) {
console.log('success');
var jwt_token = done["jwt_token"];
// save token to local storage.
}).error(function(err) {
console.log('error');
});
}
}
]);
express app.js
var express = require('express');
var app = express();
var http = require('http').Server(app);
var expressJwt = require("express-jwt");
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: true});
app.use(bodyParser.json());
// csrf setup
var session = require('cookie-session');
var csrf = require('csurf');
app.use(session({
secret: 'keyboard cat'
}));
app.use(csrf());
// This part taken from csurf guide:
// https://github.com/expressjs/csurf
app.use(function(err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN') return next(err)
res.status(403);
res.send('session has expired or form tampered with');
});
app.post("/login", function(req, res) {
var email = req.body.email;
var password = req.body.password;
// save user to db ...
var jwt_token = // create and sign jwt token to be given back to registered user
res.json({"jwt_token": jwt_token});
});
http.listen(3000, function() {
console.log("Express started");
});
Now, if I submit the register form, the alert() will say the _csrf is undefined. On the expressjs side, app.post('/login') is run all the way through without having failed even though the csrf token should be bad (undefined since csrf on angular side is not working). This indicates that csurf is not working at all on the express side.
Can someone provide an in-depth explanation to integrating csurf in express 4.0, xsrf in Angular, and have them work together?

I had problems integrating both as well, and reached the following structure (just relevant parts):
express app.js
var cookieParser = require('cookie-parser');
var session = require('cookie-session');
var csrf = require('csurf');
app.use(session({
secret: 'keyboard cat'
}));
app.use(cookieParser('secret'));
app.use(csrf());
app.use(function (req, res, next) {
res.cookie("XSRF-TOKEN",req.csrfToken());
return next();
});
In AngularJS side there is no need of changes. It identifies XSRF-TOKEN automatically.
Now, to explain the piece of code above, i need to refer to the csurf lib. It is a middleware to handle all the csrf authentication, but it implements another csrf library, called csrf. This csrf returns a "class" with four functions (secret, secretSync, create, verify).
What csurf does is, when you call its middleware, generates a secret through the secretSync method and stores in your session. You can access that through the variable req.session.csrfSecret. However, it does not execute the req.csrfToken method, which uses this secret to return a csrf Token. To return correctly the csrf token, you need to call it in another middleware.
Another important thing, the name of the returning cookie must be "XSRF-TOKEN", and not "_csrf". Therefore, Angular identifies it automatically and appends to HTTP requests the "X-XSRF-TOKEN", which is identified by the csurf middleware.
Hope it helps.

Related

Node js authentication in cross domain

I am working on a MEAN application, I am using Angular 4 for my project. For authentication, I have implemented the Passport js Local-strategy. And I am maintaining persistent session using Express-session. Things are working fine till here.
The Problem
In the same domain session works fine and I am able to authenticate the user. But in cross-domain, I am not able to maintain the session. It generates a new session id for each new request in cross-domain.
I then tried Passport-jwt but the problem with it is I don't have the control over user session. I mean I can't logout the user from the server if he is inactive or even on server re-start also the token don't get invalid.
So in simple words, I am looking for an authentication solution in Node js (Express js) in which I can manage authentication in cross-domain.
I have already seen some blog post and SO questions like this, but it doesn't help.
Thank you.
EDIT
Should I write my own code to achieve this? If so I have a plan.
My basic plan is:
The user will send credentials with the login request.
I will check for the credentials in the database. If credentials are valid, I will generate a random token and save it to the database, in the user table and the same token I will provide to the user with success response.
Now, with each request user will send the token and I will check the token for each request in the database. If the token is valid then I will allow the user to access the API otherwise I will generate an error with 401 status code.
I am using Mongoose (MongoDB) so I will be ok to check the token in each request (performance point of view).
I think this is also a good idea. I just want some suggestions, whether I am thinking in right direction or not.
What I will get with this:
The number of logged in user in the application (active sessions).
I can logout a user if he is idle for a certain interval of time.
I can manage multiple login session of the same user (by doing an entry in the database).
I can allow the end user to clear all other login sessions (like Facebook and Gmail offers).
Any customization related to authorization.
EDIT 2
Here I am shareing my app.js code
var express = require('express');
var helmet = require('helmet');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var dotenv = require('dotenv');
var env = dotenv.load();
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var session = require('express-session');
var cors = require('cors');
var databaseUrl = require('./config/database.js')[process.env.NODE_ENV || 'development'];
// configuration
mongoose.connect(databaseUrl); // connect to our database
var app = express();
// app.use(helmet());
// required for passport
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) {
res.send(200);
} else {
next();
}
});
app.use(cookieParser());
app.use(session({
secret: 'ilovescotchscotchyscotchscotch', // session secret
resave: true,
saveUninitialized: true,
name: 'Session-Id',
cookie: {
secure: false,
httpOnly: false
}
}));
require('./config/passport')(passport); // pass passport for configuration
var index = require('./routes/index');
var users = require('./routes/user.route');
var seeders = require('./routes/seeder.route');
var branches = require('./routes/branch.route');
var companies = require('./routes/company.route');
var dashboard = require('./routes/dashboard.route');
var navigation = require('./routes/navigation.route');
var roles = require('./routes/role.route');
var services = require('./routes/services.route');
// 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: true }));
// app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
require('./routes/auth.route')(app, passport);
app.use('/', index);
app.use('/users', users);
app.use('/seed', seeders);
app.use('/branches', branches);
app.use('/companies', companies);
app.use('/dashboard', dashboard);
app.use('/navigation', navigation);
app.use('/roles', roles);
app.use('/services', services);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
res.status(404).send({ status: 'NOT_FOUND', message: 'This resource is not available.'});
});
// 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
let errorObj = {
status: 'INTERNAL_SERVER_ERROR',
message: 'Something went wrong.',
error: err.message
};
res.status(err.status || 500).send(errorObj);
});
module.exports = app;
EDIT 3
For those who don't understand my problem. Explaining the problem in
simple words:
My Express server is running on port 3000.
In order to consume any API from the server, a user must be logged in.
When a user gets logged in from localhost:3000, the server checks the credentials(using Passport-local) and returns a token in the response header.
Now after login, when a user hits any API from localhost:3000, a predefined Header comes with passport-session and then passport verifies the user session using req.isAuthenticated() and all the things works as expected.
When a user gets logged in from localhost:4000 and the server send a token in response header (same as localhost:3000).
When after successful login, the user hits any API from localhost:4000 the passport js function req.isAuthenticated() returns false.
This was happening because in cross-domain the cookie doesn't go to the server we need to set withCredentials header to true at the client side.
I have set withCredentials header to true but still at the server the req.isAuthenticated() is returning false.
One possible solution to get around CORS/cookie/same-domain problems is to create proxy server that will mirror all requests from localhost:3000/api to localhost:4000, and then use localhost:3000/api to access the API instead of localhost:4000.
Best way for production deployment is to do it on your web server (nginx/apache).
You can also do it in node via express and request modules, or use some ready made middleware like this one:
https://github.com/villadora/express-http-proxy
Solution with this middleware is pretty straightforward:
var proxy = require('express-http-proxy');
var app = require('express')();
app.use('/api', proxy('localhost:4000'));
If you want to use sessions (ie. instead of jwt, etc) I think by default they are just in-memory so it will not work as your application scales to multiple hosts. It is easy to configure them to persist though.
See
https://github.com/expressjs/session#compatible-session-stores
You might have tried with passport-jwt. It generates tokens as per the JWT protocol on login. Your requirement is to blacklist the generated token when you logout. To achieve that, you can create a collection in mongodb named "BlacklistToken" with fields userid and token. When the user logs out, you can insert the token and userid in the collection. Then write a middleware to check whether the token is blacklisted or not. if it is redirect to login page.
did you already take a look here:
In this case, responses can be sent back based on some considerations.
If the resource in question is meant to be widely accessed (just like any HTTP resource accessed by GET), then sending back the Access-Control-Allow-Origin: * header will be sufficient,[...]
You may try this (allow any public IP) :
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', '*'); // add this line
// res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
It is normal that the second server re-create a new session, because assuming that you use Express-session, and according to the documentation:
Session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.
Which mean that you need to find a way to synchronize servers session data ...
Assuming that you find a method to do that, when you will try to connect, both server will retrieve the same user session data and the second will not have to create a new session...
If I understand the problem correctly here, you want the user's session to be stateless on the server. So that whenever the user logs in, the session can be re-used in any instance of the server when you scale your application, or even if you were to just reboot your application.
To achieve this, what you need is to configure the express-session with a database solution. You can do this with mongo using this package https://github.com/jdesboeufs/connect-mongo.
However, best practice is to use something a bit more robust for this sort of use-case, like redis using this package https://github.com/tj/connect-redis.

Nodejs app throws cannot POST /users with body-parser middleware

Here's my app.js file.
var express = require('express'),
bodyParser = require('body-parser'),
oauthServer = require('oauth2-server'),
oauth_model = require('./app_modules/oauth_model')
const app = express()
app.use(bodyParser.urlencoded({ extended: true }));
var jsonParser = bodyParser.json();
app.oauth = oauthServer({
model: oauth_model, // See below for specification
grants: ['password', 'refresh_token'],
debug: process.env.OAUTH_DEBUG,
accessTokenLifetime: 172800,
refreshTokenLifetime: 172800,
authCodeLifetime: 120,
});
// Oauth endpoint.
app.all('/oauth/token', app.oauth.grant());
// User registration endpoint.
app.post('/users', jsonParser, require('./routes/register.js'));
// Get user details.
app.get('/users', app.oauth.authorise(), require('./routes/users.js'));
app.post('/', app.oauth.authorise(), require('./routes/test.js'));
app.use(app.oauth.errorHandler());
app.listen(3000, function () {
console.log('Mixtra app listening on port 3000!')
})
When I send an invalid json body with POST request to localhost:3000/users the request goes to register.js and the validation code works there.
but strangely when I send valid JSON body, it says "Cannot POST /users" with a 404 Not Found HTTP status code and nothing in terminal log.
Note: I'm using postman to send the api requests.
It would be really great if someone could help me with this.
Thanks,
Joy
I don't see you using the jsonParser
You should use it before sending any json to it
app.use(jsonParser);

Twitter stream API returning 401

I'm using the Twit Node module with Node.js to try to track certain hashtags for an app I'm writing and I'm not getting anything out of the Api other than an error saying "Error: Bad Twitter streaming request". As far as I can tell I've followed the documentation but I'm obviously missing something.
Here is the code, with a few bits redacted:
var express = require('express');
var app = express();
app.set('view engine', 'ejs');
app.use(express.static('public'));
app.get('/', function(req, res) {
res.render('pages/index');
});
var http = require('http').createServer(app);
http.listen(80);
var io = require('socket.io').listen(http);
var twitterApi = require('twit');
var twitter = new twitterApi({
consumer_key: '***',
consumer_secret: '***',
app_only_auth: true
});
var stream = twitter.stream('statuses/filter', {track: '#twitter'});
stream.on('tweet', function(tweet){
console.log(tweet);
});
I'm working on an Ubuntu VM through Vagrant if that makes any difference?
Did you get the API credentials right ? 401 is usually returned with a unauthorized request, so I guess it must be with your API credentials.
You haven't provided access token and access token secret

Migrating away from bodyParser() in Express app with busboy?

Being a newbie in Nodejs, I jumped right into writing a simple app without really reading up on good security practices. I just found out that using bodyParser() for all routes is actually a bad thing because it allows for DOS attack using multipart files.
A recommended fix is to only load specific modules depending on the route. ie, for multipart fileupload, use multipart. For regular POST without file uploads (ie, text form submission), use express.json(), express.urlencoded().
Or another option is to use busboy with connect-busboy. But the thing I'm confused on is how I can specify which route should handle multipart data and which should not? Otherwise, wouldn't I have the same problem as with bodyParser?
Furthermore, busboy docs says it does not handle GET:
If you find that req.busboy is not defined in your code when you expect it to be, check that the following conditions are met. If they are not, req.busboy won't be defined:
1. The request method is not GET or HEAD
So, I'm even more confused how I would parse params in a GET. I think bodyParser does this for me so I could access data with req.params.
For example, how would I migrate away from bodyParser() to busboy/connect-busboy with this simple app:
var express = require('express');
var app = express();
var http = require('http').Server(app);
var bodyParser = require('body-parser');
app.use(bodyParser.json());
var busboy = require('connect-busboy');
app.use(busboy());
// How to use busboy to prevent multipart files here?
app.post("/form_data_no_fileupload", function(req, res) {
var somedata = req.body.somedata;
});
// Use busboy to handle both regular form data + fileuploads
app.post("/form_data_AND_fileupload", function(req, res) {
});
// What would handle GET without bodyparser?
app.get("/get_something", function(req, res) {
var params = req.params;
});
http.listen(3000, function() {});
[How] I can specify which route should handle multipart data and which should not?
All of Express' routing methods allow for providing middleware specific to the route. This includes Router methods.
app.METHOD(path, callback [, callback ...])
Depending on the body expected for an individual route, you can use different modules to handle each of them (rather than applying them to the entire application with app.use()).
var express = require('express');
var app = express();
var http = require('http').Server(app);
var bodyParser = require('body-parser');
var busboy = require('connect-busboy');
app.post("/form_data_no_fileupload",
bodyParser.urlencoded(),
function(req, res, next) {
// check that the request's body was as expected
if (!req.body) return next('route'); // or next(new Error('...'));
// ...
});
app.post("/form_data_AND_fileupload",
busboy({
limits: {
fileSize: 10 * 1024 * 1024
}
}),
function(req, res, next) {
// check that the request's body was as expected
if (!req.busboy) return next('route'); // or next(new Error('...'));
// ...
});
// ...
Furthermore, busboy docs says it does not handle GET.
So, I'm even more confused how I would parse params in a GET.
Busboy and BodyParser are designed for reading in and parsing the request's body, which GET and HEAD requests aren't expected to have.
For such requests, parameters can only be passed within the query-string within the URL, which Express parses itself. They're available via req.query.
app.get('/get_something', function () {
console.log(req.originalUrl);
// "/get_something?id=1
console.log(req.query);
// { id: "1" }
});
req.params represents any placeholders matched in the path by the route. These are available for any route, regardless of the method.
app.get('/thing/:id', function (req, res) {
console.log(req.originalUrl);
// "/thing/2"
console.log(req.params);
// { id: "2" }
});

Maintain sessions in Node.js

How can I maintain my SESSIONS in Node.js?
For example, I want to store UserID in SESSION using Node.js. How can I do that in Node.js? And can I use that Node.js SESSION in PHP too?
I want the following in Node.js:
<?php $_SESSION['user'] = $userId; ?>
First install the session package
npm install express-session --save
Initialization of the session on your server page
var express = require('express');
var session = require('express-session');
var app = express();
app.use(session({secret: 'ssshhhhh', saveUninitialized: true, resave: true}));
Store session
sess = req.session;
var user_id = 1;
sess.user_id = user_id;
Access the session
sess = req.session;
sess.user_id
Let me divide your question in two parts.
How can I maintain my SESSIONS in Node.js?
Answer: Use express-session middleware for maintaining SESSIONS
Can I use that a Node.js SESSION in PHP too?
Answer:
Yes, you can use that session in PHP too, but keep in mind you have to store that session in the database.
ExpressJS has official session middleware, and it is also the current de-facto standard web framework for Node.js.
If you wish to implement session support on your own, this is how the implementation is normally done, upon every request:
Check if the cookie contains a session ID
If not, create a session object that is either stored in memory, on file, or in a database (or a combination of those), and set the session id in the response cookie to match this object's identifier.
If the cookie does contain a session ID, locate the session object by the ID.
Provide the obtained/created object from step 1 as the persisted session object for the request.
You will also have to implement some timeout mechanism, so that after a while the session objects are deleted, at least from memory.
You could use the express-session middleware.
Combine it with connect-redis or connect-mongo to store your sessions inside a database and save memory if memory is valuable to you (like in a cloud setup).
express-sessions (npm)
If you store it in, say, MongoDB, use the PHP MongoDB driver to pick it up from there.
You don't need to do it by yourself. There are some amazing modules in Node.js that handle this kind of things for you.
You can use session middleware from Express.js, as suggested before.
However, I'd recommend you to use Passport.js. This module does the authentication part for you, has a lot of strategies that you could integrate in your website (log in with Facebook, Google, Twitter, etc.), and deals with all the session stuff automatically, using serializeUser() and deserializeUser() functions whenever you need to.
You can take a look at this here, within the "Sessions" section: Configure Passport.js
Session that gives access/permission to view a user's area, as well as it's a credential, so we can use it over the application.
I used jsonwebtoken to make a token which will has the user's details with time after a successful login attempt by the user. I stored it in Redis, and it can be used for a pre-declared time limit.
To maintain a session is now older, and you should try with using JWT token. It is very effective and easy. But still to maintain the session in Node.js:
In your Express.js configuration:
var cookieParser = require('cookie-parser');
var session = require('express-session');
app.use(cookieParser());
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
rolling: true,
cookie: {
path: '/',
maxAge: 60000 * 1000
},
name: 'SID'
}));
Store session after Login:
var session = req.session;
if (user) {
session.user = user._id;
session.save();
console.log(session);
}
Check Session from middleware:
var session = req.session;
if (session.user) {
req.userid = session.user;
next();
} else {
return res.status(401).send({
code: 401,
message: Constant.authentication_fails
});
}
Follow the below steps:
npm install express-session --save
Write the below code:
var express = require('express');
var session = require('express-session');
var app = express();
app.use(session({secret: 'your secret key', saveUninitialized: true, resave: true}));
var userId = 1234;
app.get('/', function (req, res, next) {
req.session.userId = userId;
});
Storing a session in Node.js is fairly easy but you need to understands its step, you could handle this manually, also you can use few NPM modules. Passport can help you to authenticate and login and store the session i would recommend you to read its documentation, Passport allow you to authenticate user with different other platform like Google, github many more.
If you are going to use passport use these below NPM module
Passport
Passport Local
Express-flash
Express-session
2 -Import these modules in your main app.js:
const flash = require('express-flash')
const session = require('express-session')
const passport = require('passport')
app.use(session({
secret:'secret',
resave:false,
saveUninitialized:false
}))
app.use(flash())
app.use(passport.initialize())
app.use(passport.session())
3- Create the passport.js file. You can name anything. So basic understanding behind this is that you have to check the valid user coming from your input form, and you have to compare the email id with your model. If it is valid, check the password and then return the user. Once that is done, serialize and deserialize your user data to store in the session..
I would recommend to check this part in the documentation for more clear understanding: Overview
const localStrategy = require('passport-local').Strategy
const bycrypt = require('bcrypt')
const User = require('../model/User')
const initalize = function(passport) {
const auth = async(email, password, done) => {
try {
const user = await User.findOne({email:email})
if(!user) {
throw new Error("Incorrect Email ..!")
}
const match = await bycrypt.compare(password, user.password)
if(!match) {
throw new Error('Incorrect Password..!')
}
return done(null, user)
}
catch (error) {
console.log(error)
done(null,false,error)
}
}
passport.use(new localStrategy({usernameField:'email'}, auth))
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
module.exports = initalize
4 - Now go to your login router and use the below code
const passport = require('passport')
require('../passport/passport')(passport)
routes.get('/signin', (req,res) => {
res.render('signin', {
pageTitle: 'sign in'
})
})
routes.post('/signin', passport.authenticate('local', {
successRedirect: '/welcome',
failureRedirect: '/',
failureFlash: true
}))
You can use sessions in Node.js by using the 'express-session' package in Node.js.
You have to install express and express-session in your application:
const express = require('express');
const session = require('express-session');
const app = express();
"secret" is used for the cookie, and we have to add some secret for managing a session.
"request" we use as a request variable as we use $_SESSION in PHP.
var sess;
app.get('/',function(req,res){ // Get request from the app side
sess = req.session;
sess.email; // Equivalent to $_SESSION['email'] in PHP.
sess.username; // Equivalent to $_SESSION['username'] in PHP.
});
Here is full documentation in Code for Geek about the session in Node.js if you want to learn in detail about the session in Node.js.
You can handle the session in two ways.
Using express-session
Using JWT web token and handle your own session (token-based session handling).
I think token-based session handling is more important rather than using express-session. You will get a problem when you scale your server and also a problem with some single device login situation.
For checking I have a token-based session handling Node.js folder structure. You can check it, and it may be helpful.

Categories

Resources