"req.session.user = user" where user is not defined - node.js

I had read almost every possible threads, post, questions, I got on setting session cookies, still didn't get the perfect solution.
I had created a web application where I want to set sessions cookies who had logged in.
Not able to figure out why the "user" is undefined. Where did I lack? I had did every trail and error to figure out the error.
App.js file
const session = require('client-sessions');
app.use(cookieParser());
app.use(session({
cookieName: 'sessioncookie',
secret: 'long_string_which_is_hard_to_crack',
duration: 30 * 60 * 1000,
activeDuration: 5 * 60 * 1000,
}));
Routes.js file
router.post('/login', (req, res) => {
const emaild = req.body.email;
const passd = req.body.pass;
db.collection('check').findOne({email: emaild}, (err, result) => {
if (err) return console.log(err);
console.log(result.passcode);
console.log('Retrived from database');
bcrypt.compare(passd, result.passcode, function(err, boolean) {
console.log(boolean);
if(boolean == true) {
req.session.user = user; //// It's Undefined
res.redirect('/dashboard');
} else {
res.send('<h1>Wrong Password</h1>');
}
});
});
});

When you query your collection with findOne method it will return you the related document ( here user which you name it result ) and you should name it with a meaningful parameter like user and then user becames available within this callback function.
Also when you get the following error on req.session.user = user;:
Cannot set property 'user' of undefined
It means that req.session is undefined. Using express, setting session on request is doing by session middlewares. here you are using client-sessions which attachs session by cookieName option value you pass it at initialization. so you should set it like the following:
req.sessioncookie.user = user;
Note that middleware ordering does matters. you should configure you application using middlewares before setting up your routes. this way every incoming request is populated with desired configuration you have been set up.

Related

How can I know if a user has disabled cookies on my NodeJS Server?

I am writing a NodeJS middleware which creates a user object and saves its ID in a cookie. Before it does this, to ensure that I don't create duplicate user objects, I check to see if a that cookie has already been set. If so, I move on without creating a user object. The problem is when users have disabled cookies. Now my middleware will create a new duplicate user every time it is fired causing spam in my database. How can I prevent this?
Also if I specify secure: true, when using my app on mobile (ios) it also keeps creating duplicate users. I do not have an SSL certificate on my site so that could be the reason? Any help would be appreciated.
async function setUser(req, res, next) {
if (!req.cookies?.user_jwt) {
try {
const newUser = User();
const savedUser = await newUser.save();
const token = jwt.sign(
{id: savedUser._id},
process.env.JWT_ACCESS_SEC,
{expiresIn: "3650d"}
);
await res.cookie("user_jwt", token, {
secure: false,
httpOnly: true,
expires: new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000),
});
}
catch (err) {
console.log("error getting user")
return next();
}
}
return next();
}
The only way you can really test is setting a cookie, let the user make another request and see if the cookie is still there.

Cookies persist in supertest/superagent test, but the user doesn't stay logged in

My Goal
I'm trying to use supertest's agent function in a jest beforeEach() to login the user before each test, as I want each test to run under the assumption that the user is signed in. For authentication, I am using passport and passport-local.
This is what I tried (with parts cut out for brevity):
Test file:
import { agent, SuperAgentTest } from 'supertest';
import app from '../../src/app';
// create a `testRequest` variable to use in the tests
// that will be refreshed in between
let testRequest: SuperAgentTest;
const fakeUser = { email: 'john#john', username: 'john', password: 'john' };
beforeEach(async () => {
// create new agent
testRequest = agent(app);
// register and login
await testRequest.post('/register').send(fakeUser).expect(302);
// other irrelevant stuff...
});
// protected route
describe('POST /campgrounds/new', () => {
it('returns 200 OK', () => {
return testRequest.get('/campgrounds/new');
})
});
/register route:
router.post('/register', async (req, res) => {
const { password, ...details } = req.body;
try {
// I am using passport-local-mongoose for this function-
// it just registers the user
const user = await User.register(new User(details), password);
req.login(user, (err) => {
// error handling and redirect
});
} catch (e) {
// error handling
}
})
This is my result
Instead of a 200 status, I get a 302 status, meaning I was redirected to the login page. To debug this, I created a test route called /current which will log the current user and session ID cookie. I then sent a GET request to this route in both the it and beforeEach function respectively.
Interestingly, they both logged the same session ID, but only the request in beforeEach had a user object attached to the request.
#1 Ensure body parser correct order
Make sure you have this before any routes or auth-related things.
app.use(express.json())
#2 Check Passport Middleware Wire-up
Ensure you call app.use(passport.initialize()) & app.use(passport.session()) before any app.use('/', aRouter), router.get, router.post, etc:
// Set up session w/ specific config
app.use(session({
secret: 'bquyqueajhbd',
resave: true,
saveUninitialized: true,
store: new FileStore({path: '/tmp/session'})
}));
// Wire up the
app.use(passport.initialize())
app.use(passport.session())
EDIT: Notes on req.user
Passport is designed to store the user ID in session.
Every request to the server must reload the user from the database.
This is the job of the middleware passport.initialize() and passport.session().
The logic there will call passport.deserializeUser to lookup the user by ID - the same ID that was saved upon login into the session by passport.serializeUser.
passport.serializeUser(function(user, done) {
done(null, user.id); // <-- Here's where the ID is saved to session.
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user); // <-- Here is where the `req.user` get's it's value from.
});
});
To debug this I'd focus on the passport.deserializeUser callback, add logs before and after the DB query.
(Note: it's been a few years since I taught this. Appologies if I'm not using the precise terms, etc.)

