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:
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:
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:
localhost:3000
, the server checks the credentials(using Passport-local) and returns a token in the response header.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.localhost:4000
and the server send a token in response header (same as localhost:3000
).localhost:4000
the passport js function req.isAuthenticated()
returns false
.cookie
doesn't go to the server we need to set withCredentials
header to true
at the client side.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 love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With