I am using socket.io with express and using express session and express-socket.io-session, but I can't can't access properties of the express session in the socket.io session object and vice versa.
const server = require("http").createServer(app);
const client = require("socket.io").listen(server);
session = require("express-session")({
secret: "my-secret",
resave: true,
saveUninitialized: true
}),
sharedsession = require("express-socket.io-session");
app.use(session);
client.use(sharedsession(session, {
autoSave:true
}));
client.on("connection", (socket) => {
socket.on("input", data => {
console.log(socket.handshake.session.user)
socket.handshake.session.name = "bar"
socket.handshake.session.save()
})
})
app.post("/signup", (req, res, next) => {
req.session.user = "foo";
})
app.get("/test", (req, res, next) => {
console.log(req.session.name)
})
Both console.log() return undefined, as it seems like they both are two different objects.
I got my issue resolved, but can't seem to understand this weird issue when using var socket = io('http://localhost:8080') in the client HTML sock.io session id is differing from express session id, but when I do var socket = io() they both share the same session, and everything is working as expected.
Related
My Question is how can i attach the user id and the Socket id to access it like pair key = value
When a user is login to my website i'm using mongodbStore with sessions to detect the user id, saving data and sending data related to the current user id but lately i want to apply notifications for my website so when a user create a post i will save the post in db and save also the publisher id in the same mongo document i want when a user like the post the i will receive the session id of the liked user i want to send the notification to the post publisher that there's a user liked your post now I'm sending the publisher id with the socket event and compare it in the client side and when id is matching the current user the notification will be displayed but that way I'm sending the notification to all connected users i want to send it just to the publisher how can i do that from a different router here's my code
const cors = require("cors");
const express = require("express");
const session = require("express-session");
const MongoDBStore = require("express-mongodb-session")(session);
require("./mongoose");
const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
const users = require("./routers/users");
const posts = require("./routers/posts");
io.on("connection", async (socket) => {
console.log("a user connected");
socket.on("connect_error", (err) => {
console.log(`connect_error due to ${err.message}`);
});
});
io.on("disconnect", function() {
console.log("User Disconnected")
})
//Here's i added the socket io to the request to access it from another router
app.use(function (req, res, next) {
req.io = io;
next();
});
const store = new MongoDBStore({
uri: process.env.DATABASE_URL,
collection: "mySessions",
});
app.use(
session({
store,
name: "sid",
saveUninitialized: false,
resave: false,
secret: `it is a secret!`,
cookie: {
maxAge: 1000 * 60 * 60,
sameSite: true,
},
})
);
app.use(users);
app.use(posts)
app.listen(3000)
On the other side in posts router
const express = require("express");
const Posts = require("../db/posts");
const Users = require("../db/users");
const router = express.Router();
//Url like that http://locahost:8000/posts/likes?id=1346
router.post("/posts/likes", async function(req, res) {
const post = await Posts.findById(req.params.id)
const publusher = post.publisherID
const liker = req.session.userId;
const likerName = await Users.findById(liker).select("username");
req.io.emit("like", {
receiver: publusher,
Message: `${likerName.username} liked your post`,
sender: liker
});
});
export default router
on the client side i created an api to send the userId to the client side and i'm handling it like that
const socket = io();
let usr;
$.get("/myId").done(data => usr = data.id)
socket.on("like", function (data) {
if(data.receiver === usr) {
console.log(data.Message)
}
})
I want to emit like event just for the publisher like
req.io.to(req.io.sockets[publusher]).emit("like", {message: "done!"})
Can anyone help me to access the publisher socket id from the sessions i checked also how i can wrap socket to sessions https://socket.io/how-to/use-with-express-session but i can't figure out how it works
I am trying to save this variable called server from the user. After they submit, they go to another page. On that page, I verify their server exists and if not send them back.
The session is saved when I run the post request because I immediately check via the get request if it exists (via client-side). However, if I refresh my page, the session is no longer defined and the user is sent back home (verified this with the logs). I think it is something cookie related, but I could not seem to get it to work.
Here is my sesssions.js route:
const session = require('express-session');
const express = require("express");
const router = express.Router();
router.use(session({
secret: 'ssshhhhh',
resave: false,
saveUninitialized: true,
cookie: { secure: false }
}));
router.get('/check', (req, res) => {
let sesh = req.session;
console.log("Checking server exists: " + sesh.server);
res.send(sesh.server);
});
router.post('/login', (req, res) => {
let sesh = req.session;
sesh.server = req.body.server;
console.log("Setting server: " + sesh.server);
res.send(sesh.server);
});
module.exports = router;
And this is how are the requests which both return promises:
function setSession(value) {
return $.post("/sessions/login", { 'server': value }).then();
}
function getSession() {
return $.get("/sessions/check").then();
}
Simple solution from a friend. You cannot instantiate middleware in a route. Just had to move the middleware instantiation to app.js.
I am writing a nodejs backend application for an application I developed with Vue. I need to create sessions for users' db operations and select databases according to the user. I wanted to use express-session for this, but the sessions I created are seen as undefined in different requests. How can I overcome this problem? I use history mode on Vue so my requests must go through the router for now. Also I can convey that I am open to alternative suggestions.
const express = require('express')
const app = express()
const productRequest = require("./controllers/products/products")
const customerRequest = require("./controllers/customers/customers")
const orderRequest = require("./controllers/orders/orders")
const controllerRequest = require("./controllers/controllers")
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');
var session = require('express-session')
const cors = require("cors")
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
app.use(express.json());
app.use(cors());
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.use('/products', productRequest)
app.use('/orders', orderRequest)
app.use('/customers', customerRequest)
app.use('/controllers', controllerRequest)
module.exports = app;
const express = require('express');
const controllers = express.Router();
const login = require('./login.js')
const userkey = require("./userkey");
controllers.post("/login", (req, res, next) => {
login(req.body, response => {
if (response[0].total == 0) {
res.status(204).json({
message: "Fail",
result: null
})
} else if (response[0].total == 1) {
/* SESSION SETTED HERE */
req.session.corp = response[0].corp
console.log(req.session.corp)
/* HERE SEEMS CREATED */
res.status(200).json({
message: "Connected",
result: response
})
}
})
});
controllers.post("/userkey", (req, res, next) => {
/* CANT USE HERE response UNDEFINED*/
console.log(req.session.corp)
userkey([req.body, req.session.corp], response => {
res.status(200).json({
data: response
})
})
});
module.exports = controllers;
I had some massive headaches with sessions and would have kept going until I read
Also I can convey that I am open to alternative suggestions.
Please then consider using JWT
as simple as this :
var jwt = require('jsonwebtoken');
var token = jwt.sign({ foo: 'bar' }, 'shhhhh');
The jsonwebtoken package is simple to use and should match your needs.
There are many questions relating to getting a req.user undefined after social authentication, but I found none which could help me.
I have been able to successfully use the example for using passport for twitter authentication: https://github.com/passport/express-4.x-twitter-example. I tried to follow this pattern as closely as possible but cannot seem to get it to work.
Specifically, I am able to successfully authenticate, but the req.user is undefined. This makes no sense to me as my user data was returned no problem from the example.
I'm not inclined to believe this is a middleware problem (as it has been for others) as the middleware is the same as that used in the example. It could be something about having multiple domains, but I'm not sure what. All of this is being done on the localhost.
In Twitter, the app is set up so that
website is: 127.0.0.1:3000/signin
and the
callback url is: 127.0.0.1:2999/auth/twitter/return
As you can tell, my client is working on port 3000 and it is making calls to a server running on port 2999.
To briefly walk you through the code, the client on 127.0.0.1:3000/signin has a button which links to 127.0.0.1:2999/auth/twitter, thus initiating the authentication request. Under the hood, the express server is created in server/index.js--server. This imports the routes in routes/index.js, some of which the controller authenticate.js handles. As you can see, the oauth twitter request is made in authenticate.js. Again, authentication proceeds successfully, I am redirected to 127.0.0.1:3000/search. However, as you can see in this.twitter_callback, I am printing the req.user and it is undefined.
Please note that I have redacted the consumer key/secret from my code.
server/index.js
var cors = require('cors')
var bodyParser = require('body-parser')
var express = require('express');
var app = express();
var http = require('http').Server(app)
var io = require('socket.io')(http)
// NOT SURE WHY I NEED TO GO BACK 3 FOLDERS TO GET TO PORT_CONFIG
var port = require("../port_config.json").server_port;
var PORT = Number(process.env.PORT || port);
var routes = require('./routes/index.js')
var database = require('./database/db.js')
var db = new database()
app.use(cors()); // middleware that allows cross-platform requests
app.use(bodyParser.json());
db.dbConnect(function(err,db_instance){
// routes
routes(app, db_instance, io)
// send user polls on connection
// TEMPORARY (WILL BE GRABBED ON LOGIN)
var user = null // WILL BE SET AFTER LOGIN
io.on('connection', function(socket) {
var places_attending = db_instance.collection('places_attending')
places_attending.find({}).toArray(function(err,docs){
var places_user_attending = docs.map(doc => {
if (doc.attending.indexOf(user) !== -1) {
return {
id: doc.id,
name: doc.name,
num_attending: doc.attending.length
}
}
})
socket.emit('places_user_attending', places_user_attending);
})
})
})
http.listen(PORT, function () {
console.log('Backend server listening at http://localhost:' + PORT);
})
module.exports = http
routes/index.js
var Search = require('../controllers/search.js')
var Add = require('../controllers/add.js')
var Authenticate = require('../controllers/authenticate.js')
module.exports = function(app, db, io) {
var search = new Search(db, io)
var add = new Add(db, io)
var authenticate = new Authenticate(app)
app.route('/api/search')
.post(search.search_yelp)
app.route('/api/add')
.post(add.add_attendee)
app.route('/auth/twitter')
.get(authenticate.twitter_authentication)
app.route('/auth/twitter/return')
.get(authenticate.twitter_callback)
}
authenticate.js
function authenticate(app) {
var passport = require('passport');
var Strategy = require('passport-twitter').Strategy;
// Configure the Twitter strategy for use by Passport.
passport.use(new Strategy({
consumerKey: REDACTED,
consumerSecret: REDACTED,
callbackURL: 'http://127.0.0.1:2999/auth/twitter/return'
},
function(token, tokenSecret, profile, cb) {
// In this example, the user's Twitter profile is supplied as the user
// record. In a production-quality application, the Twitter profile should
// be associated with a user record in the application's database, which
// allows for account linking and authentication with other identity
// providers.
return cb(null, profile);
}));
// Configure Passport authenticated session persistence.
passport.serializeUser(function(user, cb) {
cb(null, user);
});
passport.deserializeUser(function(obj, cb) {
cb(null, obj);
});
// Use application-level middleware for common functionality, including
// logging, parsing, and session handling.
app.use(require('morgan')('combined'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true }));
// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.use(passport.session());
this.twitter_authentication = passport.authenticate('twitter')
this.twitter_callback = (
passport.authenticate('twitter', { failureRedirect: 'http://127.0.0.1:3000/signin' }),
function(req, res) {
console.log('REQ.USER OBJECT: ' + req.user)
res.redirect('http://127.0.0.1:3000/search');
}
)
}
module.exports = authenticate
Any help would be greatly, greatly appreciated.
The problem was in how my twitter_callback route was specified.
If I change the callback to this:
this.twitter_callback = app.get('/auth/twitter/return',
passport.authenticate('twitter', { failureRedirect: 'http://127.0.0.1:3000/signin' }),
function(req, res) {
console.log(req.user)
res.redirect('http://127.0.0.1:3000/search');
})
everything works fine. I think this has something to do with the middleware not being applied correctly the initial way I wrote it. Not exactly sure how I would rewrite it to export it, without using app.get in the twitter_callback though
This is my first time using Express' app.all(). When a user signs up through an outside oAuth provider, I still need them to provide an email after returning to the site. I'm basically setting them as inactive in the database and checking for req.session.active.
What I'm doing is
app.all('*', function(req, res, next) {
if(!req.session.active) {
if(req.path == '/complete_signup') {
next();
} else {
return res.redirect('/complete_signup');
}
}
});
But this doesn't seem to be working. How can I correctly check if the user is already redirected?
If you can suggest a method other than app.all(), that would work, too.
EDIT:
On second look, this is working, but none of the external resources (stylesheets, javascripts, etc.) seem to be loading since they don't match req.path.
You can use the express-redirect-loop middleware (which uses sessions since HTTP Referrer header is unreliable). This will only work for requests that support cookie storage/jar (e.g. browser).
const express = require('express');
const session = require('express-session');
const redirectLoop = require('express-redirect-loop');
const app = express();
app.use(
session({
secret: 'test',
resave: false,
saveUninitialized: true
})
);
app.use(redirectLoop());
app.get('/', (req, res) => res.sendStatus(200));
app.get('/bar', (req, res) => res.redirect('/foo'));
app.get('/foo', (req, res) => res.redirect('/foo'));
app.get('/baz', (req, res) => res.redirect('/bar'));
app.listen(3000);