HTTP Authentication in Node.js

Colin Ihrig
Colin Ihrig
Share

Last week, in Creating a HTTP Server in Node.js, I covered the basics of HTTP in Node.js. Today’s article will show you how to password protect your Node.js site using HTTP authentication. We’ll start by looking at basic access authentication, and then move on to the more secure digest access authentication.

Basic Access Authentication

When a user visits a site that implements authentication, he/she is prompted for a username and password. If the user provides valid credentials they are taken to the page’s content, otherwise they are rejected with a “401 Unauthorized” response. The simplest type of HTTP authentication is basic access authentication.

The Password File

On the server side, all of the usernames and encrypted passwords are stored in a password file. The Node.js utility, htpasswd can be used to manage the password file. To install htpasswd
, use the command shown below. npm stands for Node.js Package Manager, and it is installed by default with Node.js. npm is used to install Node.js modules. The -g flag installs the package globally, meaning that it is included in the system’s PATH
variable.
npm install -g htpasswd
Once htpasswd is installed, you can create new users using the command shown below. This example creates a new password file named “htpasswd” using the -c flag. In the new file, a user named “foo” is added. The -b flag allows the password, “bar”, to be specified as part of the command line.
htpasswd -bc htpasswd foo bar
After running the command, open your “htpasswd” file. The password file entry for the user “foo” is shown below. This line contains the username and encrypted password. Since this is the first and only user in the file, this should be the only line in the file.
foo:{SHA}Ys23Ag/5IOWqZCw9QGaVDdHwH00=

Node.js Incorporation

The next step is to add authentication support to our HTTP server. First, you will need to install the http-auth module using the following npm command.
npm install http-auth
Next, create a new file named “basic_auth_server.js”, and add the code shown below. Notice that the http-auth module is referenced on line 2. On lines 3 through 7, a configuration object is passed to the authentication module. The authRealm field defines an authentication realm. The authFile field points to the password file we created earlier. __dirname refers to the directory that the currently executing script resides in. This example assumes that the “htpasswd” file is in the same directory as “basic_auth_server.js”. The authType
configuration field indicates the type of authentication to use. On line 9, the basic authentication scheme is applied to the HTTP connection. The authentication callback function provides the authenticated username for further processing.
var http = require("http");
var auth = require("http-auth");
var basic = auth({
  authRealm: "Private area",
  authFile: __dirname + "/htpasswd",
  authType: "basic"
});
var server = http.createServer(function(request, response) {
  basic.apply(request, response, function(username) {
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Hello " + username);
    response.end();
  });
});

server.listen(80);
console.log("Server is listening");
Finally, start the server. You can connect to the server by navigating to http://localhost. You will be prompted for a username and password. Provide the credentials you created earlier, and the browser will respond by greeting you by name.

Limitations

The biggest shortcoming of basic access authentication is the fact that credentials are sent over the network as plaintext. This type of authentication should only be used with secure (i.e. HTTPS) connections in order to prevent eavesdropping. If a secure connection is not available, a more secure form of authentication should be used instead.

Digest Access Authentication

Digest access authentication
is a more secure alternative to basic authentication. With digest authentication, passwords are encrypted prior to network transmission.

The Password File

Digest authentication also uses a password file. However, the format of the file is slightly different from the one used for basic authentication. In order to work with the digest password file format, we will use a different utility named htdigest. Install htdigest using the following npm command.
npm install -g htdigest
Next, create a new password file using the command shown below. Again, the -c flag is used to create a new password file named “htpasswd”. This time we must also specify an authentication realm. In this case, the authentication realm is “Private area”. In this example, the username is “foo” again. Notice that the password is not provided in the command. Once you enter the command, you will be prompted to provide the password.
htdigest -c htpasswd "Private area" foo
After running htdigest, look inside the new “htpasswd” file. The entry for “foo” is shown below. The digest authentication file contains the username and encrypted password, as well as the authentication realm, which was not included in the basic authentication file.
foo:Private area:b8e1b1c08abcd38173a7dba3ad93a0c3

