Express-Session: Get Session with session ID - express-session

I'm fully aware that normally, with express-session, the session is returned with each request and there's no need to try to manage sessions. However, imagine for a moment, you have multiple applications each with different jobs, but you want your authentication to work with all your apps. So the problem there is, a session created on one app will not be magically replicated on all your other apps.
So what do you have to do? Presumably, you would need to query the app which created your sessions and get the session using it's ID. And you would store the session ID on the client via local storage.
I'm unable to find anything that would support this architecture (Micro-Apps instead of Monolithic).
There's some documentation in the readme of express-session which explains their store a bit but not how to use it. As an example, it states you can get the session like this:
store.get(sid, callback)
However, the only store available is a class called "Store". There's no clear indication in the docs on how to get/use this.
The docs go on to say something about "compatible session stores". Does this mean express session doesn't have it's own store, what are they trying to say in this readme? It's very cryptic and could have been written better...

Here's how I solved it:
In your middle-ware do the following (I'm using Typescript):
export const sessionStore = new session.MemoryStore();
...
UserApp.use(session({
secret: SESSION_SECRET,
resave: false,
saveUninitialized: true,
store: sessionStore, // Session store goes here!
cookie: {
secure: true,
httpOnly: false,
maxAge: ONE_DAY }
}));
We will call this app which contains the middle-ware App A. I have another app, I will call App B. From that app, we will make an http.request to App A. App A will call the following.
sessionStore.get(payload.SID, (_err, session) => {/* callback */})
That will return the session from the session store, provided it still exists. We return that session to App B and we can do whatever we want with it. Hope this helps someone, this was annoying for me.
EDIT: Ah and then I saw this...
Warning: the default server-side session storage, MemoryStore, is
purposely not designed for a production environment. It will leak
memory under most conditions, does not scale past a single process,
and is only meant for debugging and developing.
Great.
EDIT 2:
I'm using this store instead, pretty simple to swap out.
https://www.npmjs.com/package/connect-redis

Related

General doubts about Express-session middleware

I am a complete beginner in the web and I have a lot of stable doubts which I hope you won't mind answering.
Ques1: Sir how my MongoDB store is clearing a session after the max-age or expiry time? Does the driver of connect-mongo have some async function that is called as an event whenever the session is expired or the MongoDB server has a special meaning for sessions collection or MongoDB have build-in methods that are doing so with the help of some async functions and all?  I implemented this manually for learning purposes and it was fun but how this thing is happening in the Express-session part I am amazed and very confused. Who is taking care of the expiry thing and all?
Ques2: Sir I don't understand the resave thing. I have done a lot of googles and came to know about resave and saveUninitialsied. I know what saveUninitialised means. It says don't save a new session unless and until the cookie is modified. Cool thing. Then people went on explaining about resave on StackOverflow and touch method but I don't get from there. What does it even mean if the database does not have a touch command initialised? Also does the resaving and other stuff happens on the database or from the command of my server to the database. It's my servers command on the database right? Or something is going on under the hood.
Ques3: Sir if I want to send more than 1 cookie to the user in express-session, is it possible and is it a professional practice? Or should I use cookie parse with res.cookie to send more than 1 cookie? Also, can I change the name of connect.sid to something else which is my next question?
Ques4: Sir I wanted to know if I can store more key-value pair as key and JSON in the cookie so I did something like this but the data is not stored in MongoDB as far as I have checked. So is the data stored in the cookie?
//creating the session
app.use(session({
name: 'auth cookie', //Name of the cookie
store: new MongoStore({ mongooseConnection: mongoose.connection }), //To store session-store as Mongoose,
secret: process.env.COOKIE_SECRET, //Used to sign the cookie
saveUninitialized: false, //If the user bounces off, don't create a session
resave: false, //Touch method is implemented by MongoDB. I want to know more about this option
cookie: {
path: '/', //Where the cookies would be saved
httpOnly: true, //Javascript on client cannot get access to cookies. Good for security.
secure: false, //http service. Not strictly HSTS or HTTPS
maxAge: 60*1000, //1 minute
sameSite: true, //No XSRF
name: 'Hobbit Holet', //This is how values are stored in the cookie for furture use
age: '21' //This is how values are stored in the cookie for furture use
}
}));
Ques5: Sir as you can see I have a secret. Now I can see the cookies on my browser. Now the cookie has some data and I have the secret key. So for debugging purposes and only in development mode is there a way I can decrypt the data inside the cookie by the secret key I am having here? If there is a way please share the algorithm used to hash or encrypt the data before sending.
Thank you, everyone and I am open to constructive criticism if I wrote something wrong or did any mistakes on my part in asking the question. I hope to get help as I am stuck here for the day.

How to use new enhanced sessions in Parse with users created on cloud code?

I was trying out the new enhanced revocable sessions in Parse on my Android app. It works well when logging in or signing up via email password or facebook but doesn't work well for custom authentication, e.g. google+.
I'm currently logging in the user using the cloud code which also creates the new user when signing up. This does not create a new Session object, that means the new enhanced sessions are not used and it still uses the legacy sessions.
I pass the session token back to client where using the become method the user logs in but it's the legacy sessions.
This feels like the feature is not complete but I would really like to move to the new enhanced sessions with my app. Has anyone worked with them yet? Are there any workarounds using the REST API or by creating the sessions manually and handling them manually? I looked into the JS API but it says it's only read only.
Here's the Blog post on Enhanced Sessions.
Where should I go next?
Yes, I found a solution but it's a workaround, works for my case because I don't support signing up with user/password.
Basically, the solution (cloud code) in semi pseudo-code is:
Fetch the user with master key
Check if user.getSessionToken() has value
if it has, return the session token and do a user.become() in the client as usual
if it's not, here the workaround, do the following:
yourPreviousPromiseInOrderToChainThem.then(function(user)
password = new Buffer(24);
_.times(24, function(i) {
password.set(i, _.random(0, 255));
});
password = password.toString('base64')
user.setPassword(password);
return user.save();
}).then(function(user) {
return Parse.User.logIn(user.get('username'), password)
}).then(function(user) {
var sessionToken = user.getSessionToken();
// Return the session token to the client as you've been doing with legacy sessions
})
That means, I'm changing the user password each time in order to make a remote login and, of course, I know thist can't be applied to all cases, it's enough for app because I don't support login with user/password (only third party logins) but I understand that maybe it's not for all cases.
I got the idea from this official Parse example.
I don't like this solution because I think is not a workaround, it's a mega hack but I think there is no other way to do it currently (either Parse.com or Parse-Server)
If you find other workaround, please, share it :)

