Expressjs doesn't destroy session - javascript

I have an Backbone View which sends an Ajax call to the server to remove a session.
On the server following event is triggered:
app.delete('/session', function(req, res) {
if (req.session) {
req.session.destroy(function() {
res.clearCookie('connect.sid', { path: '/' });
res.send('removed session', 200);
});
} else {
res.send('no session assigned', 500);
}
});
The weird about this is that I can press the logout button multiple times without getting a HTTP 500 error code. Also chromium shows me that a cookie still exists.
What is going wrong?
Regards
EDIT:
I found out that this isn't directly a session issue but a cookie one.
I added res.clearCookie to the route. Unfortunatly the behaviour (cookie, session keep alive) didn't change
EDIT2:
I now gave res.clearCookie some parameters => res.clearCookie('connect.sid', { path: '/' });
Now at least the cookie is gone in the browser. But the session seems to be still available.
Or at least I can call the logout route how often I want even req.session should be false
EDIT3:
I now removed all sessions out of redis and restarted everything (redis, node, browser).
Than I have logged in again and logged out. This works so far but when I relaod the page with F5 I get a new session. WHY?

To concentrate all comments together I have written an answer:
Because express always creates a session and a cookie for a client we have to take a different approach than just to check if there is a session.
This parts handles logins
app.post('/session', function(req, res) {
User.findOne({ username: req.body.username })
.select('salt') // my mongoose schema doesn't fetches salt
.select('password') // and password by default
.exec(function(err, user) {
if (err || user === null) throw err; // awful error handling here
// mongoose schema methods which checks if the sent credentials
// are equal to the hashed password (allows callback)
user.hasEqualPassword(req.body.password, function(hasEqualPassword) {
if (hasEqualPassword) {
// if the password matches we do this:
req.session.authenticated = true; // flag the session, all logged-in check now check if authenticated is true (this is required for the secured-area-check-middleware)
req.session.user = user; // this is optionally. I have done this because I want to have the user credentials available
// another benefit of storing the user instance in the session is
// that we can gain from the speed of redis. If the user logs out we would have to save the user instance in the session (didn't tried this)
res.send(200); // sent the client that everything gone ok
} else {
res.send("wrong password", 500); // tells the client that the password was wrong (on production sys you want to hide what gone wronge)
}
});
});
});
That was the login part lets go to the logout:
app.delete('/session', function(req, res) {
// here is our security check
// if you use a isAuthenticated-middleware you could make this shorter
if (req.session.authenticated) {
// this destroys the current session (not really necessary because you get a new one
req.session.destroy(function() {
// if you don't want destroy the whole session, because you anyway get a new one you also could just change the flags and remove the private informations
// req.session.user.save(callback(err, user)) // didn't checked this
//delete req.session.user; // remove credentials
//req.session.authenticated = false; // set flag
//res.clearCookie('connect.sid', { path: '/' }); // see comments above res.send('removed session', 200); // tell the client everything went well
});
} else {
res.send('cant remove public session', 500); // public sessions don't containt sensible information so we leave them
}
});
Hope this helps

Related

Why does req.sessions return an empty object instead of the passed value?

