Welcome to part 5 of the Beer Locker series
In our previous article we ended wtih a functional API capable of creating user accounts, locking down API endpoints, only allowing access to a user’s own beer locker, and an OAuth2 server.
Many readers have asked questions about how to use different authentication strategies so I am going to continue this series and delve into many of those strategies.
This article will explore the use of Digest authentication instead of Basic.
Digest
Like the Basic scheme, Digest uses a username and password to authenticate a user. The benefit it provides over Basic is that it uses a challenge-response paradigm to avoid sending the password in the clear.
Here is how it works:
- Client sends an unauthenticated request to a server.
- Server responds with a 401 “Unauthorized” reponse code along with a special code (called a nonce) and another string representing the authentication realm.
- Client responds with the nonce and an encrypted version of the username, password and realm.
- Server responds with a 200 OK and the response data if the authentication passes.
Update our Routes
There is something odd going on with the way in which we defined our routes in the original tutorials. In most cases it isn’t an issue but it is for Digest auth. When we tell Express to use our router, we were prefixing it with /api
. The problem is that Digest auth uses the URI as part of the scheme and the request object within Express is seeing it without the /api
portion of the URI. This will cause authentication to fail. You will need to update server.js
routes like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
Update our Auth Controller
The first thing we need to do is update our Auth Controller. Open controllers/auth.js
and implement the Digest strategy as follows. You can leave the Basic auth strategy implementation if you want. We will be updating the isAuthenticated
to use Digest instead of Basic.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
We have done 3 main things here.
First, we required the DigestStrategy provided by the passport-http
module.
Second, we told Passport to use DigestStrategy. Inside this we told it to use qop: 'auth'
which is quality of protection along with two anonymous functions. The first function does our verification of the user and if success we call the callback and supply the user along with their password. The second anonymous function can be used to protect against replay attacks. Nonces should be validated to make sure they are not used again. You can do this by storing issued nonces and removing them as they are used. This will increase the security of your authentication.
Third, we updated isAuthenticated
to use digest instead of basic.
Remove hashing of passwords :(
This is the part of using Digest and more specifically the implementation provided by the passport-http
module that I like the least. You cannot store the password as a hash. You need to get to the original password in order for the authentication to work. At the very least you will want to encrypt the password. I highly advise you analyze this type of approach for your application. If you must use Digest, you may want to consider implementing your own Digest strategy or updating the existing one where you don’t pass in the password in the success callback. You could instead pass in the MD5 hash of the username:realm:password
which you have pre calculated and stored.
So on to the very unsafe update to our User model. Again, for the tutorial we are going to store the password in plain text. You NEVER want to do this in a real situation. You will want to look into cryptography modules such as crypto
to do this.
Open up models/user.js
and update it to look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Testing it out
Fire up your trusted tool such as Postman and create a new user as the old ones will have hashed passwords which will not work.
I found using Postman to test Digest a bit unfriendly so I opted instead for curl.
Here is a command you can use to test your API using Digest:
1
|
|
Just change the username:password to whatever you used. This command is great in curl because it will issue the intial unauthenticated request, process the reponse along with the nonce, qop, realm, etc, and finally issue another authenticated request.
Here is what my test looked like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
|
Wrap up
You now have the tools needed to implement Digest authentication.
I still strongly caution against the use as it currently stands. I highly dislike and advise against storing passwords encrypted. It is only slightly better than plain text since it is encrypted, but it is only one step away from becoming plain text. The best is to implement your own Digest strategy so you can store your passwords in a way that they can be hashed.
If you have thoughts on this, please share them in the comments section.
I have a lot more tutorials coming so be sure to subscribe to my RSS feed or follow me on Twitter. Also, if there are certain topics you would like me to write on, feel free to leave comments and let me know.
Source code for this part can be found here on GitHub.