Reputation: 501
I wrote a JS script for a webserver that includes authentication using the passport and the digest strategy. I am not using sessions, but I have tried using sessions and it does not change the results. The browser requests the "/login" route and displays a built-in login dialog. Authentication works fine, but I can't get the user to "logout." The problem seems to be that the browser remembers the login credentials and resends them automatically. The end result is that the user must close the browser completely to log out, but that is a problem for this application. I know that there must be a way to tell the browser not to do this, but I haven't figured it out.
I figured out a hack to get the browser to display the login dialog again; force the authentication function to return a false. However, I haven't figured out a way to do this per-session. Right now, if one person logs out, everyone gets logged out. It's not a workable solution.
Can anyone tell me what I'm doing wrong here? One thing I'm wondering is whether I'm returning the proper response to the browser when it POSTs to the /logout route (see end). I return res.json(""), but maybe there's a different response I should send to tell the browser to forget the credentials for the session?
My code follows. Any insight is greatly appreciated. Thank you in advance.
T
var passport = require('passport'),
DigestStrategy = require('passport-http').DigestStrategy;
var express = require('express');
var app = express();
app.configure(function () {
app.use(
"/", //the URL throught which you want to access to you static content
express.static('./www') //where your static content is located in your filesystem
);
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'keep moving forward' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
});
app.listen(80); //the port you want to use
/**
* CORS support.
*/
app.all('*', function(req, res, next){
if (!req.get('Origin')) return next();
// use "*" here to accept any origin
// For specific domain, do similar: http://localhost'
// Use an array for multiple domains, like [http://localhost', 'http://example.com' ]
res.set('Access-Control-Allow-Origin', '*' );
res.set('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Authorization');
next();
});
//
// Configure passport authentication
//
// Used to force the browser to display the login screen again.
var forceLogin = false;
passport.use(new DigestStrategy({ qop: 'auth' },
function(username, done ) {
if ( !forceLogin )
{
return done(null, username, "nimda");
}
else
{
//
// Forces the browser to request the user name again by returning a failure to its last request.
//
console.log ( "forcing user to log in" );
forceLogin = false;
return done(null, false);
}
));
passport.serializeUser(function(user, done) {
console.log( "serialize user " + user.toString() );
done(null, user.toString());
});
passport.deserializeUser(function(id, done) {
console.log( "deserialize user " + id.toString() );
done(null, id);
});
app.post('/login', passport.authenticate('digest', { session: true }),
function(req, res) {
console.log( "/login");
res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
res.json({ id: req.user.id, username: req.user.username });
});
app.post('/logout', function(req, res){
req.logOut();
// NOTE: Same results as req.logout
//
// req.session.destroy(function (err) {
// res.redirect('/');
// });
res.redirect("/");
// flag to force a login
forceLogin = true;
console.log( "logout");
// Is this the proper return to the browser?
return res.json("");
});
Upvotes: 9
Views: 18924
Reputation: 765
You could try the following
req.session.destroy()
req.logout()
res.redirect('/')
Saving the new state of the session is important if you're using some form a session store for persistent sessions (eg. Redis). I tried the above code with a similar setup and it seems to work for me
Upvotes: 7
Reputation: 401
A bit late to the party, but I found this thread on Google while searching for answer and nothing worked for me. Finally, I was able to solve it so thought I could post solution here for anyone that might read this in the future.
So basically, I use node.js, express, passport(local) and rethinkdb as my storage, and I was having problem with req.logout();
not logging me out.
My setup:
var express = require( 'express' );
var passport = require( 'passport' );
var session = require( 'express-session' );
var RDBStore = require( 'express-session-rethinkdb' )( session );
I tried a lot of stuff and nothing was working, so finally I decided to manually do it, by removing session record from database myself.
Here is the code I used: app.get( '/logout', function ( req, res, next ) {
if ( req.isUnauthenticated() ) {
// you are not even logged in, wtf
res.redirect( '/' );
return;
}
var sessionCookie = req.cookies['connect.sid'];
if ( ! sessionCookie ) {
// nothing to do here
res.redirect( '/' );
return;
}
var sessionId = sessionCookie.split( '.' )[0].replace( 's:', '' );
thinky.r.db( 'test' ).table( 'session' ).get( sessionId ).delete().run().then( function( result ) {
if ( ! result.deleted ) {
// we did not manage to find session for this user
res.redirect( '/' );
return;
}
req.logout();
res.redirect( '/' );
return;
});
});
So I hope this helps someone :)
Upvotes: 4
Reputation: 236
Server needs somehow notify web browser that he need to refresh login info (MD5 digests). For that you can send 401 error code and usually browser will show login popup message.
By the way, all these tricks with sessions are useless because browser already have all required info for automatic logging in.
So you can try next code:
req.logout();
res.send("logged out", 401);
Upvotes: 5
Reputation: 18956
res.redirect('/')
will end the response, you can not call subsequently write(), end(), send(), json() ...etc
Just like this:
app.post('/logout', function(req, res){
req.logOut();
res.redirect("/");
});
deserializeUser should return a user instead of id:
passport.deserializeUser(function(id, done) {
findUser(id, function(err, user) {
if(err) {
done(err)
} else {
done(null, user);
}
}
});
I have taken a short discussion, I realize that digest username/password store on browser (not on server), so req.logout not help, there no way to clear it from server. You just close browser and open again that mean logout.
You can use a variable in cookie or session to mark the session as logout, but I think we should not do not, because username/password still in browser.
It's digest!
Upvotes: 0