12

I'm trying to set up a web server using express. To access this server, users have to authenticate and for that, I use the basicAuth() middleware provided by Express. It works perfectly, except that I do not know how to log out once I logged in ! I have to close my browser to disconnect, but instead I would like to have a "disconnect" page which would redirect towards the "login" page (this one with the hideous form used to log in...).

Does anyone has an idea ?

Thanks per advance

PS : I apologize for my pathetic English :)

Sara
  • 469
  • 2
  • 8
  • 24
  • [This StackOverflow](http://stackoverflow.com/questions/7990890/how-to-implement-login-auth-in-node-js) question has a logout example The most simple answer is "update the req.session variable on logout". – Matthew Bakaitis May 03 '13 at 14:33
  • take a look at the passport http://passportjs.org/ module along with passport-local https://github.com/jaredhanson/passport-local. It makes log-out easy by calling req.logout(). Passport also makes it easy to add other authentication methods in the future if you want – Noah May 03 '13 at 15:32

4 Answers4

24

Express' basicAuth uses HTTP Basic Authentication whose implementation doesn't need HTML pages, cookies nor session ids. Its main drawbacks are its not secure and, our concern here, there is no mechanism in the spec for the server to instruct the browser to log out.

express.basicAuth() calls require(blah-blah/connect/lib/utils).unauthorized() which sends a 401 status with header 'WWW-Authenticate: Basic realm="..."'. The browser handles the authentication window and from then on sends a header like 'Authorization: Basic YmFzaWM6YmFzaWM=' which contains the username and password.

(express.basicAuth is not secure, especially over HTTP, because you can get the username:password with

new Buffer('YmFzaWM6YmFzaWM=' , 'base64').toString() 

which gives basic:basic in this case.)

Our problem is the HTTP spec does not provide a way to stop that header being sent. A workaround for HTTPS is to redirect the user to a URL on the same domain having incorrect credentials.

The HTTP workaround I use for Express V3 can be used with app.use(express.basicAuth(...)). I find this easier to use than other solutions which require a call to middleware in every secured route, e.g. app.get('/secure', checkAuth, function (req, res) {...}).

Here is a working example.

var express = require('express'),
    http = require('http'),
    app = express();

app.use(express.favicon()); // prevent interference during test
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: 'winter is coming' }));

app.use(function (req, res, next) {
  if (!req.session.authStatus || 'loggedOut' === req.session.authStatus) {
    req.session.authStatus = 'loggingIn';

    // cause Express to issue 401 status so browser asks for authentication
    req.user = false;
    req.remoteUser = false;
    if (req.headers && req.headers.authorization) { delete req.headers.authorization; }
  }
  next();
});
app.use(express.basicAuth(function(user, pass, callback) {
  callback(null, user === 'basic' && pass === 'basic');
}, '***** Enter user= basic & password= basic'));
app.use(function (req, res, next) {
  req.session.authStatus = 'loggedIn';
  next();
});

app.use(app.router);
app.get('/secure', function (req, res) {
  res.send([
    'You are on a secured page.',
    '<br>',
    '<a href="./secure">Refresh this page without having to log in again.</a>',
    '&lt;br/>',
    '<a href="./logout">Log out.</a>'
  ].join(''));
});
app.get('/logout', function (req, res) {
  delete req.session.authStatus;
  res.send([
    'You are now logged out.',
    '&lt;br/>',
    '<a href="./secure">Return to the secure page. You will have to log in again.</a>',
  ].join(''));
});

http.createServer(app).listen(3000, function(){
  console.log('Express server listening on port 3000. Point browser to route /secure');
});

P.S. Your English is excellent.

JohnSz
  • 2,049
  • 18
  • 17
11

For express.js 4 and basicAuth, you can use this method:

app.get('/logout', function (req, res) {
    res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
    return res.sendStatus(401);
});
Cristik
  • 30,989
  • 25
  • 91
  • 127
wyllman
  • 119
  • 1
  • 2
1

Adding to wyllman, the 401 code is Unauthorized. You can simply respond with res.status(401).send('Logged out')

or

app.get('/logout', function (req, res) {
    res.status(401).send('Logged out')
    //or res.status(401).end() if you don't want to send any message
});
Kevin Potgieter
  • 672
  • 10
  • 23
0

I've confirmed that redirecting the user to a /logout page with an http code 401 and html with <a> element links to /login works.

noreply
  • 867
  • 6
  • 5