How to have users 'reconnect' with soundcloud on each page reload?

I'm using the Javascript SDK inside a node.js (Express) App.
Connecting Users works fine, but the connection does not persist between page reloads.
Is this the default behaviour, or is the User supposed to stay connected in new requests?
Do I have to use OAuth token authentication and if so, how can this be done with the JS-SDK?
Inside the "Permission"-Popup, Users are already logged in with soundlcoud, though.
(just have to click the "connect" button each time)
Figured I'd share my answer for those who are unsatisfied with the current answers for automated oauth:
Retrieving access_token:
I had to define get and set cookie functions and then I use the functions to set and retrieve a function holding the access token. I'm not going to give these functions for conciseness but you can easily find them with a google search. I then use this line of code to get the SC access token (once the user has authenticated for the first time)
SC.accessToken()
Setting token:
So this is kind of just an elephant in the room in my opinion that for some reason no one has mentioned. How in the **** do you connect w/ SC using the access token? Do you set it as oauth param? On each call pass it? Well, after experimenting with putting the parameter in every single place I could think, I found out you have to do something like this:
SC.initialize({
client_id: '[removed for security reasons]',
client_secret: '[removed for security reasons]',
redirect_uri: '[removed for security reasons]',
access_token: getCookie("sc_lm2"),
scope: 'non-expiring'
});
//Where "sc_lm2" is the name of my cookie
Hope the helps! Took me a while to figure this out for such a simple thing
EDIT
Using PHP and Wordpress:
$json = wp_remote_get("http://api.soundcloud.com/users/[user_id]/tracks.json?client_id=[client_id]");
$soundcloudData = json_decode($json['body'],true);
(substitue cURL functionality if you're not using Wordpress). #krafty I assume you would just change the endpoint from "/tracks" to "/users" but I can't say I have ever really needed to grab anything but tracks using the Soundcloud API. Hope this helps, though I'm not sure I fully understand what it is that you are trying to accomplish (or rather, how exactly you're going about it) - are you trying to allow user logins? If you want to explain fully what you're trying to accomplish and the steps you're taking I'd be happy to take a crack at it.
Yep, this is the way to do it, officially. :)
For the Next SoundCloud site, we store the token in localStorage (until the user logs out, of course). In each AJAX request to the API from the front end, we put the oauth token in a request header:
jqXHR.setRequestHeader('Authorization', 'OAuth ' + the_oauth_token);

Using Everyauth/Express and Multiple Configurations?

