Digest Authentication using Node.js

Posted by: Mahesh Sabnis , on 6/30/2016, in Category Node.js
Views: 36136
Abstract: Using crypto module in Node.js to implement digest authentication in our application.

In a previous article, we saw how Basic Authentication is a quick way to protect your content. However a drawback of Basic Authentication is that when used over HTTP, the password is sent as plain text. A recommendation is to use Basic with HTTPS.

Another type of authentication is Digest Authentication where hash of the clients’ information is sent over a communication channel, thereby making it more secured when compared to Basic authentication, where the credentials are passed over the channel in a non-encrypted mode. When compared to basic authentication, digest authentication is more suitable for internet applications. This mechanism uses MD5 encryption to make secure login over HTTP communication channel.

 

In Node.js, for digest authentication we use the crypto module. This module offers a way of an encapsulating credentials passed over HTTP or HTTPS. This module uses createHash() function which returns an instance of the hash object. This object contains update() function to update the hash contents with the given data. The digest() function calculates the digest of all the passed data to be hashed.

Implementing  Digest Authentication in Node.js

This application is implemented using Visual Studio Code. This is a free new IDE for building and debugging modern Web and Cloud applications. This IDE can be downloaded from here. This application uses Node.js tools. They can be downloaded from here.

Step 1: Create a folder of name VSCodeDigestAuthentication on your disk. We will use this folder as a workspace for our application. Open Visual Studio Code, from the File menu open the VSCodeDigestAuthentication folder. Add a new folder of name Scripts in the VSCodeDigestAuthentication folder.

Step 2: To install Node.js intellisense for the current project, open the Node.js command prompt. Navigate to the VSCodeDigestAuthentication folder and run following commands:

npm install -g tsd

tsd query node --action install

Step 3: In the Scripts folder add a new JavaScript file of name app.js. Add the following code in app.js.

//1.
var http = require('http');

//2.
var crypt = require('crypto');

//3.
var credentials = {
    userName: 'mahesh',
    password: 'mahesh1234',
    realm: 'Digest Authenticatoin'
};

//3a.
var hash;

//4. 

function cryptoUsingMD5(data) {
    return crypt.createHash('md5').update(data).digest('hex');
}

//5.
hash = cryptoUsingMD5(credentials.realm);

//6.
function authenticateUser(res) {
    console.log({ 'WWW-Authenticate': 'Digest realm="' + credentials.realm + '",qop="auth",nonce="' + Math.random() + '",opaque="' + hash + '"' });
    res.writeHead(401, { 'WWW-Authenticate': 'Digest realm="' + credentials.realm + '",qop="auth",nonce="' + Math.random() + '",opaque="' + hash + '"' });
    res.end('Authorization is needed.');
}

//7.
function parseAuthenticationInfo(authData) {
    var authenticationObj = {};
    authData.split(', ').forEach(function (d) {
        d = d.split('=');

        authenticationObj[d[0]] = d[1].replace(/"/g, '');
    });
    console.log(JSON.stringify(authenticationObj));
    return authenticationObj;
}

//8. 
var server = http.createServer(function (request, response) {
    var authInfo, digestAuthObject = {};

    //9.
    if (!request.headers.authorization) {
        authenticateUser(response);
        return;
    }

    //10.
    authInfo = request.headers.authorization.replace(/^Digest /, '');
    authInfo = parseAuthenticationInfo(authInfo);

    //11.
    if (authInfo.username !== credentials.userName) {
        authenticateUser(response); return;
    }

    //12.
    digestAuthObject.ha1 = cryptoUsingMD5(authInfo.username + ':' + credentials.realm + ':' + credentials.password);

    //13.
    digestAuthObject.ha2 = cryptoUsingMD5(request.method + ':' + authInfo.uri);

    //14.
    var resp = cryptoUsingMD5([digestAuthObject.ha1, authInfo.nonce, authInfo.nc, authInfo.cnonce, authInfo.qop, digestAuthObject.ha2].join(':'));
   
    digestAuthObject.response = resp;

    //15.
    if (authInfo.response !== digestAuthObject.response) {
        authenticateUser(response); return;
    }
    
    response.end('Congratulations!!!! You are successfully authenticated');

});
//16.
server.listen(5050);

 

The above code does the following. (Note: The comments numbering set for the code matches with the following numbering.)

1. Load the http module so that we can create a web server.

2. Load crypto module. This provides functions to hash credentials sent by the client over http communication channel.

3. The JSON object used to store credential schema, e.g. userName and password.

a. The hash variable used to represent the string data specified by the server. This is the used to store the hash of the credentials information posted by the client.

4. The cryptoUsingMD5() function is used to generate the hash of the credentials passed by the client. This provides hex digest calculation of the data.

5. The hex information is stored in the hash variable.

6. The authenticateUser() function is used for authentication based on the values entered by the client. When the server sends the WWW-Authenticate header to the browser, it includes attributes like opaque, qop, nonce, etc. qop is the quality of protection attribute, this is set to the auth, and this means the browser must compute more secure MD5 hash. nonce is used to make the MD5 hash less predictable so that it can be prevented being decoded.

7. The parseAuthenticationInfo() function is used to parse the authentication data received, this contains credential information.

8. Create an http server with request listener. This provide the request object from which we will read header information and the authorization value.

9. If the header does not have authorization values then the authenticateUser() function will be called with the Authorization is needed message.

10. The object containing digest headers from the client is received and parsed to retrieve the credentials.

11. If the userName is not matching with the server side store (In our case we are using credentials, JSON object, ref Step 3), then do not generate crypto hash with MD5, just call authenticateUser() function which returns the Authorization is needed message.

12. Get the MD5 hash for the username, password, and realm separated by colon ‘:’.

13. Get the MD 5 hash for the Request method in our case ‘GET’ and the uri separated by colon ‘:’.

14. Generate the authenticated response by the browser by combining step 12 and step 13 hash. Here we are defining JavaScript array with hash, client nonce (cnonce) and using JavaScript join() method to separate these values by colon (‘:’). This generates the final response string. This response is passed to the digestAuthObject response.

15. If the username and password are correct and digest response generated is correct based on the received information, then the response with string “Congratulations!!! You are successfully Authenticated” is sent to the client else the ‘Authorization is needed’ message will be responded.

16. Start listening on port 5050.

Step 4: Right click on the app.js, and select the Open in Command Prompt option. Run the following command from the command prompt:

Node app

This will start listening on port 5050.

Step 5: Open any browser and enter following address

http://localhost:5050

This will provide the login Windows as shown in the following image :

nodejs-digest-login

Enter User name as mahesh and password as mahesh1234 and the following result will be displayed:

nodejs-authenticated

On the command prompt, we can see the digest string as shown in the following image

digest-string

Conclusion

Using the crypto module in Node.js we can implement digest authentication in our application. More information about the digest information can be read from the following link https://en.wikipedia.org/wiki/Digest_access_authentication

Download the entire source code of this article (Github)

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @maheshdotnet or connect with him on LinkedIn


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!