Node.js Incorporation

To incorporate digest authentication into our server, we will use the http-auth module again. If you’ve been following along with this tutorial, the module should already be installed on your machine. Next, create a new file named “digest_auth_server.js” to implement your server. The server code is shown below. Notice that the server code is nearly identical to the basic authentication server code. The difference lies in the authType field of the configuration object. In this case, authType has been set to "digest". This server can be accessed in the same fashion as the basic authentication server.
var http = require("http");
var auth = require("http-auth");
var digest = auth({
  authRealm: "Private area",
  authFile: __dirname + "/htpasswd",
  authType: "digest"
});
var server = http.createServer(function(request, response) {
  digest.apply(request, response, function(username) {
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Hello " + username);
    response.end();
  });
});

server.listen(80);
console.log("Server is listening");

Conclusion

This article has covered the basics of HTTP authentication. By following the examples provided here, your Node.js applications can be a little more secure. However, you should be aware that authentication alone is not enough. If security is a primary concern, your site should be served over HTTPS. In future articles, I’ll explore HTTPS and many other awesome Node.js features.

If you’ve enjoyed this post, you’re going to want to learn all about SitePoint’s newest series of print and ebooks, Jump Start. The first title is Node.js by Don Nguyen — find out more at SitePoint!

Frequently Asked Questions (FAQs) on HTTP Authentication in Node.js

How can I implement HTTP authentication in Node.js using Express.js?

Express.js is a popular web application framework for Node.js. To implement HTTP authentication in Node.js using Express.js, you can use the express-basic-auth middleware. First, install it using npm install express-basic-auth. Then, you can use it in your application like this:

const express = require('express');
const basicAuth = require('express-basic-auth');

const app = express();

app.use(basicAuth({
users: { 'admin': 'supersecret' }
}));

app.get('/', (req, res) => {
res.send('You are authenticated!');
});

app.listen(3000);
In this example, the username is ‘admin’ and the password is ‘supersecret’. You can replace these with your own credentials.

How can I secure my Node.js application using HTTP authentication?

HTTP authentication is a simple way to secure your Node.js application. It works by requiring a username and password to access certain routes in your application. This can be done using the http-auth module. First, install it using npm install http-auth. Then, you can use it in your application like this:

const http = require('http');
const auth = require('http-auth');

const basic = auth.basic({
realm: 'Private Area',
file: __dirname + '/.htpasswd' // Path to users file
});

const server = http.createServer(basic, (req, res) => {
res.end('You are authenticated!');
});

server.listen(3000);
In this example, the .htpasswd file contains the usernames and passwords of authorized users. You can create this file using an online .htpasswd generator.

How can I handle multiple users with HTTP authentication in Node.js?

To handle multiple users with HTTP authentication in Node.js, you can use a file or a database to store the usernames and passwords. The http-auth module supports this feature. You can specify a file or a database in the options when creating the basic auth object. Here is an example using a file:

const basic = auth.basic({
realm: 'Private Area',
file: __dirname + '/.htpasswd' // Path to users file
});
In this example, the .htpasswd file contains the usernames and passwords of authorized users. Each line in the file represents a user. The username and password are separated by a colon (:), and the password is hashed for security.

How can I customize the HTTP authentication prompt in Node.js?

The HTTP authentication prompt can be customized by setting the realm option when creating the basic auth object. The realm is a string that is displayed in the authentication dialog. Here is an example:

const basic = auth.basic({
realm: 'My Application'
});
In this example, the authentication dialog will display ‘My Application’ as the realm.

How can I handle authentication failure in Node.js?

When a user fails to authenticate, the server sends a 401 Unauthorized response. You can customize this response by setting the unauthorizedResponse option when creating the basic auth object. Here is an example:

const basic = auth.basic({
realm: 'Private Area',
unauthorizedResponse: 'Access denied'
});
In this example, the server will send ‘Access denied’ as the response body when a user fails to authenticate.

