I am using nodejs, express and connect-memcaced to handle my sessions. I've made a login script which executes perfectly every time, sets the session data and marks the user as logged in and than redirects him back to the page he logged in from. What happens on occasion (not always) is that after he is redirected and the page loads the cookies sessionID changes and the user is off course not logged in anymore. I have failed finding the reason why this happens and why it doesn't happen every time.
Code snipet of the login function:
DB.getOne('User',{filters:{'primary':{email:req.body.email}}}, function(err,data){
if(data[0] && data[0].active == 1){
var encodedPass = self.encodePass(req.body.pass,req.body.email);
if(encodedPass == data[0].pass){
req.session.pr.user = data[0];
req.session.pr.user.status = true;
res.writeHead(302, {
'Location': goTo
});
res.end();
}
}
});
Looking directly into memcached I can see that this executes perfectly and the data is always saved into memcached under the original sessionID. For some reason the redirect must be changing the sessionID
I often find that this happens to me when I do not notice that I have restarted the development server. Remember that the session cookies are stored in memory, so when the server is restarted, all the sessions will be forgotten and the user will be assigned a new session automatically.
Related
I'm using express-ntlm to get the current user's windows ID in an intranet setting. It works fine most of the time, but occasionally it will return the ID of a completely different person. I'm guessing this is something to do with sessions maybe?
const ntlm = require('express-ntlm');
module.exports = app => {
app.use(
ntlm({
debug: function() {
var args = Array.prototype.slice.apply(arguments);
console.log.apply(null, args);
},
domain: 'MS',
domaincontroller: 'ldap://something.com'
})
);
app.post('/get-user-details/', (req, res) => {
console.log(req.ntlm.UserName); //Returns correct user most of the time, but sometimes it returns different person who open site at the same time
});
Unfortunately NTLM authenticates connections, not sessions. Which was fine in the past, but doesn't make sense anymore, since browser tend to open multiple connections at once to speed up page loading and reverse proxies are sharing connections to the backend. That's where the problem is: Your reverse proxy will reuse already authenticated connections to the backend, and therefore mix up users. To mitigate this issue, you have to make sure your reverse proxy has NTLM support enabled.
There is still an open pull request for express-ntlm that adds a Keep-Alive property which might solve this issue, unfortunately it's widely untested and first needs to be verified.
I've recently written a session management plugin in node.js for Muneem web framework. Here is pseudo code to create a new session;
function createSession(){
// read encrypted session-id from the request
if( sessionId ){
// decrypt it
if (decryptedSessionId ) {
//read session detail from the store
options.store.get(decryptedSessionId, (err, sessionFromStore) => {
if(err){
throw Error(err);
}else if( sessionFromStore){
if( shouldRenew(sessionFromStore) ){
//delete previous session
options.store.destroy(sessionFromStore.id, err=> {
//update the session object in memory
});
}
}else{ //session detail is not present in store
// create new session
}
});
} else { //invalid or tempered session
// throw error
}
}else{ //session-id is not presnet in request
// create new session
}
}
As you can notice, I'm renewing a session when it is valid and satisfy certain conditions by deleting the previous session. But I don't update it in the store immediately. Instead, I update the session information in the store and set the cookies when the response is being sent to the client.
Now suppose a condition, when the server receives multiple requests with the same session-id which is eligible to renew. I renew the session on the first request.
Scenarios
Session is not updated in store. So I'll renew the previous session with another new session id. A user will have multiples session-id in this case.
Session is updated in the store. Now, the previous session will not be available in the store. I'll have to ask the user to login again if it is authorized session. Or I'll create another session.
How to handle this race condition?
Here is the full code, in case we need.
This situation seems not to occur if we authenticate the main route only.
I was previously thinking to authenticate every request. A server can receive multiple requests with the same session ID only on page load which includes access to the main route along with static files like CSS, js, etc. Static files are not required to be authenticated. Hence race condition should not occur.
I want to emit an event to the client when a long fucntion comes to an end.
This will show a hidden div with a link - on the client side.
This is the approach i tested:
//server.js
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
require('./app/routes.js')(app, io);
//routes.js
app.post('/pst', function(req, res) {
var url = req.body.convo;
res.render('processing.ejs');
myAsyncFunction(url).then(result => {
console.log('Async Function completed');
socket.emit('dlReady', { description: 'Your file is ready!'});
//do some other stuff here
}).catch(err => {
console.log(err);
res.render('error.ejs');
})
});
I get this
ERROR: ReferenceError: socket is not defined
If i change the socket.emit() line to this:
io.on('connection', function(socket) {
socket.emit('dlReady', { description: 'Your file is ready!'});
});
Then i don't receive an error, but nothing happens at the client.
This is the client code:
<script>
document.querySelector('.container2').style.display = "none";
var socket = io();
socket.on('dlReady', function(data) { //When you receive dlReady event from socket.io, show the link part
document.querySelector('.container1').style.display = "none";
document.querySelector('.container2').style.display = "block";
});
</script>
This whole concept is likely a bit flawed. Let me state some facts about this environment that you must fully understand before you can follow what needs to happen:
When the browser does a POST, there's an existing page in the browser that issues the post.
If that POST is issued from a form post (not a post from Javascript in the page), then when you send back the response with res.render(), the browser will close down the previous page and render the new page.
Any socket.io connection from the previous page will be closed. If the new page from the res.render() has Javascript in it, when that Javascript runs, it may or may not create a new socket.io connection to your server. In any case, that won't happen until some time AFTER the res.render() is called as the browser has to receive the new page, parse it, then run the Javascript in it which has to then connect socket.io to your server again.
Remember that servers handle lots of clients. They are a one-to-many environment. So, you could easily have hundreds or thousands of clients that all have a socket.io connection to your server. So, your server can never assume there is ONE socket.io connection and sending to that one connection will go to a particular page. The server must keep track of N socket.io connections.
If the server ever wants to emit to a particular page, it has to create a means of figuring out which exact socket.io connect belongs to the page that it is trying to emit to, get that particular socket and call socket.emit() only on that particular socket. The server can never do this by creating some server-wide variable named socket and using that. A multi-user server can never do that.
The usual way to "track" a given client as it returns time after time to a server is by setting a unique cookie when the client first connects to your server. From then on, every connection from that client to your server (until the cookie expires or is somehow deleted by the browser) whether the client is connection for an http request or is making a socket.io connection (which also starts with an http request) will present the cookie and you can then tell which client it is from that cookie.
So, my understanding of your problem is that you'd like to get a form POST from the client, return back to the client a rendered processing.ejs and then sometime later, you'd like to communicate with that rendered page in the client via socket.io. To do that, the following steps must occur.
Whenever the client makes the POST to your server, you must make sure there is a unique cookie sent back to that client. If the cookie already exists, you can leave it. If it does not exist, you must create a new one. This can be done manually, or you can use express-session to do it for you. I'd suggest using express-session because it will make the following steps easier and I will outline steps assuming you are using express-session.
Your processing.ejs page must have Javascript in it that makes a socket.io connection to your server and registers a message listener for your "dlready" message that your server will emit.
You will need a top-level io.on('connection', ...) on your server that puts the socket into the session object. Because the client can connect from multiple tabs, if you don't want that to cause trouble, you probably have to maintain an array of sockets in the session object.
You will need a socket.on('disconnect', ...) handler on your server that can remove a socket from the session object it's been stored in when it disconnects.
In your app.post() handler, when you are ready to send the dlready message, you will have to find the appropriate socket for that browser in the session object for that page and emit to that socket(s). If there are none because the page you rendered has not yet connected, you will have to wait for it to connect (this is tricky to do efficiently).
If the POST request comes in from Javascript in the page rather than from a form post, then things are slightly simpler because the browser won't close the current page and start a new page and thus the current socket.io connection will stay connected. You could still completely change the page visuals using client-side Javascript if you wanted. I would recommend this option.
I am trying to implement a login system that sends a confirmation email to the user in case he logs in from a new computer/browser.
I am using Nodejs, AngularJS and PassportJS.
Any pointers to where I could find resources for this will be greatly appreciated.
The client side can detect stuff like os/browser, so you can just POST that data up to the server whenever the client loads. Other than that, you can match usernames with IP-adresses, but if you're storing that kind of information you ought to hash the information before saving it.
Could be as simple as setting a session variable (https://github.com/expressjs/session)
if(req.user){ // so that it only triggers when the user has actually logged in
if(!req.session.thisBrowser) {
req.session.thisBrowser = true || 'this computer/browser' || 'whatever';
req.user.email('You have been logged in from ...'); // do your thing
}
}
I'm currently developing an app which needs users and administrators. What I do right now is, I create an admin account on the client with username 'admin' and a default password that should be changed over the accounts-ui.
I do this because creating a user like this:
Accounts.createUser({
username : 'admin',
email : 'test#test.com',
password : 'changethispasswordovertheuserinterface',
profile : { type : 'admin' }
});
doesn't work for me on server side. That means I just create the admin in my client.js and just use this code to check if the admin is logged in.
Template.admin.isAdmin = function () {
var currentUser = Meteor.user();
// Is this hackable?
if (null !== currentUser) {
if ('admin' === currentUser.username) {
return true;
}
}
};
Is this the best way to approach this? And most importantly, is my site hackable like this (Could somebody fake it)?
Yes this is hackable, one could pull up the chrome inspector and modify this quite easily. Or even faster, by typing something like Template.admin.isAdmin = function () { return true; } into Chrome's web console
The best approach would be to only provide the information to the client from the servers end if the user is an admin. So this would mean using Meteor.allow to ensure the database can only be changed by an administrative user, if peforming ops from the client end.
It also depends a bit on what you want to use 'isAdmin' for too. If its content, you could generate the html on the server's end and send it down to the client in a Meteor.methods. At the moment the templating system doesn't provide for locking down the UI on the clients end depending on what the user's document contains.
For any administrative commands, you could use a Meteor.call at which point the user is vetted on the server's and and the transaction is performed there.
The answer on this thread works too AND the top-voted answer has code for a server side, Meteor method call.