SailsJS CSRF mismatch after logout AND getting new /csrfToken - node.js

I'm creating Single Page Application. I've created login-logout actions and they work fine. But if user logs out and will not refresh page in browser, he couldn't log in again because of "CSRF mismatch" error.
Steps:
1. At first page load, csrf is view local:
<div class="app" id="app" data-csrf="<%= _csrf %>"></div>
2. User logs in with this token successfully:
420["post",{"method":"post","headers":{},"data":{"_csrf":"VHcT2F44-KhZMJmhcAVB1H69BgTMWMZji9_8","login":"alex","password":"123"},"url":"http://127.0.0.1:1337/login"}]
430[{"body":{"id":"560e5756cd01633407eea8be"},"headers":{cut headers},"statusCode":200}]
3. Then, user logs out with this token:
421["post",{"method":"post","headers":{},"data":{"_csrf":"VHcT2F44-KhZMJmhcAVB1H69BgTMWMZji9_8"},"url":"http://127.0.0.1:1337/logout"}]
431[{"body":"ok","headers":{cut headers},"statusCode":200}]
4. And if he try to log in again, he will catch "CSRF mismatch" error and I expected that:
422["post",{"method":"post","headers":{},"data":{"_csrf":"VHcT2F44-KhZMJmhcAVB1H69BgTMWMZji9_8","login":"alex","password":"123"},"url":"http://127.0.0.1:1337/login"}]
432[{"body":"CSRF mismatch","headers":{},"statusCode":403}]
5. I'm catching this error and doing /csrfToken request as said in docs
423["get",{"method":"get","headers":{},"data":{},"url":"/csrfToken"}]
433[{"body":{"_csrf":"49C5OVUZ-6SIL_zW3g1NGI87ux6Mlp-UJj_w"},"headers":{cut headers},"statusCode":200}]
6. Trying to log in again with new token:
424["post",{"method":"post","headers":{},"data":{"_csrf":"49C5OVUZ-6SIL_zW3g1NGI87ux6Mlp-UJj_w","login":"alex","password":"123"},"url":"http://127.0.0.1:1337/login"}]
434[{"body":"CSRF mismatch","headers":{},"statusCode":403}]
I could repeat steps 5 and 6 with same result.
If I refresh page, I can log in without errors.
Question is, what's going on? Why token mismatch at step 6?
Edit: logout method:
req.session.destroy();
return res.send("ok");
Edit 2: sending request:
import socketIOClient from 'socket.io-client';
import sailsIOClient from 'sails.io.js';
var io = sailsIOClient(socketIOClient);
io.socket.post(form.action, serialize(form, {hash: true}), function (data, jwres){
...
});

TL;DR: Use req.session.csrfSecret = null instead of req.session.destroy() if you're doing everything over sockets.
The issue here is with the way Express sessions work. When you call .destroy() on one, it removes the entry for the current session ID (SID) in the sessions object that Express MemoryStore maintains. Normally that's fine because it will be regenerated on the next request, but that's only the next request that runs all of the Express middleware--and Socket requests to a Sails app don't do that, as anyone trying to use Passport with Sails out-of-the-box will tell you. So the next time you make a socket request (to generate a new CSRF secret and get a new token), you're not actually connected to a session and the new info doesn't get saved.
If instead of destroying the whole session, you just destroy the csrfSecret which is used to generate CSRF tokens, then the next socket request will still be attached to a session and the new secret will be saved. You'll also want to blank out any other session variables that were keeping the user logged in.

Related

Axios Interceptors Prevent Error logging into console

Using interceptors for the first time to refresh an expired JSON Web Token. The response interceptor I implemented works successfully in refreshing the token every time, but still logs the error I throw at the server end from the jwt.verify. (I see 403 forbidden each time before token refreshes).
If I'm doing it correctly on the server end and in the client end, should this error even be logging in the browser? I don't want this error to be logging into console. Now I'm thinking I have to refresh the token before it even expires and not even use interceptors.
check the order of responses from the server. Judging by the description, you are not waiting for the generation of a new token. If in JS terms check async/ await sequence

How to create sessions in a strong way and store them in cookies in node js?