I'm learning express.js and I'm stuck on one little problem.
How can I save sessions in the browser so I don't have to log in every time. I use cookie-session. I send the login data downloaded from the form for validation using the POST method to endpoint ./login. After successfully passing the validation, it saves sessions in this way: req.session.admin = 1;, and then using redirect, it redirects to the admin endpoint. After redirecting to ./admin, I try to read if there is a previously saved req.session.admin session. The above action returns me undefined, and the req.session itself returns an empty object {}. I tried console.log on the ./login endpoint and yes, everything is OK i.e. it returns 1 and on the ./admin endpoint it is undfined.
My question to you is: Has anything changed recently in the implementation of cookie-session (although I don't think so, because I do it according to the documentation available on npm), or do I need to install some package besides cookie-session?
Save the session
.post('/login', (req, res) => {
const { body } = req;
if (password === body.password && login === body.login) {
req.session.admin = 1;
res.redirect('/admin');
} else {
res.redirect('/login');
}
});
This is where my problem arises, because the session is not saved and each time the validation fails, I return to login
.all('*', (req, res, next) => {
if (!req.session.admin) {
res.redirect('/login');
return;
}
next();
})
Firstly, req.session.admin = 1 does not save session, but it assigns value to separate request. Each request is independent.
You have to return some information to client (e.g token) for further using. Token should be sent in each request in appropriate header and express should verify it.
What's more you should not send credentials in plain text.
What I recommend is to familiarise yourself with JWT concept https://jwt.io/introduction

URL Access to static HTML pages - in Parse.com platform before login

I’m new to parse and was trying to create a simple web app with it using static HTML pages, CSS, and JS
I’ve used cloud functions also for a few tasks.
The thing is that I’ve created a login page and a function that checks if the user is logged into the web app with JS client-side and it looks like this:
function CheckUserLogin(){
var currentUser = Parse.User.current();
if (currentUser) {
//do nothing
} else {
//rediect user to login html page
window.location.href = “loginpage.html”
}
}
The thing is that if the user navigates to the /Home.html page via browser URL he can see the page for a few seconds until js checks if he logged in and redirects the user out to the login page.
I guess that’s not so secure…
Any idea how to get a solution for achieving this task via js or parse.com cloud functions more secure?
You can do following two things to achieve this:
First you need to override Parse login with your own custom login, you can do something like this:
// this will add sessionToken in a cookie, which can be later used to check if your user is logged in or not
app.post('/login', function(req, res) {
Parse.User.logIn(req.body.username, req.body.password).then(function(user) {
var val = JSON.stringify({sessionToken: user.getSessionToken()});
var opts = {path: '/', httpOnly: true};
res.cookie('parse.session', val, opts);
res.redirect('/');
}).then(null, function(error) {
res.clearCookie('parse.session');
res.render('pages/login', { flash: error.message });
});
});
Now you need to add a middleware to get this user from the cookie:
// Middleware adding current user to `req.user` or redirecting to login if not logged in
app.use(function (req, res, next) {
var cookie = null;
new Parse.Promise.as().then(function() {
cookie = JSON.parse(req.cookies['parse.session']);
return Parse._request('GET', 'users/me', {}, {sessionToken: cookie.sessionToken});
}).then(function (userData) {
userData.sessionToken = cookie.sessionToken;
req.user = Parse.Object.fromJSON(userData);
next();
}).then(null, function () {
res.clearCookie('parse.session');
return res.redirect('/login');
});
});
Now you can safely assume that if user reached Home.html, it will be a logged in user. Still I would not suggest putting HTML files in public folder, instead use a template engine.

How to Refresh Firebase Session Cookie

I'm developing a web application using Node.js/Express.js for the backend and I use Firebase for user authentication, and to manage user registration etc I use Firebase Admin SDK.
When a user want to login I sign him in using Firebase Client SDK like this:
// Handling User SignIn
$('#signin').on('click', function(e){
e.preventDefault();
let form = $('#signin-form'),
email = form.find('#email').val(),
pass = form.find('#password').val(),
errorWrapper = form.find('.error-wrapper');
if(email && pass){
firebase.auth().signInWithEmailAndPassword(email, pass)
.catch(err => {
showError(errorWrapper, err.code)
});
}else {
showError(errorWrapper, 'auth/required');
}
});
Below this code, I set an observer to watch for when the user successfully sign in, After a successfull sign in I get a Firebase ID token which I send to an endpoint on the server to exchange it for a session cookie that has the same claims the ID token since the later expires after 1 hour.
// POST to session login endpoint.
let postIdTokenToSessionLogin = function(url, idToken, csrfToken) {
return $.ajax({
type: 'POST',
url: url,
data: {
idToken: idToken,
csrfToken: csrfToken
},
contentType: 'application/x-www-form-urlencoded'
});
};
// Handling SignedIn Users
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
user.getIdToken().then(function(idToken) {
let csrfToken = getCookie('csrfToken');
return postIdTokenToSessionLogin('/auth/signin', idToken, csrfToken)
.then(() => {
location.href = '/dashboard';
}).catch(err => {
location.href = '/signin';
});
});
});
} else {
// No user is signed in.
}
});
Sign in endpoint on the server looks like this:
// Session signin endpoint.
router.post('/auth/signin', (req, res) => {
// Omitted Code...
firebase.auth().verifyIdToken(idToken).then(decodedClaims => {
return firebase.auth().createSessionCookie(idToken, {
expiresIn
});
}).then(sessionCookie => {
// Omitted Code...
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({
status: 'success'
}));
}).catch(err => {
res.status(401).send('UNAUTHORIZED REQUEST!');
});
});
I have created a middle ware to verify user session cookie before giving him access to protected content that looks like this:
function isAuthenticated(auth) {
return (req, res, next) => {
let sessionCookie = req.cookies.session || '';
firebase.auth().verifySessionCookie(sessionCookie, true).then(decodedClaims => {
if (auth) {
return res.redirect('/dashboard')
} else {
res.locals.user = decodedClaims;
next();
}
}).catch(err => {
if (auth) next();
else return res.redirect('/signin')
});
}
}
To show user information on the view I set the decoded claims on res.locals.user variable and pass it to the next middle ware where I render the view and passing that variable like this.
router.get('/', (req, res) => {
res.render('dashboard/settings', {
user: res.locals.user
});
});
So far everything is fine, now the problem comes after the user go to his dashboard to change his information (name and email), when he submits the form that has his name and email to an endpoint on the server I update his credentials using Firebase Admin SDK
// Handling User Profile Update
function settingsRouter(req, res) {
// Validate User Information ...
// Update User Info
let displayName = req.body.fullName,
email = req.body.email
let userRecord = {
email,
displayName
}
return updateUser(res.locals.user.sub, userRecord).then(userRecord => {
res.locals.user = userRecord;
return res.render('dashboard/settings', {
user: res.locals.user
});
}).catch(err => {
return res.status(422).render('dashboard/settings', {
user: res.locals.user
});
});
}
Now the view gets updated when the user submits the form because I set the res.locals.user variable to the new userRecord but once he refreshes the page the view shows the old credentials because before any get request for a protected content the middle ware isAuthenticated gets executed and the later gets user information from the session cookie which contains the old user credentials before he updated them.
So far these are the conclusions that I came to and what I tried to do:
If I want the view to render properly I should sign out and sign in again to get a new Firebase ID token to create a new session cookie which is not an option.
I tried to refresh the session cookie by creating a new ID token from the Admin SDK but it doesn't seem to have this option available and I can't do that through the client SDK because the user is already signed in.
Storing the ID token to use later in creating session cookies is not an option as they expire after 1 hour.
I Googled the hell out of this problem before posting here so any help is so much appreciated.
I am facing a very similar scenario with one of my apps. I think the answer lies in these clues.
From Firebase docs
Firebase Auth provides server-side session cookie management for traditional websites that rely on session cookies. This solution has several advantages over client-side short-lived ID tokens, which may require a redirect mechanism each time to update the session cookie on expiration:
So they're hinting here that you want to manage the session and it's lifetime from the server.
Second clue is in the docs
Assuming an application is using httpOnly server side cookies, sign in a user on the login page using the client SDKs. A Firebase ID token is generated, and the ID token is then sent via HTTP POST to a session login endpoint where, using the Admin SDK, a session cookie is generated. On success, the state should be cleared from the client side storage.
If you look at the example code, the even explicitly set persistence to None to clear state from the client using firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
So they are intending there to be no state on the client beyond the initial auth. They explicitly clear that state and expect an httponly cookie so the client can't grab the cookie (which really is just the ID token) and use it to get a new one.
It is odd that there is no clear way of refreshing the token client-side but there it is. You can only really create a session cookie with a super long lifetime and decide on the server when to delete the cookie or revoke the refresh token etc.
So that leaves the other option: manage state client-side. Some examples and tutorials simply send the ID token from the client to the server in a cookie. The satte sits on the client and the client can use the ID token to use all firebase features. The server can verify the user identity and use the token etc.
This scenario should work better. If the server needs to kick the user then it can delete the cookie revoke the refresh token (a bit harsh admittedly).
Hope that helps. Another scheme would be to build custom tokens, then you have complete control.

