session icon indicating copy to clipboard operation
session copied to clipboard

Is there a suggested way to handle sessions for multiple devices connecting to a single account? Keeping synchronized.

Open CosmosisT opened this issue 3 years ago • 7 comments

I don't see much written up and I know this could be subject to app design, but with using this I ran into a fun little scenario that I'd like to try and make more stateless.

If I connect to an account on two different devices, or sessions these two sessions don't keep sync at all so an adjustment of avatar or any simple data, won't reflect across the sessions. This is normal behavior out the box and no issues with that, it doesn't break my code at all.

What would be the suggestion if I wanted to keep the devices synchronized but avoid using expensive protocols like web-socket; Could I re-use the cookie ID and it not be a security problem; what are the ideas around this. Thank you!

CosmosisT avatar Jan 08 '22 19:01 CosmosisT

As you are describing it currently, you are abusing sessions for your intentions. Sessions should be used to contain information that should persist between HTTP requests (i.e. whether a user is logged in, a state of the user, etc.).

You are not saying whether you want a real-time synchronization, it seems that way because you mention websocket but on the other hand you're here on the express-session which is not real-time again, so I'm sorry that I most likely cannot provide you with the immediate answer. I'm writing down two options which I believe are best practices:

  • If you want realtime communication between a browser and a server, you need websockets. This is not an expensive protocol, it's capable of streaming 1080p video so avatar updates shouldn't be a problem. NodeJS and browsers have it built-in (although NodeJS has several packages to make them easier to setup and use) so it shouldn't increase your codebase size too much.
  • If you just want avatars to update whenever you refresh the page, then it would be as simple as storing your avatar to file. If the user uploads another avatar, you overwrite their avatar file. A refresh will re-load the avatar image and it will always show the current/latest one.

ultimate-tester avatar Feb 13 '22 15:02 ultimate-tester

I generate avatar paths uniquely to prevent scrapes, it is stored in database with user information but it's undesirable to constantly make calls to database to see if this has changed or know that it's still the same.

So having a compliant way to keep sync without issues or massive requests going out be suitable.

An idea I had as well was to keep track of session ids generated by username in a separate table so if updates to avatar or other information is made this list is referenced and the data is synced across the sessions for that specified user.

This would require I never generate the same unique ID twice and am able to clear the list properly but would mean upon refresh a user only makes the single call for session store opposed to session store + user account.


This whole problem can be solved by just requesting database each refresh but it is already by referencing the session so to keep requests down and sync across devices I'd like to hear ideas. (Redis I think can fast-search values but not sure how fast/reliable that is).

CosmosisT avatar Feb 14 '22 12:02 CosmosisT

I most likely do not understand your situation which is making me give you a possible wrong answer, but if this is indeed just as simple as I understand, you should indeed do a database query every request. This is absolutely normal. You can write a small middleware that will just attach the user to the req object (req.user) and use it. Furthermore, I always tend to make avatar paths as easy as possible and incorporate the user's ID into it (my IDs are UUIDv4's so not guessable or countable). By exposing the User ID to the view, the view can generate the full path to the avatar and the browser will take care of the loading and doesn't bother with if this is an updated avatar or not.

Looking at passport JS you can see that they are doing the same approach. Every request you make the current user ID is fetched from the session which is then looked up in the database and stored in the req object.

Hearing your statement regarding websockets before (the overhead), you might be in a situation where I was in before as well; trying to keep everything to a minimal expecting this to give the highest performance and make the application "the best". My advice to you is to drop this mindset. Your application should in fact be maintainable, according to security best practices, etc. Performance does not need to be optimized at the beginning, but according to demand (emphasize on "optimized" because it definitely does need to be taken into the design). If you notice your database is overloaded by requests then you can re-evaluate your code, upscale the database hardware, or implement caching techniques for example.

ultimate-tester avatar Feb 14 '22 13:02 ultimate-tester

The goal: If one or more devices sign into cosmosist, a unique session is created for the one or more devices. The unique IDs would be associated some way/shape/form to the username cosmosist. So if any of the devices did in fact make an adjustment to the profile/avatar or any user setting belonging to cosmosist the other devices would know.

The server would take the request, update the account for cosmosist and then set/update the session for each user associated to cosmosist (and their sessions on file).

This would prevent the need for wasteful queries each refresh which would substantial impact my systems performance given I deal with thousands of users now.

The question is how possible is this, can't see there being an issue overly. Perhaps I pre-key the sessions like so sess:cosmosist:sessid

So I can search by sess:cosmosist and get their devices.

CosmosisT avatar Feb 14 '22 15:02 CosmosisT

Thanks for explaining it further. I stick with my previous messages. I understand you're dealing with quite some users now, but take the advice from me as I have experience with programming applications for a user base with 100.000+ users.

A session is used to keep data that should be persisted in between browser and server requests, not to synchronize data between multiple clients. We have other mechanisms for that.

Use the database more and cache where necessary. How else are you retrieving other user related data? Do you store everything in the session object? Your servers memory will run out this way if you have thousands of users.

ultimate-tester avatar Feb 14 '22 15:02 ultimate-tester

My sessions are quite small in size actually. I have an object containing loggedin, validated, username and now avatar. Other account information is generated upon loading settings page or before entering a room.

The goal was to minimize the requests if necessary and I did so actually by setting up my session ID's differently.

genid: (req) => ${req.body.username}:${uid.sync(256)},

This allowed me to do a fast scan on uids by the username and update all user sessions to match current.


This allows me to keep the important session information synced across sessions. If I need to pull more detail it's generally an API request and that's acceptable. Hopefully this makes sense I do appreciate feed-back and no-goes, this should be more than fine and non abusive and keep me in play just nicely at the cost of like 20MB for 20-50K sessions (small data).

CosmosisT avatar Feb 15 '22 00:02 CosmosisT

I appreciate the additional heads-up and advice I really do! Helped me sort something cool out.

CosmosisT avatar Feb 15 '22 00:02 CosmosisT