I'm successfully using Node.js + Express + Everyauth ( https://github.com/abelmartin/Express-And-Everyauth/blob/master/app.js ) to login to Facebook, Twitter, etc. from my application.
The problem I'm trying to wrap my head around is that Everyauth seems to be "configure and forget." I set up a single everyauth object and configure it to act as middleware for express, and then forget about it. For example, if I want to create a mobile Facebook login I do:
var app = express.createServer();
everyauth.facebook
.appId('AAAA')
.appSecret('BBBB')
.entryPath('/login/facebook')
.callbackPath('/callback/facebook')
.mobile(true); // mobile!
app.use(everyauth.middleware());
everyauth.helpExpress(app);
app.listen(8000);
Here's the problem:
Both mobile and non-mobile clients will connect to my server, and I don't know which is connecting until the connection is made. Even worse, I need to support multiple Facebook app IDs (and, again, I don't know which one I will want to use until the client connects and I partially parse the input). Because everyauth is a singleton which in configured once, I cannot see how to make these changes to the configuration based upon the request that is made.
What it seems like is that I need to create some sort of middleware which acts before the everyauth middleware to configure the everyauth object, such that everyauth subsequently uses the correct appId/appSecret/mobile parameters. I have no clue how to go about this...
Suggestions?
Here's the best idea I have so far, though it seems terrible:
Create an everyauth object for every possible configuration using a different entryPath for each...
Apparently I jumped the gun and wrote this before my morning cup of coffee, because I answered my own question, and it was quite easy to implement. Basically I just had to create my own custom express middleware to switch the everyauth configuration before the everyauth gets its grubby paws on the request, so...
var configureEveryauth = function()
{
return function configure(req, res, next) {
// make some changes to the everyauth object as needed....
next();
};
}
and now my setup becomes:
var app = express.createServer();
everyauth.facebook
.entryPath('/login/facebook')
.callbackPath('/callback/facebook');
app.use(configureEveryauth());
app.use(everyauth.middleware());
everyauth.helpExpress(app);
app.listen(8000);
Notice that I don't even bother fully configuring the everyauth Facebook object during the startup, since I know that the middleware will fill in the missing params.

What's the best action persistence technique for a Catalyst application?

I'm writing a Catalyst application that's required to have a fairly short session expiration (15 minutes). I'm using the standard Catalyst framework authentication modules, so the user data is stored in the session -- i.e., when your session expires, you get logged out.
Many of the uses of this application will require >15 minutes to complete, so users will frequently submit a form only to find their session state is gone and they're required to log back in.
If this happens I want to preserve the original form submission, and if they log in successfully, continue on and carry out the form submission just as if the session had not expired.
I've got the authentication stuff being handled by an auto() method in the controller -- if you request an action that requires authentication and you're not currently logged in, you get redirected to the login() method, which displays the login form and then processes it once it's submitted. It seems like it should be possible to store the request and any form parameters when the auto method redirects to the login(), and then pull them back out if the login() succeeds -- but I'm not entirely sure of the best way to grab or store this information in a generic/standard/reusable way. (I'm figuring on storing it in the session and then deleting it once it's pulled back out; if that seems like a bad idea, that's something else to address.)
Is there a standard "best practices" or cookbook way to do this?
(One wrinkle: these forms are being submitted via POST.)
I can't help thinking that there's a fundamental flaw in mandating a 15 minute timeout in an app that routinely requires >15 minutes between actions.
Be that as it may, I would look at over-riding the Catalyst::Plugin::Session->delete_session method so that any contents of $c->request->body_parameters are serialised and saved (presumably to the database) for later recovery. You would probably want some rudimentary check of the POST arguments to ensure they're what you're expecting.
Similarly, create_session needs to take responsibility for pulling this data back out of the database and making it available to the original form action.
It does seem like a messy situation, and I'm inclined to repeat my first sentence...
UPDATE:
Whether you use delete_session or auto, the paradoxical issue remains: you can't store this info in the session because the time-out event will destroy the session. You've got to store it somewhere more permanent so it survives the session re-initialization. Catalyst::Plugin::Session itself is using Storable, and you should be able to with something along these lines:
use Storable;
...
sub auto {
...
unless (...) { #ie don't do this if processing the login action
my $formitems = freeze $c->request->body_parameters;
my $freezer = $rs->update_or_create(
{user => $c->user, formitems => $formitems} );
# Don't quote me on the exact syntax, I don't use DBIx::Class
}
...
my $formitems = $c->request->body_parameters
|| thaw $rs->find({$user => $c->user})->formitems
|| {} ;
# use formitems instead of $c->request->body_parameters from here on in
The underlying table probably has (user CHAR(x), formitems TEXT) or similar. Perhaps a timestamp so that nothing too stale gets recovered. You might also want to store the action you were processing, to be sure the retrieved form items belong to the right form. You know the issues for your app better than me.
I would store the form data as some sort of per user data in the model.
Catalyst::Plugin::Session::PerUser is one way of doing that (albeit somewhat hackishly). I would reccomend using the session plugin only for authentication and storing all the state info in the model that stores your user data instead.
And I totally agree with RET's opinion that the 15 minute limit seems really counter productive in this context.
I came across this whilst searching CPAN for something entirely unrelated.
Catalyst::Plugin::Wizard purports to do exactly what you need. The documentation suggests it can redirect to a login page whilst retaining the state of the previous action.
NB: I haven't used it, so can't vouch for its effectiveness.
In the end, we ended up grabbing the pending request (URL+params) in the auto(), serializing and encrypting it, and passing it via a hidden form element on the login page. If we got a login request with the hidden element populated, we decrypted and deserialized it and then redirected appropriately (making sure to pass through the standard "can this user do this thing" code paths).
You could always have some javascript on the client that keeps the session from expiring by making a small request every few minutes.
Or you could have AJAX check for an active session before posting the form and presenting the user with a new login box at that time if needed.