In my login module once I log in I pass my request to a header to store a session here is the code
var series = rand.generate(16);
var token = rand.generate(16);
var loginToken = new LoginTokens({
userId: req.user._id,
series: series,
token: token
});
loginToken.save(function(err, l) {
if (err) {
console.log(err);
} else {
console.log(l);
res.cookie('loginToken', JSON.stringify({
series: series,
token: passwordHash.generate(token)
}));
res.status(200);
res.set('Content-Type', 'application/json');
res.end(JSON.stringify({
'success': req.params.res
}));
}
});
Though this code was pre-written and I don't understand it much(I don't get the point of randomly generating 16 digit number and saving it if somebody does please explain)
I have been tasked with implementing log out and As I don't understand the rationale behind above code I want to implement my own session module such that even if the same user logs in from a different device, both sessions will be maintained and logging out from one device will not log out someone from all device.
So now the point comes that the session id will not be based on user id. Also there is a module called express-session which I don't know if I should use or not, personally I feel that after reading it up on GitHub that what it will do is create a session id for me, which again I will have to save in my database, so why to import a module, instead I will base64 encode a random number like above and use it.
So what I want to ask is the real question
How do you implement sessions in node js?
My understanding of life cycle
A user signs up you see if he/she exists in the database and if he does you raise an error message and if he/she doesn't you create a username for him, probably by email id.
You then via code log him in. In that process, you first check if he is there in the database. If he is you go ahead take the _id field and try and create a session object. Ques what does your session table should look like? What all parameters should be present in sessions table? Then how do save them into res.cookies? Then what do you do with all those cookies, like on client side?
While implementing logout. My thinking is using passport.logout will remove the user object from request header but, I will have to first remove the entry from sessions table somehow empty the cookies and then do req.logout?
Can somebody please answer the above doubts?
I asked a very specific question but realised after the comment that what I need is logical clarity and I did not find one on youtube videos nor on SO posts, so if someone can share their views mostly in code, with some explanation it would be great.
Cookies get deleted by sending a new cookie with the same name (some invalid data would be good) and an expiry in the past.
passport.logout() removes the session cookie that passport uses to maintain the current user, the next request will no longer have the cookie and will appear as an invalid request.
I'm not sure why there's a loginToken as well as passport authentication, apart from allowing you to invalidate sessions server side (by deleting the appropriate token from the database) Check if your passport strategy configuration has any checks against LoginToken during it's deserialize user step (this is where passport takes the session cookie and turns it into a user against the request)

Session management in sails js framework

I'm new to sail's and node, I'm trying to create/maintain a session without user login. The user sends request to server and i'm trying to store the session by req.session.uid="some uniqueid", and when again the same user tries for another request i'm unable to get the session. For every request a new session id is coming(session is not persisting).
please help by posting the code or by referring to already existing code.
You should call req.session.save(); at the end to persist the data.

NodeJS - Not being able to access the route I want

I'm having some troubles getting to a route I got. The route works on http://localhost:3000/me and shows info but on http://localhost:3000/!#/me it doenst show anything. The purpose of said route is to show the logged persons' profile.
On my server routes I got:
app.get('/me', users.me);
The users.me function is as follows:
exports.me = function(req, res) {
res.jsonp(req.user);
};
The console states it expected a object and got an array, I can understand that since I'm getting a json, but how can I send the own user back to the front-end so it shows his/her profile?
Edit: I managed to solve my problem, since I use passportjs I can get the user id from the session. Since I already had a route for a user by id, I simply had to redirect to said route. Like this: req.redirect('users/'+ req.session.passport.user);. Since I already had a /users/:userId route working it completely solved my issue.
Edit2: Apparently there are several ways to get the user id. Try to console.log the request and you will see what I mean :)
/me and /!#/me are not the same route . The later won't match get('
/me',..)
the hash fragment #/me will not send to the server, you cannot capture that by server side routers(without force the page refresh by client code). But you can manage that by client-code.

mediator.user is lost after route is changed manually

I am building a single-page-application with a passport local authentication.
The user is authenticated and returned within app.post "/login"
After the userobject is returned I save it in Chaplin.mediator.user (like seen in the facebook-example). As long as I don't change the URL manually everything works fine, but when I change the URL and hit enter, the application gets loaded again however with a different route --> no user in the front end.
What I am doing right now is everytime I change the route manually I send a request to the server and ask for the user in req.user. After receiving the user the application continues its workflow. It works but I don't think this is how it's meant to be. If you don't wait for the response you end up having no user object although you are logged in.
How can I solve this problem?
EDIT: I ended up saving the user in a seperate cookie.
This is how I save the user:
$.cookie.json = true;
$.cookie 'user', user.toJSON()
And this is how I extract the user after the page was loaded:
userCookie = $.cookie 'user'
if userCookie?
mediator.user = new Model JSON.parse userCookie
You need the jquery-cookie plugin to make it work. Don't forget to delete the cookie if the user logs out. And of course I am still open to other ideas.
You should store the user (aka the session) locally. When the Backbone application loads, you should then route the user to the correct place if they in fact are already logged in.

Resources