nodejs - Share session between express and socket.io (authentication with passport)

I am currently creating a webapplication using node.js.
When a user enters the side, a connection via socket.io is built up.
I want to block multiple connections. A user should just be able to connect one time, if he opens the page in another window, i want to display a message like "already connected, please close all other tabs", or something like that.
To do so, i create sessions for the users using "express-session". this sessions are shared with socket.io by using following code.
I am not sure, if i am doing it correctly. I have looked at some tutorials and they are all sharing the sessions in a similar way. But sometimes, i think, the session data is overridden, when i change and save a value in the socket.io-session.
var sessionStoreClient = Redis.createClient({
port: DBs.sessionStore_redis.port,
host: DBs.sessionStore_redis.host,
password: DBs.sessionStore_redis.password,
db: DBs.sessionStore_redis.database
});
var sessionStore = new RedisStore({
unset: "destroy",
client: sessionStoreClient
});
var sessionInstance = expressSession(
{
key: sessionsKey,
store: sessionStore,
secret: sessionsSecret,
unset: "destroy"
}
);
app.use(sessionInstance);
app.use(passport.initialize());
app.use(passport.session());
to share the sessions with socket.io, i am using following code
var passportSocketIo = function(app,sessionInstance, passport){
return function(socket, next){
var req = socket.request;
sessionInstance(req,req.res,
function () {
if (!req.session) {
return next(new Error("Not authorized"), false);
}
req.updateSessionValue = function (propertyName, propertyValue, callback) {
if(!req.session){
console.log("unauthorized session modification");
return;
}
req.session.reload(()=>{
req.session[propertyName] = propertyValue;
req.session.save();
if(callback)
callback(propertyName, propertyValue,req.session);
});
};
if(req.session.passport && req.session.passport.user) {
passport.deserializeUser(req.session.passport.user, req, function (err, user) {
req.user = user;
req.user.logged_in = true;
return next(null, true);
});
}else {
return next(null, true);
}
}
);
};
};
var nsp = io.of(Packages.NAMESPACES.LOBBY);
nsp .use(options.sessionMiddleware);
to edit the values of the sessions, inside of the socket.io-listeners, i use the function, which is set to the request in the code above.
_onConnectionReceived(socket) {
socket.request.updateSessionValue("isConnected",true);
}
and when the user disconnects from the socket:
_onDisconnect(socket) {
socket.request.updateSessionValue("isConnected",false);
}
For login and authentication I am currently using passport.js.
When the user logs out, he is redirected to the samepage, but as "guest"-user. For loggin out, I am using following code:
router.get('/logout',function(req, res,next){
req.logout();
req.session.destroy(
function (err) {
res.render('logout',{});
}
);
});
The problem there is, that after the session is destroyed and the user is redirected, the socket.io disconnect event is fired. Even after the reload inside of the "onDisconnected" event-listener, the session seems to exist, after the value is modified, the session seems to get recreated somehow. Also it feels like, that the session values are sometimes just overwritten - i have not exactly found out why.
Can anyone explain what i am doing wrong, or if there is a better way to share and modify the sessions? Is the "reload" function of the session really reloading its data from the sessionStore?
Another approach could be to just save the session/user ids in a hashset, and check if there is already a connection open (without saving it in the session it self), but i am curious, if my approach is correct, becaus i need the sharing and modifing of the sessions anyways.