How to test sail.js controller with user logged in with passport.js

I try to unit test my sail.js controller that requires user to be logged in. My current test is:
var request = require('supertest'), // I try to use 'superagent', this changes nothing
user = request.agent;
describe('SomeController', function() {
// login user first of all
before(function(done) {
user(sails.hooks.http.app)
.post('/user/login')
.send({username: 'hellocat', password: '123456789'})
// request works well by itself,
// it does not return error and return normal response.
.end(function (err, res) {
// then I try to save cookies of logged in user to it agent
user(sails.hooks.http.app).saveCookies(res);
done();
});
});
// let test something
it('must return "ok" when user is logged in or "wtf" if opposite', function(done) {
user(sails.hooks.http.app)
.get('/user/me')
.end(function(err, res) {
console.log(res.body);
// no need to expect something, user is not logged in (
done();
});
});
});
Before hook works well and my user is logged in at the end of request. But in test it is not.
I lift sails application before all the test started to run. Similar tests that is not require authorization work well.
I guess the error is probably in cookie saving or wrong understanding of how to use supertest agents. What am I doing wrong?
Well, I finally found my error. request.agent function does not return singleton. So each call of user(sails.hooks.http.app) is actually new agent and new user. To make this test snippet work I should do something like this
var request = require('supertest'),
agent = request.agent;
And call agent once in describe section:
describe('SomeController', function() {
var user;
before(function(done) {
user = agent(sails.hooks.http.app);
user
.post('/user/login')
// ...
// ...

NodeJS Sessions

Hy and thanks in advance, So im working on a project, where part of the requirements, is to have a field where the user can insert is e-email, and then receive a custom url on his e-mail account, from there he can access the site. This is like the example of a "password-reset", where a custom url is generated and sent to u with a time-stamp validation. Im very new to nodejs, and my question here is if anyone has some type of guidlines to start doing this. My idea was to generate a sessionID, then generate a custom url, send the email, and then the users goes to the webpage. Im using express, and the whole site is already assembled, its just this feature tha is killing me! :(
I'm not sure what you need, but here is what I'd suggest (spoke about this on Reddit earlier).
// If you want the user to **have** to be a custom URL
req.param('userId', function(req, res, next) {
db.getUser(userId, function(e, usr) {
if (e) throw new Error(e);
req.user = usr;
});
});
req.all("*", function(req, res, next) {
if (!req.user) return res.redirect('/login');
});
// Rest of routes
req.get()
req.post()
That being said, you normally shouldn't have user login this way. Normally you would set the user in the session and validate it that way. Here's a good article on setting up sessions in Express.
If you do it this way, you would do something like this:
req.all("*", function(req, res, next) {
var userId = req.session('userid');
if (!userId) res.redirect('/login');
db.getUser(userId, function(e, usr) {
if (e) throw new Error(e);
if (!usr) return res.redirect('/login');
// Now the user is accessbile through its session
req.session.user = usr;
});
});
// Rest of routes
req.get()
req.post()
Password resetting requires what's called a nonce. This will be a small object in your DB that has a created date. You email them a link to your site that uses that nonce.
Whenever the route is hit that uses the password reset nonce, you look it up in the DB, verify it's new enough (within X hours or days), then give them access to the route that resets their password.
Your project requirements are pretty vague, so I'm not exactly sure what you need.
As of my understanding you want how to implement password reset functionality using expressjs.
Here are steps
1.
for forgot password
create an API
app.post('/forgotpassword',function(req,res){
var email = req.body.email;
// validation
// your custom url creation logic here
// someurl/:resetpasswordtoken
// store this in user data
// for this I'll suggest to use any unique string creation logic*
}
to show reset password page
app.get('/someurl/:resetpasswordtoken, function(req,res) {
//check token exist
// render the page for reset like current passwor,new password
}
Action for reset password
app.post('/someurl/:resetpasswordtoken , function(req,res){
var currentPwd = //get current pwd
var newPwd = //get new pwd
//check token
//then reset password for user
}

Categories

Resources