Reputation: 683
Part I: Problem introduction
The short story: I need to pass data from the server to the client, but this data will not need to be displayed on the client end. Instead, the data is used by the client socket to join the correct room. Move on to Part II: Solution attempt
The long story:
For my first web development project, I am attempting to create a remote presenter (i.e. using a mobile device to control a Google Slides presentation on desktop).
On the frontend, I have been using plain old HTMl, CSS and vanilla JS, while the backend has involved NodeJS, Express framework and Socket.io.
Here's a brief overview of the user flow: when a user is authenticated via Google sign-in on the desktop, the website will open a socket connection which is automatically joined to a room identified by the socket id. After the user has signed in on the desktop end, the user will (for now) see this socket id as a 'secret key' which he can use to login on the mobile end.
Hence, I have a server.js
which handles the routing and form action (i.e. the secret key submitted on the mobile end) like so:
// Store all the currently connected web clients
var webClients = [];
// Index route -- home page for website and mobile site
app.get('/', function (req, res) {
// Basic user agent check - test for mobiles
var userAgent = req.headers['user-agent'];
if (/mobile/i.test(userAgent)) {
// Send mobile to the mobile login site
res.sendFile(__dirname + '/pages/mobile.html');
} else {
// Send desktop to the main site
res.sendFile(__dirname + '/pages/index.html');
}
});
// Dealing with secret key input
app.post('/secretKey', function(req, res) {
// Store the secret key in a variable first
var secretKey = req.body.secretKey;
// Check if the secret key matches with any key in the database (current session)
var index = webClients.indexOf(secretKey);
// Send the user to the mobile controls if there is something that matches
if (index != -1) {
res.sendFile(__dirname + '/pages/mobileControl.html');
} else {
res.redirect('/');
}
}
});
Unfortunately, I have hit a snag. After the mobile controls page is loaded, another socket instance will be opened on the mobile end. In order to ensure that the mobile is controlling the right presentation, I need to let the socket on the mobile end join the same room as the desktop socket (i.e. the room with the desktop socket's id). Hence, I need to pass the desktop socket Id to the mobile side, so that the mobile socket can connect to the right room.
Part II: Solution attempt
I know that several other users have already asked similar questions on SO. For example:
From these, I can summarise several prominent recommendations:
I looked into Jade and ejs, and I am reluctant to use these template systems because I suspect that they might be overkill for what I am trying to achieve here - simply pass a string from the server to client side JavaScript without rendering the string in the view. Additionally, I do not really need the partials offered by these template systems, further reinforcing my belief that these may be overkill.
Currently, this solution appeals the most to me simply because it seems to be the easy way out. I can simply do something like:
var options = {
headers: {
'set-cookie' : roomId=secretKey
}
};
res.sendFile(__dirname + '/pages/mobileControl.html', options);
Then, I can access the data in the cookie within mobileControl.js
by doing something like document.cookie
. However, I have read that there are security issues associated with this method, such as cross-site scripting (XSS) attacks. Hence, I am reluctant to use this method as well.
Aside from these three methods, I have also looked into implementing user sessions, but I do not think that that is the solution for me since user sessions will likely come in at a later stage to allow for logins to persist.
So, which method do you think I should use in this case? Should I just go along with 2, and tweak the HttpOnly
field of the cookie? Any other recommendations? Thanks a lot!
Upvotes: 3
Views: 2325
Reputation: 3373
If you don't want to use a full blown template engine (even though I think this would not be over the top for this - that's what template engines are for), how about just doing a single string replace?
Put something like
<script>var SECRET_KEY = '{{{SECRET_KEY}}}';</script>
in your HTML file, stream the file instead of sending it and use something like stream-replace to replace the {{{SECRET_KEY}}}
string with your secret key.
Untested example:
var fs = require('fs');
var replace = require('stream-replace');
app.post('/secretKey', function(req, res) {
// Store the secret key in a variable first
var secretKey = req.body.secretKey;
// Check if the secret key matches with any key in the database (current session)
var index = webClients.indexOf(secretKey);
// Send the user to the mobile controls if there is something that matches
if (index != -1) {
fs.createReadStream(__dirname + '/pages/mobileControl.html')
.pipe(replace('{{{SECRET_KEY}}}', secretKey))
.pipe(res);
} else {
res.redirect('/');
}
});
Then you'll be able to use the SECRET_KEY
variable in your script.
Upvotes: 2