Search for blog posts, documentation, or pages

Documentation

Duo Web

Duo Web makes it easy to add strong two-factor authentication to your web application. Client libraries are available for Python, Ruby, Classic ASP, ASP.NET, Java, PHP, Node.js, ColdFusion, and Perl.

Walkthrough Video

Overview

Implementing Duo two-factor authentication into your site involves simply adding a second login page and splitting your login handler into two parts. You should be familiar with your web application’s programming language and authentication process.

For example, a typical single factor login process looks something like this:

Duo Web Flow Before

After adding two-factor authentication it will look more like this:

Duo Web Flow After

There are three things you need to do to set this up: call sign_request(), add the JavaScript and IFRAME, and then call verify_response().

First Steps

Before starting:

  1. Sign up for a Duo account (free for < 10 users!)
  2. Log in to the Duo Admin Panel and create a new Web SDK application to get an integration key, secret key, and API hostname. (See Getting Started for help.)
  3. Download and install a supported client library (Python, Ruby, Classic ASP, ASP.NET, Java, PHP, Node.js, ColdFusion, Perl).
  4. Use NTP to ensure that your server’s time is correct.

Instructions

1. Generate an akey

Your integration secret key (or akey) is a string that you should generate and keep secret from Duo. It should be at least 40 characters long and stored alongside your integration key and secret key in a configuration file.

You can generate a random string in Python with:

import os, hashlib
print hashlib.sha1(os.urandom(32)).hexdigest()

2. Call sign_request()

After you perform primary authentication (e.g. look up a user’s username and password in your database), you should call sign_request() which initializes the secondary authentication process.

sign_request() takes your integration key (ikey), secret key (skey), integration secret key (akey), and the username of the user who just successfully completed primary authentication. (If users can change their usernames, you’ll probably want to use something that won’t change, like an email address or primary key.)

For example, in Python:

sig_request = sign_request(ikey, skey, akey, username)
Note

sign_request() performs a HMAC-SHA1 of the username, integration key, and an expiration timestamp, using the application’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.

3. Show the IFRAME

After generating the signed request, your server should now display a second page that will contain the Duo IFRAME used for secondary authentication.

Duo’s JavaScript handles the setup and communication between the IFRAME, the user, and your server. First, you will need to include a short snippet of JavaScript in the page.

<script src="/path/to/Duo-Web-v1.js"></script>
<script>
  Duo.init({
    'host': 'host',
    'sig_request': 'sig_request',
    'post_action': 'post_action'
  });
</script>
Note

Duo-Web-v1.js relies on jQuery. Use Duo-Web-v1.bundled.js if you’re not using jQuery in your application.

Then, you will need to include an IFRAME on the page with an id of duo_iframe. This is where the secondary authentication prompt will appear.

You may specify width and height attributes directly on the IFRAME tag. This is the simplest way to display the frame, but it may not fit on mobile devices. For example:

<iframe id="duo_iframe" width="620" height="330" frameborder="0"></iframe>
Note

The height property of the IFRAME should be 500 if you’re using classic enrollment.

If you would like the frame to fit on smaller screen devices, like phones and tablets, you should use CSS to set the frame’s dimensions:

<iframe id="duo_iframe" frameborder="0"></iframe>
<style>
  #duo_iframe {
    width: 100%;
    min-width: 304px;
    max-width: 620px;
    height: 330px;
  }
</style>

To make sure the page’s width and zoom is set correctly for smaller screen devices, you may want to add a viewport meta tag to your page’s header:

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  ...
</head>

Duo.init() takes the following options:

host Your API hostname (i.e. api-XXXXXXXX.duosecurity.com
sig_request The signed request generated by sign_request()
post_action The server-side URI of where the secondary authentication results (the signed response) should be POSTed to

To ensure that Internet Explorer renders the page in standards mode, add this meta tag to the top of your HTML <head>.

<meta http-equiv="X-UA-Compatible" content="IE=edge">

When this page loads, the JavaScript snippet will set up the IFRAME, prompt the user for secondary authentication, and POST back the results to your server.

4. Call verify_response()

After the user authenticates (e.g. via phone call, SMS passcode, etc.) the IFRAME will generate a signed response called sig_response and POST it back to the post_action URL. Your server-side code should then call verify_response() to verify that the signed response is legitimate.

verify_response() takes your integration key (ikey), secret key (skey), integration secret key (akey), and the signed response as inputs. It will return the username of the authenticated user if the response is valid, or null (None in Python, Nil in Ruby, etc.) if the response is invalid.

For example, in Python:

sig_response = self.get_argument("sig_response") # for example (if using Tornado: http://www.tornadoweb.org/en/stable/documentation.html)
authenticated_username = verify_response(ikey, skey, akey, sig_response)
if authenticated_username:
  log_user_in(authenticated_username)

After ensuring that the username returned by verify_response() is not null, your application can set whatever identifier is necessary (e.g. cookie, session state, etc.) to indicate that the user has successfully authenticated. If your application preserves state and you know the login username, you should verify that authenticated_username returned from verify_response() matches that username before proceeding.

Note

verify_response() verifies the HMAC-SHA1 signature on the signed response to ensure it was properly signed by Duo and not modified by the client in any way.

Appendices

Duo.init() arguments

Argument Value Required? Default
host Your API hostname (i.e. api-XXXXXXXX.duosecurity.com Required
sig_request The signed request generated by sign_request() Required
post_action The URL to POST the signed response to Optional “” (i.e. post back to same page, like a form with an empty action)
post_argument The argument for signed response in post_action Optional sig_response

Passing additional POST arguments with the signed response

Duo’s JavaScript will pass additional arguments found in a duo_form form with the signed response. For example:

<form method="POST" id="duo_form">
 <input type="hidden" name="next" value="next" />
</form>

Note that while the signed response is protected from spoofing by its signature and expiration, you must provide any such protection for these additional arguments yourself.

Example: Duo for WordPress

Take a look at the duo_wordpress code for an example implementation of Duo Web.

Troubleshooting

Need some help? Try searching our Knowledge Base. For further assistance, contact Support.

Network Diagram

  1. Web Application or Service connection initiated
  2. Primary authentication
  3. Web Application or Service connection established to Duo Security over TCP port 443
  4. Secondary authentication via Duo Security’s service
  5. Web Application or Service receives authentication response
  6. Web Application or Service session logged in