A major downside to basic HTTP authentication is that the login sticks around as long as the credentials are stored and is not very secure. A much better method is to implement your own authentication and store it in a session that you can expire whenever you want.
The session
middleware inside Express works very well for implementing session authentication. The session
middleware attaches a Session
object req.session
to the Request
object to provide the session functionality. Table 19.1 describers the methods you can call on the res.session
object.
Listing 19.9 shows how to implement session authentication using the crypto
module to generate secure passwords. The example is very rudimentary to keep it small enough for the book, but it contains the basic functionality so you can see how to implement session authentication.
Lines 6–9 encrypt the passwords by using the hasPW()
function. Notice that the listing uses the body-parser
, cookieParser
, and session
middleware. Lines 44 and 45 simulate getting a user
object from the database and comparing the stored password hash with the password hash from the request body. Lines 46–50 create the session. Notice that the regenerate()
function is used to regenerate a new session, and the callback function passed to regenerate()
sets the session.user
and session.success
properties of the session. If the authentication fails, then only the session.error
property is set for the session.
The /login
route in lines 29–41 displays a rudimentary login to get credentials. If session.error
is set, then it is also displayed on the login page. The /restricted
route in lines 14–23 checks the session to see if it has a valid user, and if it does, displays a success message; otherwise, session.error
is set, and the response is redirected to /login
.
The /logout
route in lines 24–28 calls destroy()
on the session to remove the authentication. You could also have other code destroy the session, based on a timeout, a number of requests, etc. Figure 19.5 shows the browser screens forcing a login and then displaying success.
01 var express = require('express'),
02 var bodyParser = require('body-parser'),
03 var cookieParser = require('cookie-parser'),
04 var session = require('express-session'),
05 var crypto = require('crypto'),
06 function hashPW(pwd){
07 return crypto.createHash('sha256').update(pwd).
08 digest('base64').toString();
09 }
10 var app = express();
11 app.use(bodyParser());
12 app.use(cookieParser('MAGICString'));
13 app.use(session());
14 app.get('/restricted', function(req, res){
15 if (req.session.user) {
16 res.send('<h2>'+ req.session.success + '</h2>' +
17 '<p>You have entered the restricted section<p><br>' +
18 ' <a href="/logout">logout</a>'),
19 } else {
20 req.session.error = 'Access denied!';
21 res.redirect('/login'),
22 }
23 });
24 app.get('/logout', function(req, res){
25 req.session.destroy(function(){
26 res.redirect('/login'),
27 });
28 });
29 app.get('/login', function(req, res){
30 var response = '<form method="POST">' +
31 'Username: <input type="text" name="username"><br>' +
32 'Password: <input type="password" name="password"><br>' +
33 '<input type="submit" value="Submit"></form>';
34 if(req.session.user){
35 res.redirect('/restricted'),
36 }else if(req.session.error){
37 response +='<h2>' + req.session.error + '<h2>';
38 }
39 res.type('html'),
40 res.send(response);
41 });
42 app.post('/login', function(req, res){
43 //user should be a lookup of req.body.username in database
44 var user = {name:req.body.username, password:hashPW("myPass")};
45 if (user.password === hashPW(req.body.password.toString())) {
46 req.session.regenerate(function(){
47 req.session.user = user;
48 req.session.success = 'Authenticated as ' + user.name;
49 res.redirect('/restricted'),
50 });
51 } else {
52 req.session.regenerate(function(){
53 req.session.error = 'Authentication failed.';
54 res.redirect('/restricted'),
55 });
56 res.redirect('/login'),
57 }
58 });
59 app.listen(80);