Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  1. The user will send credentials with the login request.
  2. 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.
  3. 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.
  4. 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:

  1. The number of logged in user in the application (active sessions).
  2. I can logout a user if he is idle for a certain interval of time.
  3. I can manage multiple login session of the same user (by doing an entry in the database).
  4. I can allow the end user to clear all other login sessions (like Facebook and Gmail offers).
  5. 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:

  1. My Express server is running on port 3000.
  2. In order to consume any API from the server, a user must be logged in.
  3. 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.
  4. 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.
  5. When a user gets logged in from localhost:4000 and the server send a token in response header (same as localhost:3000).
  6. When after successful login, the user hits any API from localhost:4000 the passport js function req.isAuthenticated() returns false.
  7. 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.
  8. I have set withCredentials header to true but still at the server the req.isAuthenticated() is returning false.
like image 564
Arpit Kumar Avatar asked Nov 26 '17 14:11

Arpit Kumar


Video Answer


1 Answers

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'));
like image 79
Martin Adámek Avatar answered Oct 09 '22 23:10

Martin Adámek