skip to Main Content

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.

5

Answers


  1. 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

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
  3. 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…

    Login or Signup to reply.
  4. 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.

    Login or Signup to reply.
  5. 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'));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search