I'm very new to learning Node and Express, and I'm still trying to wrap my head around the code flow with express. Suppose we have code that looks like this in a session.js:
app.post('/session', notLoggedIn, function(req, res) {
User.findOne({
username: req.body.username,
password: req.body.password
}, function (err, user) {
if (err) {
return next(err);
}
if (user) {
req.session.user = user;
res.redirect('/users');
} else {
res.redirect('/session/new');
}
});
});
Assuming the User is a required mongo schema. What I find strange is the session.user assignment:
req.session.user = user;
Since the req variable will be out of scope after the redirect, but we're obviously doing this to persist the user data, I'm left with figuring out which of the following scenarios describe what is happening. Either (A) the argument that's being assigned to the req parameter (when the callback is called) is stored/somewhere still on the stack, (B) the session is stored/on the stack and being assigned to a new req object before it's passed in to the callback, or (C) the same as B, but on the user field (seems unlikely and maybe contrived on my part).
There's an overall session data structure that stores all session info for all users (like a global, but it could also be in a database - just something that is persistent at least across connections). Each client's session data uses one unique key to index into the session store to get the session data for that client.
Part of establishing a session for a given browser client is creating a unique client key (which will usually be stored in a cookie) that becomes the index into the global session object.
On an incoming http request, Express middleware that supports the session checks a particular client cookie and if that particular cookie is found on the http request and is found in the global session object/database, then it adds that session's stored info to the request object for the http request handler to later use.
So, here's a typical sequence:
Incoming HTTP request.
Middleware checks for session cookie.
If session cookie not there, then create one and, in the process created a unique id to identify this http client.
In the persistent session store, initialize the session for this new client.
If session cookie is there, then look in the session store for the session data for this client and add that data to the request object.
End of session middleware processing
Later on in the Express processing of this http request, it gets to a matching request handler. The session data from the session store for this particular http client is already attached to the request object and available for the request handler to use.
I think the accepted answer misses one crucial detail, which was surfaced by #jpaddison3: "Express-session hooks res.end() to see when the request is done and then it updates the session store if needed."
Basically, when you add the expression-session middleware, it wraps res.end() so that the new session information is saved just before the stream is closed.
Related
I have written a small application using React Native and TS. In my application I use authorization through access_tokens with lifetime for one hour. Every time the token expires, I launch refreshToken function where I send access_token and refresh_token that were saved previously in local storage and send it through POST method.
basically something like this:
if (token_from_storage !== is_expired) {
//assuming token is indeed in storage
return access_token;
} else {
//this Function calls an api with refresh token
const { tokens }: { tokens: ITokens } = await refreshToken(refresh_token);
saveTokens(tokens); //here we save new access_token and refresh_token
}
after refreshToken has finished the new tokens are saved locally (and were updated on DB level) and must be used if you want refresh tokens again.
For example, we launch an application with an expired token. The methods on the start will be getUserInfo() and getApplicationInfo() that get some important data. They are launched through redux-saga or something similar (async).
//first app screen
useEffect(() => {
dispatch(ACTION_GET_USER_INFO);
dispatch(ACTION_GET_APPLICATION_INFO);
},[])
first method would launch refresh_token about the same time as the second one. The tokens will be rebuilt for the first method and update on the database level. By the time second method calls refreshToken (he was launched at the same time; he also thinks that the token is dead), the refresh_token will be changed on DB level and would not answer 200 on the refreshToken() call.
What should be done to achieve proper refreshing?
If I am understanding your issue correctly it sounds like you are calling two async function that both refresh tokens if the current token has expired.
This indicates you are experiencing a race condition in your useEffect statement, and because the requests handle refreshing their token independently at least one of the request will have an invalid token.
Personally, I would not being handling tokens in the client at all especially with local storage.
If you must however, you can fix the race condition by adding a service layer that handles requests, and tokens singularly. Your dispatch functions will pass through the service layer which can determine the need to refresh a token and if a token is being refreshed already in a synchronous manner before attempting to send the requests.
To give an example, let's say a user sends some json to a server containing his socket id, and the server takes 5 seconds to respond. When it's ready to respond, it extracts the socket.id from the json object, and emits some data only to that socket.
If the user where to refresh (therefore changing the socket he is connecting via ) between sending the message and the server responding, how would I go about ensuring they still receives the data?
Server:
io.on("connection", function(socket) {
socket.on("data", function(data) {
var id = data.socket_id;
io.sockets.connected[id].emit("response", "sup");
// I know I could just do -> socket.emit("response", "sup") <- but im simply trying it out for learning purposes
})
})
Client:
socket.emit("data", {
username: "chris",
socket_id: socket.id,
});
socket.on("response", function(res) {
console.log(res);
})
If the user refreshes their page, that will make a new socket.io connection that will have a new and different socket.id. So, if you want to be able to identify a particular browser session across refreshes, then you need an identifier that survives a page refresh. That is not the socket.id.
Most likely, you would use some sort of long lasting userID in a cookie. If you don't already have any sort of authentication happening that gives you a userID in a cookie, then you can simply coin a unique ID in a cookie if there isn't already one anytime a user makes a request to your server. That will give you a unique userID in a cookie for every incoming socket.io connection (as long as cookies aren't disabled). When your socket.io connection connects, you can add it to a Map object you keep where the userID is the key and the current socket.id is the data and you can add the userID as a property on the socket. When the socket disconnects, you would then remove it from the Map object.
Then, when you want to save a reference to the user, you save the userID, not the socket.id and that reference stays the same across a page refresh. When you want to send to the userID, you can look it up in the Map object to get the current socket.id (which will be automatically kept current by your connect and disconnect event handlers as described above) and then use that to send to it.
How do I determine which user has logged in in an express js application? I want to determine who is sending the request in my server program.
MeanJS stack
You can use req.user
exports.some_method = function(req, res) {
var user = req.user;
//do something
};
But you have to use users.requiresLogin to have persisted user
app.route('/model/:modelId').get(users.requiresLogin, my_controller.some_method)
It's implementation is pretty simple
exports.requiresLogin = function(req, res, next) {
if (!req.isAuthenticated()) {
return res.status(401).send({
message: 'User is not logged in'
});
}
next();
};
It's session based implementation indeed, but still good. That's it
Pure expressJS
You must use middleware that would detect current user by its cookie
It's more complicated indeed, and you have to write own implementation
But there are plenty of plugins, like passport, that would validate user by your fields. Also can serialize into req.user and vice versa
But i would strongly recommend to checkout MeanJS stack implementation, it's pretty easy to understand. As the name implies, it's MongoExpressAngularNode, so it's express based stack.
More
It depends on what kind of auth schema you are using, if it's REST, then you have to pass token in all requests to server, so that server checks db and get's user with corresponding token. If it's sessions based, then you can simple use any session based plugins. But the idea is same, when signing in, serialize user to session table, set cookie, when receiving request take cookie from requester, deserialize from session table, you got user now
I have been using passport.js. It works and I am able to get the oauth_token and Verifier easily from APIs.
In passport.js each API has a strategy which basically decide how to communicate with that API server. At the end a callback to get returned user profile.
But I saw no way to use oauth_token to get the profile myself. Its a one time shot at the end of Oauth authentication to save data in session or db.
Is there any way I can use my oauth_token to directly access the user profile any time usingpassport methods.
In prototype I saw userProfile function which does what I need but its a private method somehow. I don't know how to use it
Update 1
I have been scavenging the git repo for passport.js , I come to know they use the "node-oauth" to manage calls to the API servers. This is available in any strategies _oauth.
But I am not aware what calls to make to get resource token. Also I have to initiate all API calls in callbacks step by step to imitate the token access calls. Is there any standard way to do this.
Without digging into the code (but having used Passport before) I'm assuming that the oauth_token is being stored with the user data in your database. You may have to access your database models directly in order to get the token, then you can use it with the provider APIs to get access to the information you need.
Visit following page to get a grasp of how to use the _oauth property.
https://github.com/ciaranj/node-oauth
It works like this:
oauth.get(
'https://api.twitter.com/1.1/trends/place.json?id=23424977',
'your user token for this app', //test user token
'your user secret for this app', //test user secret
function (e, data, res){
if (e) console.error(e);
console.log(require('util').inspect(data));
done();
});
In case you implement your own OAuth1.0 strategy you easily could overwrite
following method to implement your own logic for fetching the profile data from the remote OAuth server:
/**
* #overwritten
* #method userProfile
* #class OAuth1Strategy
* #description used by OAuth1Strategy class while authenticating
* to get the user profile from the server
*/
OAuth1Strategy.prototype.userProfile =
function(token, tokenSecret, params, done) {
this._oauth.get(
config.profileUrl,
token,
tokenSecret,
function (e, data, res){
if (e) console.error(e);
console.log(require('util').inspect(data));
done(e, data);
});
};
How do you find the current Session Id on the client?
I am able to get what seems like the last session id, not the current session id.
console.log(Meteor.default_connection._lastSessionId)
The wording for this is a bit confusing, but _lastSessionId is the current session id.
It is only called this because if the client is disconnected and seeks to reconnect it wants to re-establish the session with the last session id.
The client would reconnect with a message like this :
{"msg": "connect ", "session": "ERoZSR3R3f8zBQ6Ry", "version": "pre1","support":["pre1"]}
The session uses the lastSessionId value. This is then used to re-establish the previous connection.
This is the only case where a new session id would be assigned on a reconnection. That or the session is expired off the server.
If the server restarts it's cache is refreshed and it wouldn't recognize the session anymore, and a new session id would be assigned.
The Meteor login token by default is stored in local storage (not in cookie).
On the client you could access
token = Meteor._localStorage.getItem('Meteor.loginToken')
On the server, once token is received, use the Accounts api to hash using
Accounts._hashLoginToken(res.req.body.token)
Then, you could validate hashed value against users collection for services.resume.loginTokens.hashedToken field
This hack can be used to build meteor - express integration