How can I use HTTP authentication with HTTPS in Node.js?

HTTP authentication can be used with HTTPS to provide an extra layer of security. To do this, you need to create an HTTPS server instead of an HTTP server. Here is an example:

const https = require('https');
const auth = require('http-auth');
const fs = require('fs');

const basic = auth.basic({
realm: 'Private Area'
});

const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};

const server = https.createServer(options, basic, (req, res) => {
res.end('You are authenticated!');
});

server.listen(3000);
In this example, key.pem and cert.pem are the private key and certificate files for HTTPS. You can generate these files using OpenSSL.

How can I use HTTP authentication with cookies in Node.js?

HTTP authentication can be used with cookies to remember authenticated users. To do this, you can set a cookie in the response after a successful authentication. Here is an example:

const server = http.createServer(basic, (req, res) => {
res.setHeader('Set-Cookie', 'auth=1');
res.end('You are authenticated!');
});
In this example, the server sets a cookie named ‘auth’ with a value of ‘1’ after a successful authentication. You can check this cookie in subsequent requests to remember the authenticated user.

How can I use HTTP authentication with sessions in Node.js?

HTTP authentication can be used with sessions to remember authenticated users across multiple requests. To do this, you can use a session middleware like express-session. Here is an example:

const express = require('express');
const basicAuth = require('express-basic-auth');
const session = require('express-session');

const app = express();

app.use(session({
secret: 'my secret',
resave: false,
saveUninitialized: true
}));

app.use(basicAuth({
users: { 'admin': 'supersecret' },
challenge: true,
authorizeAsync: true,
authorizer: (username, password, callback) => {
const userMatches = basicAuth.safeCompare(username, 'admin');
const passwordMatches = basicAuth.safeCompare(password, 'supersecret');
callback(userMatches & passwordMatches);
}
}));

app.get('/', (req, res) => {
if (req.session.authenticated) {
res.send('You are authenticated!');
} else {
req.session.authenticated = true;
res.send('You are now authenticated!');
}
});

app.listen(3000);
In this example, the server uses a session to remember the authenticated user. The session is created after a successful authentication and is stored in the req.session object.

How can I use HTTP authentication with JSON Web Tokens (JWT) in Node.js?

HTTP authentication can be used with JSON Web Tokens (JWT) to secure API endpoints. To do this, you can use a JWT middleware like express-jwt. Here is an example:

const express = require('express');
const jwt = require('express-jwt');
const jsonwebtoken = require('jsonwebtoken');

const app = express();

app.use(jwt({
secret: 'my secret',
algorithms: ['HS256']
}));

app.get('/', (req, res) => {
const token = jsonwebtoken.sign({ user: 'admin' }, 'my secret', { expiresIn: '1h' });
res.send(`Your token is ${token}`);
});

app.listen(3000);
In this example, the server uses a JWT to secure the ‘/’ endpoint. The JWT is signed with a secret and expires in 1 hour. The client must send this token in the Authorization header of the request.

How can I use HTTP authentication with OAuth in Node.js?

HTTP authentication can be used with OAuth to authenticate users with external services like Google, Facebook, and Twitter. To do this, you can use an OAuth middleware like passport. Here is an example:

const express = require('express');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

const app = express();

passport.use(new GoogleStrategy({
clientID: 'your-client-id',
clientSecret: 'your-client-secret',
callbackURL: 'http://localhost:3000/auth/google/callback'
}, (accessToken, refreshToken, profile, done) => {
done(null, profile);
}));

app.get('/auth/google', passport.authenticate('google', { scope: ['profile'] }));

app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => {
res.redirect('/');
});

app.listen(3000);
In this example, the server uses Google’s OAuth 2.0 strategy to authenticate users. The client must redirect the user to ‘/auth/google’ to start the authentication process. After a successful authentication, Google redirects the user to ‘/auth/google/callback’ with an authorization code. The server exchanges this code for an access token and a refresh token, and calls the verify callback with the user’s profile.