express-session: Why is redirect executed before my session info is set?

Yet another "I've just started learning node.js/express and now I stuck" person here. I've tried looking everywhere for answers but I've reached a dead end. This is my first Stackoverflow question, so please tell me if I have done something wrong or unconventional.
I'm trying to create a POST request that saves a User-object to an express-session (on MongoDB), and redirects you to a URL that handles your session information.
The problem is, that the user is redirected before the header is set, and I am given the following:
Error: Can't set headers after they are sent.
Here is my code. I know it's a lot.. Im sorry.
router.post()
// Handler for POST requests from root page (/)
router.post('/', function(req, res) {
console.log("Router: A POST request for: \"/\" has been called!");
var username = req.body.username;
var password = req.body.password;
// Connect to database
mongoose.connect(dbAddress);
var db = mongoose.connection;
// Report if error in connection
db.on('error', console.error.bind(console, 'Database: Connection error..'));
// Callback function on 'open'
db.once('open', function() {
console.log("Database: Connection successful!");
console.log("Database: User supplied username: " + username);
console.log("Database: User supplied password: " + password);
// Find User object with a userId of req's username
User.findOne({ 'userId' : username.toUpperCase() }, function(err, userObj) {
if (err)
return console.err(err);
// Disconnect when done retrieving user object
mongoose.disconnect();
if ( userObj ) {
console.log("Database: Returned password from MongoDB:");
console.log(userObj.password);
var db_password = userObj.password;
if (password === db_password) {
console.log("Database: User Authenticated");
// Set 'req.session.user' as cookie
req.session.user = userObj;
// Redirect to homepage, with new cookie set
res.redirect('/');
} else { // If passwords don't match
res.render('index', {
showError: true
});
}
} else { // If userObj is null
res.render('index', {
showError: true
});
}
});
});
});
Note the 'req.session.user = userObj' part. Here I am trying to set 'user' in the session to the user object retrieved from MongoDB. In the next line, I am redirecting the user back to the GET request handler for '/' which handles the user based on the session information.
For some reason, these aren't happening in order. The GET request handler doesn't find req.session.user.
I understand that node.js is all about asynchronisation, but from other examples I've found online, this is supposed to work. What am I missing here?
You could put your redirect inside a callback after the session is saved e.g:
...
// Set 'req.session.user' as cookie
req.session.user = userObj;
req.session.save(function(err) {
// session saved
res.redirect('/')
})
...
Hopefully this will make sure that the user is only redirected after the session is saved.
Note: Make sure you hash your password with something like Bcrypt or pbkdf2.

req.session has no method 'touch'?

I'm trying to use express-session with connect-redis to store user sessions. Currently, after a user logs in, I return back req.sessionID and then whenever the user wants to make a request to a secure part of the api, he/she has to provide the session ID. The authentication middleware then goes back into redis and checks to see if the session exists and if so, replace the current session with the one stored in Redis.
function isLoggedIn(req, res, next){
var session_id = req.body.session_id;
if (!session_id){
res.send(401, {status: 0, message: "Not authorized"});
return;
}
console.log(req);
sessionStore.get(session_id, function(err, session){
if(err){
res.send(401, {status: 0, message: "Not authorized"});
return;
}
req.session = session;
req.sessionID = req.body.session_id;
return next();
});
}
But for some reason, this error shows up:
/node_modules/express-session/index.js:269
req.session.touch();
^
TypeError: Object #<Object> has no method 'touch'
I can't seem to find anyone else online that has this error because touch() is a built in functionality to express-session. Please help? My express-session version is 1.9.3.
I was having the same error. It seems that if you're coming from express cookie sessions, it was possible to set req.session = {/* some arbitrary session object */}. Obviously, req.session has some methods on the instance that express needs.
So, just make sure you're not explicitly overriding req.session anywhere in your code.
Try this:
req.session.user = { 'id': 123 };
req.session.pageviews = 1; // This too
Font: https://davidburgos.blog/expressjs-session-error-req-session-touch-not-function/

Resources