Two-Factor Authentication With Duo-Security
In my last article about two-factor authentication, I created a screencast that outlined how to use the Authy two-factor authentication system to easily improve the login security of your website. This time, we're going to look at another service by Duo Security which offers a solid security API, guides, and pre-built libraries that make it incredibly easy to get up and running quickly.
Just like Authy, you're going to need an application server to implement Duo since some aspects of it, specifically the secret keys, need to be defined in a non-accessible spot. For this tutorial, I'm using Duo's ColdFusion library, but they also offer libs for:
- Classic ASP
From a server-side perspective, they've definitely got good coverage.
Getting Your App Ready on Duo
The first thing you'll need to do is signup for an account. I'm not going to walk you through those steps since it should be self-explanatory, but I will say it was cool to see a free option available so I could create this tutorial at no cost. Yes, Duo charges for their service and honestly that's a good thing. You want to make sure a service that's helping you secure your site has funds to keep themselves in business for a bit.
Once you're signed up, you'll be prompted to setup a new integration. This just means that you need to setup the specifics of what you'd like to secure. And you'll see why I say, "what you'd like to secure" when you click on the Integration type dropdown. Duo allows you to setup a variety of security options, not just a website.
For our purposes, we'll be using the Web SDK" option. The Integration name is any semantic name you'd like to use to identify your site with.
After saving that information, you're presented with a settings page for your integration. This is where you'll fine tune how the authentication is supposed to function and get access to your integration, secret keys, and API hostname. These are critical to successfully working with Duo and should not be shared.
In addition, you'll need to create your own 40-character alphanumeric application key that will not be known to Duo and ensures greater control of your account and application. For example, I generated the following random key for my demo app:
Just note that it must be 40-characters long. Otherwise, you'll receive an error when you try to use it.
As you look through the settings, most are self-explanatory but there is a section called Policy which allows you to define when a user will be prompted for two-factor authentication. It's important to choose the best option for your app. From my experience, most sites tend to ask their users if they'd like to opt-in to the enhanced security. Two-factor auth can be cumbersome and some users just don't want to use it. For this scenario, I'm going to go with the Require Enrollment policy (which will ensure the Duo enrollment process isn't bypassed) and setting a flag in the user's database record when they've opted in. This allows users to login using your normal authentication scheme without being forced to authenticate via Duo.
In reality, that's really all I needed to setup in the Duo admin panel to make the service available to my app. So let's start adding in some code.
Adding Duo to My App
I want to reiterate that you'll need to build server-side code to really make this work and Duo has provided a broad range of libs for you to use.
The code I'm writing is CFML and I'll be using their ColdFusion component which manages all of the complexities of signing and encrypting my request as well as verifying the return value from the Duo API.
As I mentioned earlier, most two-factor activations are opt-in meaning that a user will go to their account settings, click on a link to turn on the service and go through a process of filling in relevant information to make things work. This generally involves providing the service a cell phone number and validating the settings based on a unique number sent either via text message or a phone call. Duo can offer users either option and also provides their own mobile app that can generate the passcode for users via their phone.
If you look at the screenshot below, you can see how I've tried to replicate a simple account screen with a prominent button below it, as a call-to-action for turning on the authentication:
When the user clicks it, a call is made to the Duo component to sign the request via the
<cfset session.sigReq = createObject("component","com.duoweb").signRequest( application.IKey, application.SKey, application.AKey, "firstname.lastname@example.org" ) />
To understand what this method does, I'd like to use a quote from the Duo site:
sign_request()performs a HMAC-SHA1 of the username, integration key, and an expiration timestamp, using the integration's secret key as the HMAC key. By generating this server-side and after primary authentication, Duo is assured that the user is indeed authorized to proceed to the secondary stage of authentication.
Basically, it's creating an encrypted request based on all of the keys for your integration, the unique 40-char application key you created, and the user's unique username. The end result looks something like this:
The signature gets stored in the variable
session.sigReq which is a persistent session-based variable that I can reference later. I check its value to ensure that a valid signature was passed back and if so, I can move on to the next page in the process.
The Duo IFRAME
<iframe id="duo_iframe" width="100%" height="500" frameborder="0"></iframe>
The method call is straightforward, taking three options:
- The API hostname that was defined for your integration.
- The signature request that we generated.
- The URL that Duo will post the results to once it's done doing its processing.
If you're confused by this,
<cfoutput>#session.sigReq#</cfoutput>, don't be. It's just ColdFusion's way of replacing a variable with its value.
At this point, the user will be presented with the Duo setup screen:
The user will need to enter a phone number and then choose whether they would like to receive their six-digit validation code via voice or text message. I tried both and they worked equally well. Duo does verification on their end to ensure the code being entered is valid.
Next, the user will be presented with a screen to download the Duo mobile app:
This is actually a good thing because having the mobile app will allow the user to get a code even if they have no cell service.
Once their successfully enrolled, they'll receive the page shown below and asked one more time to validate themselves:
For all intents, all of this process is in Duo's hands; you're just waiting for feedback.
That feedback will determine if the user has been properly setup and you'll need to use the
verifyResponse() method for that.
<cfset authUser = createObject("component","com.duoweb").verifyResponse( application.IKey, application.SKey, application.AKey, form.sig_response ) />
Like before, it takes all of the key variables and in this case, receives a response from Duo in the form of a posted variable called
sig_response. I've referenced it as
form.sig_response since that's how ColdFusion allows access to posted variables.
verifyResponse() method will take the signed response sent back by Duo and if all is well, will return the user's username for you to validate against your database. So in my case, I would expect that "email@example.com" would be returned. Once I've validated it, I then set the flag in the user's database record that would let me know they've opted into two-factor authentication.
That's it. That's all you need to do to setup users to activate two-factor authentication. Now let's shift to the login experience.
Logging in With Duo
You might expect something magical to happen from here, but interestingly enough, you can almost reuse the same exact code created for activating a user to allow them to login. I went ahead and created a very basic login page:
The page itself is just HTML markup. The important part is to first determine if the user has opted-in and that happens when you validate their normal site login information. Yes, you should still do your normal login validation of username and password. Duo's service is complementary to that, not a replacement.
By checking the database record, you should be able to determine if they've opted-in. If they haven't, then you'd only authenticate them using your normal username/password combination. If they have, then you're going to call the
signRequest() method, the same one we used when activating a new user:
<cfset session.sigReq = createObject("component","com.duoweb").signRequest( application.IKey, application.SKey, application.AKey, form.username ) />
Increasing the security of one's site is always a good thing. You want to make sure you protect your users as much as possible and using two-factor authentication is a big step in the right direction.
Duo offers a solid service with incredible ease and flexibility. While I only showed you their Web SDK, they also have a much more flexible API that gives you very granular control over most aspects of the process. While I recommend using the Web SDK, it's great knowing you have that power at your disposal. Hat's off to Duo for creating a great service.