I'm building web app using Node.js Express.js for the server-side and Angular 6 SPA for the client.
Using the simple Express.js code, below, I've successfully authenticated a user via SAML2.js ADFS and now I want to access the user on the client side Angular SPA. How do I do that?
I found a similar setup here, but there is not an answer there and its a bit dated.
var saml2 = require('saml2-js');
var fs = require('fs');
var express = require('express');
var https = require('https');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({
extended: true
}));
// Create service provider
var sp_options = {
entity_id: "https://localhost:44301/",
private_key: fs.readFileSync("key.pem").toString(),
certificate: fs.readFileSync("certificate.crt").toString(),
assert_endpoint: "https://localhost:44301/assert",
force_authn: true,
auth_context: { comparison: "minimum", class_refs: ["urn:oasis:names:tc:SAML:2.0:ac:classes:password"] },
nameid_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
sign_get_request: false,
allow_unencrypted_assertion: true
};
var sp = new saml2.ServiceProvider(sp_options);
// Create identity provider
var idp_options = {
sso_login_url: "https://mmusmaadfs.company.com/adfs/ls/",
sso_logout_url: "https://mmusmaadfs.company.com/adfs/ls/",
certificates: [fs.readFileSync("./2018ADFSSigningBase64Cert.cer").toString()],
force_authn: true,
sign_get_request: false,
allow_unencrypted_assertion: true
};
var idp = new saml2.IdentityProvider(idp_options);
// ------ Define express endpoints ------
// Endpoint to retrieve metadata
app.get("/metadata.xml", function(req, res) {
res.type('application/xml');
res.send(sp.create_metadata());
});
// Starting point for login
app.get("/login", function(req, res) {
sp.create_login_request_url(idp, {}, function(err, login_url, request_id) {
if (err != null)
return res.send(500);
res.redirect(login_url);
});
});
// Assert endpoint for when login completes
app.post("/assert", function(req, res) {
var options = {request_body: req.body};
sp.post_assert(idp, options, function(err, saml_response) {
if (err != null){
console.log("got here");
console.log(err);
return res.send(err);
}
// Save name_id and session_index for logout
// Note: In practice these should be saved in the user session, not globally.
name_id = saml_response.user.name_id;
session_index = saml_response.user.session_index;
res.send("Hello " +name_id +".");
//res.send("Hello #{saml_response.user.name_id}!");
});
});
// Starting point for logout
app.get("/logout", function(req, res) {
var options = {
name_id: name_id,
session_index: session_index
};
sp.create_logout_request_url(idp, options, function(err, logout_url) {
if (err != null)
return res.send(500);
res.redirect(logout_url);
});
});
var httpsOptions = {
key: fs.readFileSync('./key.pem')
, cert: fs.readFileSync('./certificate.crt')
}
var httpsServer = https.createServer(httpsOptions, app);
// app.listen(44301,console.log("App on 44301"));
httpsServer.listen(44301,console.log("App on 44301"));
Related
I'm new on node js, and the company that i work for needs a proof of concept about postgraphile, the situation is this:
I created a node js mini server that uses postgraphile to access the data on postgres
The mini server works fine and can return data and also can use mutations.
I used keycloak-connect to try to access keycloak to authenticate the token from the request that is sent by postman but there is a problem.
If the token is valid or not it does not matter for the mini server, the only thing that seems to matter is that is a bearer token.
I tried to use other plugins (like keycloak-nodejs-connect, keycloak-verify, etc) but the result is the same, i also changed my code to use the examples in the documentation of those plugins but nothing.
This is my code: (keycloak-config.js file)
var session = require('express-session');
var Keycloak = require('keycloak-connect');
let _keycloak;
var keycloakConfig = {
clientId: 'type credential',
bearerOnly: true,
serverUrl: 'our company server',
realm: 'the test realm',
grantType: "client_credentials",
credentials: {
secret: 'our secret'
}
};
function initKeycloak(){
if(_keycloak){
console.warn("Trying to init Keycloak again!");
return _keycloak;
}
else{
console.log("Initializing Keycloak...");
var memoryStore = new session.MemoryStore();
_keycloak = new Keycloak({store: memoryStore}, keycloakConfig);
return _keycloak;
}
}
function getKeycloak(){
if(!_keycloak){
console.error('Keycloak has not been initialized. Please called init first');
}
return _keycloak;
}
module.exports = {
initKeycloak,
getKeycloak
};
My Index.js file:
const express = require('express')
const bodyParser = require('body-parser')
const postgraphile = require('./postgraphile')
const app = express()
const keycloak = require('../config/keycloak-config').initKeycloak()
var router = express.Router();
app.set( 'trust proxy', true );
app.use(keycloak.middleware());
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(postgraphile);
app.get('/', keycloak.checkSso(), (req, res) => {
res.send('success');
} );
var server = app.listen(8080, () => console.log(`Server running on port ${8080}`));
Also I used this code to get the token and use the keycloak-verify plugin but got nothing:
router.get('/',keycloak.protect(),function(req, res, next) {
var token=req.headers['authorization'];
console.log(token);
try {
let user = keycloak.jwt.verify(token);
console.log(user.isExpired());
} catch (error) {
console.error(error);
}
})
I know that I lack the knowledge because I am a backend (C#) developer, can somebody help me with this?, thanks in advance.
I found the answer to my problem:
const express = require("express");
const request = require("request");
var keycloakConfig = require('../AuthOnly/config/keycloak-config').keycloakConfig;
const postgraphile = require('./postgraphile');
const app = express();
const keycloakHost = keycloakConfig.serverUrl;
const realmName = keycloakConfig.realm;
// check each request for a valid bearer token
app.use((req, res, next) => {
// assumes bearer token is passed as an authorization header
if (req.headers.authorization) {
// configure the request to your keycloak server
const options = {
method: 'GET',
url: `${keycloakHost}/auth/realms/${realmName}/protocol/openid-connect/userinfo`,
headers: {
// add the token you received to the userinfo request, sent to keycloak
Authorization: req.headers.authorization,
},
};
// send a request to the userinfo endpoint on keycloak
request(options, (error, response, body) => {
if (error) throw new Error(error);
// if the request status isn't "OK", the token is invalid
if (response.statusCode !== 200) {
res.status(401).json({
error: `unauthorized`,
});
}
// the token is valid pass request onto your next function
else {
next();
}
});
} else {
// there is no token, don't process request further
res.status(401).json({
error: `unauthorized`,
});
}});
app.use(postgraphile);
app.listen(8080);
-> this is my server.js. while i am trying to open it by using node server, it is showing cannot get/.
-> as i am trying to open. it is showing cannot get/. I have routed all the links correctly. but still this issue is pertaining me.
-> in my server it is showing mongodb connected and running. but whilw i open it it is showing cannot get/
var express = require('express');
var bodyParser = require('body-parser');
var User = require('./app/models/user');
var app = express();
var jwt = require('jwt-simple') var _ = require('lodash') var bcrypt = require('bcrypt')
app.use(bodyParser.json()); // support json encoded bodies app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
var secretkey = "supersecretkey"
app.post('/session', function(req, res, next) {
console.log(req.body.username);
User.findOne({
username: req.body.username
}).select('password').exec(function(err, user) {
if (err) {
return next(err)
}
if (!user) {
return res.send(401)
}
bcrypt.compare(req.body.password, user.password, function(err, valid) {
if (err) {
return next(err)
}
if (!valid) {
return res.send(401)
} //var test = {username:user.username}; //console.log(user.username); var token = jwt.encode({ username: req.body.username}, secretkey) res.json(token)
})
})
})
app.get('/user', function(req, res, next) {
var token = req.headers['x-auth'] console.log(token)
var auth = jwt.decode(token, secretkey)
//console.log(auth)
User.findOne({ username: auth.username
}, function(err, user)
{ res.json(user) }) })
app.post('/user', function(req, res) {
var user = new User({
username: req.body.username
}) bcrypt.hash(req.body.password, 10, function(err, hash) {
user.password = hash user.save(function(err, user) {
if (err) {
throw next(err)
} // res.send(201) res.json(user); }) })
})
app.listen(3000)
->This is my app.js.
"use-strict"
var express = require('express');
var router = express.Router(); // get an instance of the express Router
var morgan = require('morgan');
var bodyParser = require('body-parser');
var app = express();
/*app.use(morgan('dev'));*/
app.use('/public/app/controllers/routes', router);
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({
extended: true
})); // support encoded bodies
app.use(require('./config/auth'))
app.use('/api/users', require('./app/controllers/api/users'))
app.use('/api/sessions', require('./app/controllers/api/sessions'))
app.use('/api/products', require('./app/controllers/api/products'))
app.use('/api/companies', require('./app/controllers/api/companies'))
app.use('/api/stockists', require('./app/controllers/api/stockists'))
app.use('/api/employees', require('./app/controllers/api/employees'))
app.use('/api/sales', require('./app/controllers/api/sales'))
app.use('/', require('./app/controllers/static'))
var port = process.env.PORT || 3000
var server = app.listen(port, function() {
console.log('App listening at the ', port);
});
require('./websockets').connect(server)
-> as i am trying to open. it is showing cannot get/. I have routed all the links correctly. but still this issue is pertaining me.
-> in my server it is showing mongodb connected and running. but whilw i open it it is showing cannot get/
I have some problems with the express session where I cannot retrieve my session variable that I had stored previously. Below are parts of my codes that I had written.
server.js
let express = require('express'),
path = require('path'),
bodyParser = require('body-parser'),
cors = require('cors'),
config = require('./config/database'),
expressSession = require('express-session'),
uid = require('uid-safe'),
db;
let app = express();
//Import Routes
let auth = require('./routes/auth'),
chimerListing = require('./routes/chimer-listing'),
brandListing = require('./routes/brand-listing');
//Specifies the port number
let port = process.env.PORT || 3000;
// let port = 3000;
// Express session
app.use(expressSession({
secret: "asdasd",
resave: true,
saveUninitialized: false,
cookie: {
maxAge: 36000000,
secure: false
}
}));
//CORS Middleware
app.use(cors());
//Set Static Folder
var distDir = __dirname + "/dist/";
app.use(express.static(distDir));
//Body Parser Middleware
app.use(bodyParser.json());
//MongoDB
let MongoClient = require('mongodb').MongoClient;
MongoClient.connect(config.database, (err, database) => {
if (err) return console.log(err)
db = database;
//Start the server only the connection to database is successful
app.listen(port, () => {
console.log('Server started on port' + port);
});
});
//Make db accessbile to routers;
app.use(function(req, res, next) {
req.db = db;
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.set('Access-Control-Allow-Headers', 'Content-Type');
next();
});
//Routes
app.use('/login', auth);
app.use('/user-listing', userListing);
app.use('/brand-listing', brandListing);
//Index Route
app.get('/', (req, res) => {
res.send('Invalid Endpoint');
});
genuuid = function() {
return uid.sync(18);
};
auth.js
let express = require('express'),
router = express.Router(),
db;
//Login Router for chimer
router.post('/chimer', (req, res, next) => {
db = req.db;
// let client = req.client;
db.collection('chimeUser').find({
Username: req.body.username,
Password: req.body.password
}).toArray().then(function(docs) {
//If there is such user
if (docs.length >= 1) {
req.session.chimerId = docs[0]._id;
console.log(req.session);
req.session.save(function(err) {
// session saved
if (err)
console.log(err)
res.json({
success: true,
chimerId: docs[0]._id
//objects: docs
});
})
} else {
res.json({
success: false,
//objects: docs
})
}
});
});
//Login Router brand
router.post('/brand', (req, res, next) => {
db = req.db;
db.collection('brand').find({
Username: req.body.username,
Password: req.body.password
}).toArray().then(function(docs) {
req.session.brand = docs;
console.log(req.session.brand);
//If there is such user
if (docs.length >= 1) {
res.json({
success: true,
//objects: docs
})
} else {
res.json({
success: false,
//objects: docs
})
}
//db.close()
});
});
});
module.exports = router;
user-listing.js
let express = require('express'),
moment = require('moment'),
router = express.Router(),
// ObjectID = require('mongodb').ObjectID,
db, client;
// let applyListing = require('../models/chimer-listing');
//Retrieve All Listing
router.get('/getAllListing', (req, res, next) => {
db = req.db;
console.log(req.session)
db.collection('listing').find().toArray().then(function(listing) {
//If there is any listing
if (listing.length >= 1) {
res.json({
success: true,
results: listing
})
} else {
res.json({
success: false,
})
}
//db.close()
});
});
module.exports = router;
So in my server.js, I have three routes file which is auth, user-listing, and brand-listing.
Firstly, a user will need to login with the web application which is developed in angular2 and this will trigger the auth route. It will then check for the credentials whether does it exist in the database if it exists I will then assign an ID to req.session.chimerId so that in other routes I will be able to use this chimerId.
Next, after the user has logged in, they will then retrieve an item listing. The problem arises where I can't seem to retrieve the req.session.chimerId that I had previously saved. It will be undefined
NOTE: I tried this using Postman and the browser. In the Postman it works, I am able to retrieve back the req.session.chimerId whereas when I use the angular2 application to hit the endpoints req.session.chimerId is always null
I need a little help figuring out how to get this working -- I've tested and have working JWT authentication and SSL on my '/user' routes. I'm trying to safely allow the user to upload an audio file, also using the JWT and SSL route.
The authentication middleware works, and multer works to let me upload files when I comment out the authentication middleware. However, when I leave the middleware in, the uploaded file is created on my system, but the file fails to upload properly and I get a 404 error.
Thanks for any help!
server.js (main file)
var express = require('express')
, app = express()
, passport = require('passport')
, uploads = require('./config/uploads').uploads
, user_routes = require('./routes/user')
, basic_routes = require('./routes/basic')
, jwt = require('jwt-simple');
// get our request parameters
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Use the passport package in our application
app.use(passport.initialize());
require('./config/passport')(passport);
//double check we have an ssl connection
function ensureSec(req, res, next) {
if (req.headers['x-forwarded-proto'] == 'https') {
return next();
} else {
console.log('NOT SSL PROTECTED! rejected connection.');
res.redirect('https://' + req.headers.host + req.path);
}
}
app.use(ensureSec);
//authenticate all user routes with passport middleware, decode JWT to see
//which user it is and pass it to following routes as req.user
app.use('/user', passport.authenticate('jwt', {session:false}), user_routes.middleware);
//store info on site usage- log with ID if userRoute
app.use('/', basic_routes.engagementMiddleware);
// bundle our user routes
var userRoutes = express.Router();
app.use('/user', userRoutes);
userRoutes.post('/upload', uploads:q, function(req,res){
res.status(204).end("File uploaded.");
});
// Start the server
app.listen(port);
routes/basic_routes.js (tracks engagement middleware)
var db = require('../config/database')
, jwt = require('jwt-simple')
, getIP = require('ipware')().get_ip
, secret = require('../config/secret').secret;
exports.engagementMiddleware = function(req, res, next){
if (typeof(req.user) == 'undefined') req.user = {};
var postData = {};
var ip = getIP(req).clientIp;
var fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
if (req.method=="POST") postData = req.body;
var newEngagement = new db.engagementModel({
user_id: req.user._id,
ipAddress: ip,
url: fullUrl,
action: req.method,
postData: postData
});
//log the engagement
newEngagement.save(function(err) {
if (err) {
console.log('ERROR: engagement middleware db write failed');
next();
}
console.log('LOG: user ' + req.user._id +' from ipAddress: ' + ip + ': ' + req.method + ' ' + fullUrl);
next();
});
next();
}
config/passport.js (passport authentication middleware)
var JwtStrategy = require('passport-jwt').Strategy;
// load up the user model
var db = require('../config/database'); // get db config file
var secret = require('../config/secret').secret;
module.exports = function(passport) {
var opts = {};
opts.secretOrKey = secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
db.userModel.findOne({id: jwt_payload.id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
routes/user_routes.js (user route middleware, user add to header)
var jwt = require('jwt-simple');
var db = require('../config/database');
var secret = require('../config/secret').secret;
//expose decoded userModel entry to further routes at req.user
exports.middleware = function(req, res, next){
var token = getToken(req.headers);
if (token) req.user = jwt.decode(token, secret);
else res.json({success: false, msg: 'unable to decode token'});
//should be unnecessary, double checking- after token verification against db
db.userModel.findOne({email: req.user.email}, function (err, user) {
if( err || !user ) {
console.log('something has gone horribly wrong. Token good, no user in db or access to db.');
return res.status(403).send({success: false, msg: 'unable to find user in db'});
}
});
//end unnecessary bit
next();
}
//helper function
getToken = function (headers) {
if (headers && headers.authorization) {
var parted = headers.authorization.split(' ');
if (parted.length === 2) return parted[1];
else return null;
} else { return null; }
};
config/uploads.js (finally where we try to upload)
var moment = require('moment');
var multer = require('multer');
var jwt = require('jwt-simple');
var uploadFile = multer({dest: "audioUploads/"}).any();
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'audioUploads/')
},
filename: function (req, file, cb) {
cb(null, req.user._id + '_' + moment().format('MMDDYY[_]HHmm') + '.wav')
}
});
exports.uploads = multer({storage:storage}).any();
in your server.js do this:
const authWare = passport.authenticate('jwt', {session:false});
userRoutes.post('/upload', authWare, uploads:q, function(req,res){
res.status(204).end("File uploaded.");
});
works for me!
How can I share a session with Socket.io 1.0 and Express 4.x? I use a Redis Store, but I believe it should not matter. I know I have to use a middleware to look at cookies and fetch session, but don't know how. I searched but could not find any working
var RedisStore = connectRedis(expressSession);
var session = expressSession({
store: new RedisStore({
client: redisClient
}),
secret: mysecret,
saveUninitialized: true,
resave: true
});
app.use(session);
io.use(function(socket, next) {
var handshake = socket.handshake;
if (handshake.headers.cookie) {
var str = handshake.headers.cookie;
next();
} else {
next(new Error('Missing Cookies'));
}
});
The solution is surprisingly simple. It's just not very well documented. It is possible to use the express session middleware as a Socket.IO middleware too with a small adapter like this:
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
Here's a full example with express 4.x, Socket.IO 1.x and Redis:
var express = require("express");
var Server = require("http").Server;
var session = require("express-session");
var RedisStore = require("connect-redis")(session);
var app = express();
var server = Server(app);
var sio = require("socket.io")(server);
var sessionMiddleware = session({
store: new RedisStore({}), // XXX redis server config
secret: "keyboard cat",
});
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res || {}, next);
});
app.use(sessionMiddleware);
app.get("/", function(req, res){
req.session // Session object in a normal request
});
sio.sockets.on("connection", function(socket) {
socket.request.session // Now it's available from Socket.IO sockets too! Win!
});
server.listen(8080);
Just a month and a half ago I dealt with the same problem and afterwards wrote an extensive blog post on this topic which goes together with a fully working demo app hosted on GitHub. The solution relies upon express-session, cookie-parser and connect-redis node modules to tie everything up. It allows you to access and modify sessions from both the REST and Sockets context which is quite useful.
The two crucial parts are middleware setup:
app.use(cookieParser(config.sessionSecret));
app.use(session({
store: redisStore,
key: config.sessionCookieKey,
secret: config.sessionSecret,
resave: true,
saveUninitialized: true
}));
...and SocketIO server setup:
ioServer.use(function (socket, next) {
var parseCookie = cookieParser(config.sessionSecret);
var handshake = socket.request;
parseCookie(handshake, null, function (err, data) {
sessionService.get(handshake, function (err, session) {
if (err)
next(new Error(err.message));
if (!session)
next(new Error("Not authorized"));
handshake.session = session;
next();
});
});
});
They go together with a simple sessionService module I made which allows you to do some basic operations with sessions and that code looks like this:
var config = require('../config');
var redisClient = null;
var redisStore = null;
var self = module.exports = {
initializeRedis: function (client, store) {
redisClient = client;
redisStore = store;
},
getSessionId: function (handshake) {
return handshake.signedCookies[config.sessionCookieKey];
},
get: function (handshake, callback) {
var sessionId = self.getSessionId(handshake);
self.getSessionBySessionID(sessionId, function (err, session) {
if (err) callback(err);
if (callback != undefined)
callback(null, session);
});
},
getSessionBySessionID: function (sessionId, callback) {
redisStore.load(sessionId, function (err, session) {
if (err) callback(err);
if (callback != undefined)
callback(null, session);
});
},
getUserName: function (handshake, callback) {
self.get(handshake, function (err, session) {
if (err) callback(err);
if (session)
callback(null, session.userName);
else
callback(null);
});
},
updateSession: function (session, callback) {
try {
session.reload(function () {
session.touch().save();
callback(null, session);
});
}
catch (err) {
callback(err);
}
},
setSessionProperty: function (session, propertyName, propertyValue, callback) {
session[propertyName] = propertyValue;
self.updateSession(session, callback);
}
};
Since there is more code to the whole thing than this (like initializing modules, working with sockets and REST calls on both the client and the server side), I won't be pasting all the code here, you can view it on the GitHub and you can do whatever you want with it.
express-socket.io-session
is a ready-made solution for your problem. Normally the session created at socket.io end has different sid than the ones created in express.js
Before knowing that fact, when I was working through it to find the solution, I found something a bit weird. The sessions created from express.js instance were accessible at the socket.io end, but the same was not possible for the opposite. And soon I came to know that I have to work my way through managing sid to resolve that problem. But, there was already a package written to tackle such issue. It's well documented and gets the job done. Hope it helps
Using Bradley Lederholz's answer, this is how I made it work for myself. Please refer to Bradley Lederholz's answer, for more explanation.
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io');
var cookieParse = require('cookie-parser')();
var passport = require('passport');
var passportInit = passport.initialize();
var passportSession = passport.session();
var session = require('express-session');
var mongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
var sessionMiddleware = session({
secret: 'some secret',
key: 'express.sid',
resave: true,
httpOnly: true,
secure: true,
ephemeral: true,
saveUninitialized: true,
cookie: {},
store:new mongoStore({
mongooseConnection: mongoose.connection,
db: 'mydb'
});
});
app.use(sessionMiddleware);
io = io(server);
io.use(function(socket, next){
socket.client.request.originalUrl = socket.client.request.url;
cookieParse(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
socket.client.request.originalUrl = socket.client.request.url;
sessionMiddleware(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
passportInit(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
passportSession(socket.client.request, socket.client.request.res, next);
});
io.on('connection', function(socket){
...
});
...
server.listen(8000);
Working Example for PostgreSQL & Solving the problem of getting "an object with empty session info and only cookies":
Server-Side (Node.js + PostgreSQL):
const express = require("express");
const Server = require("http").Server;
const session = require("express-session");
const pg = require('pg');
const expressSession = require('express-session');
const pgSession = require('connect-pg-simple')(expressSession);
const PORT = process.env.PORT || 5000;
const pgPool = new pg.Pool({
user : 'user',
password : 'pass',
database : 'DB',
host : '127.0.0.1',
connectionTimeoutMillis : 5000,
idleTimeoutMillis : 30000
});
const app = express();
var ioServer = require('http').createServer(app);
var io = require('socket.io')(ioServer);
var sessionMiddleware = session({
store: new RedisStore({}), // XXX redis server config
secret: "keyboard cat",
});
io.use(function(socket, next) {
session(socket.request, {}, next);
});
app.use(session);
io.on("connection", socket => {
const ioSession = socket.request.session;
socket.on('userJoined', (data) => {
console.log('---ioSession---', ioSession)
}
}
Client-Side (react-native app):
To solve the problem of getting "empty session object" you need to add withCredentials: true
this.socket = io(`http://${ip}:5000`, {
withCredentials: true,
});
I have kinda solved it, but it is not perfect. Does not support signed cookies etc. I used express-session 's getcookie function. The modified function is as follows:
io.use(function(socket, next) {
var cookie = require("cookie");
var signature = require('cookie-signature');
var debug = function() {};
var deprecate = function() {};
function getcookie(req, name, secret) {
var header = req.headers.cookie;
var raw;
var val;
// read from cookie header
if (header) {
var cookies = cookie.parse(header);
raw = cookies[name];
if (raw) {
if (raw.substr(0, 2) === 's:') {
val = signature.unsign(raw.slice(2), secret);
if (val === false) {
debug('cookie signature invalid');
val = undefined;
}
} else {
debug('cookie unsigned')
}
}
}
// back-compat read from cookieParser() signedCookies data
if (!val && req.signedCookies) {
val = req.signedCookies[name];
if (val) {
deprecate('cookie should be available in req.headers.cookie');
}
}
// back-compat read from cookieParser() cookies data
if (!val && req.cookies) {
raw = req.cookies[name];
if (raw) {
if (raw.substr(0, 2) === 's:') {
val = signature.unsign(raw.slice(2), secret);
if (val) {
deprecate('cookie should be available in req.headers.cookie');
}
if (val === false) {
debug('cookie signature invalid');
val = undefined;
}
} else {
debug('cookie unsigned')
}
}
}
return val;
}
var handshake = socket.handshake;
if (handshake.headers.cookie) {
var req = {};
req.headers = {};
req.headers.cookie = handshake.headers.cookie;
var sessionId = getcookie(req, "connect.sid", mysecret);
console.log(sessionId);
myStore.get(sessionId, function(err, sess) {
console.log(err);
console.log(sess);
if (!sess) {
next(new Error("No session"));
} else {
console.log(sess);
socket.session = sess;
next();
}
});
} else {
next(new Error("Not even a cookie found"));
}
});
// Session backend config
var RedisStore = connectRedis(expressSession);
var myStore = new RedisStore({
client: redisClient
});
var session = expressSession({
store: myStore,
secret: mysecret,
saveUninitialized: true,
resave: true
});
app.use(session);
Now, the original accepted answer doesn't work for me either. Same as #Rahil051, I used express-socket.io-session module, and it still works. This module uses cookie-parser, to parse session id before entering express-session middleware.
I think it's silmiar to #pootzko, #Mustafa and #Kosar's answer.
I'm using these modules:
"dependencies":
{
"debug": "^2.6.1",
"express": "^4.14.1",
"express-session": "^1.15.1",
"express-socket.io-session": "^1.3.2
"socket.io": "^1.7.3"
}
check out the data in socket.handshake:
const debug = require('debug')('ws');
const sharedsession = require('express-socket.io-session');
module.exports = (server, session) => {
const io = require('socket.io').listen(server);
let connections = [];
io.use(sharedsession(session, {
autoSave: true,
}));
io.use(function (socket, next) {
debug('check handshake %s', JSON.stringify(socket.handshake, null, 2));
debug('check headers %s', JSON.stringify(socket.request.headers));
debug('check socket.id %s', JSON.stringify(socket.id));
next();
});
io.sockets.on('connection', (socket) => {
connections.push(socket);
});
};