Skip navigation

Duo Security is now a part of Cisco

About Cisco

Documentation

Two-Factor Authentication for SaaS Apps

Last Updated: April 3rd, 2019

Contents

Add two-factor authentication to your web application using Duo's Auth API.

Overview

This solution guide will help you use Duo's Auth API to offer two-factor authentication to the users of your SaaS application.

After deciding to opt-in for two-factor authentication, your users will install the Duo Mobile app on their iPhone/Android devices, and then use the application to authenticate after completing primary authentication.

This guide assumes that you'll store a two-factor authentication "status" for each user — for example, adding a "Require 2FA?" column to the users table in your database. You'll set this flag to "true" once the user enables Duo authentication.

Please contact us to request access to our APIs.

Python

The examples in this guide use the duo_client_python client library. Clone the repository (and set your Auth API application's IKEY, SKEY, and HOST as environment variables) if you want to copy and paste the example code.

Safeguard your skey!

The security of your Duo application is tied to the security of your skey. Treat it like a password. Store it in a secure manner with limited access, whether that is in a database, a file on disk, or another storage mechanism. Always transfer it via secure channels, and do not send it over unencrypted email, enter it into chat channels, or include it in other communications with Duo.

$ git clone https://github.com/duosecurity/duo_client_python
$ cd duo_client_python
$ export IKEY= # your Auth API application's "Integration key"
$ export SKEY= # your Auth API application's "Secret key"
$ export HOST= # your Auth API application's "API hostname"

Then call the /check endpoint to make sure everything is working:

$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST \
  --path /auth/v2/check --method GET
200 OK
{
  "response": {
    "time": 1361213173
  },
  "stat": "OK"
}

Enrollment

Before using Duo to authenticate a user, the user and an associated device must be enrolled in Duo's service.

When a new user — that is, a user Duo doesn't know about yet — wants to enable two-factor authentication, call /enroll. This returns the URL of a barcode image that contains a unique enrollment code that corresponds to your Duo instance and Auth API application. The user scans the barcode with Duo Mobile to enroll. The user will see a new entry appear Duo Mobile:

The name of the account comes from the name of your Duo account. Upload your logo using the Duo Admin Panel.

You can optionally pass the user's username to the /enroll endpoint. This stores the username in Duo's database so you can later authenticate the user with Duo by referencing this value. If you don't pass a username, store the returned user_id in your database and use that identifier in future API calls.

A call to /enroll looks like this:

$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST \
  --path /auth/v2/enroll --method POST username=david
200 OK
{
  "response": {
    "activation_barcode": "https://api-12345678.duosecurity.com/frame/qr?value=duo%3A%2F%2FbcQirraSbgdQEY3wMjqS-YXBpLWZpcnN0LnRlc3QuZHVvc2VjdXJpdHkuY29t",
    "activation_code": "duo://bcQirraSbgdQEY3wMjqS-YXBpLWZpcnN0LnRlc3QuZHVvc2VjdXJpdHkuY29t",
    "expiration": 1361290076,
    "user_id": "DUYWPFNY2VCCHD0SI5EC",
    "username": "david"
  },
  "stat": "OK"
}

The returned activation_barcode is the URL of a barcode image. Instruct your users to install Duo Mobile (for iPhone or Android) and then scan the barcode. Duo Mobile has a built-in camera and is designed to make this barcode scanning simple and easy. Your enrollment interface might look something like this:

To check if the user successfully scanned the barcode, call /enroll_status with the user's Duo user_id and the Duo activation_code.

$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST \
  --path /auth/v2/enroll_status --method POST user_id=DUYWPFNY2VCCHD0SI5EC \
  activation_code=duo://8LIRa5danrICkhHtkLxi-cKLu2DWzDYCmBwBHY2YzW5ZYnYaRxA
200 OK
{
  "response": "success",
  "stat": "OK"
}

Confirming that response is "success" is highly recommended to ensure that the user's authenticator is fully functional before requiring its use for authentication.

Once the Duo Mobile enrollment process is successful, we recommend presenting the user with backup authentication methods before requiring Duo for future logins. See Backup Methods for more information.

Once enrollment is complete, store this fact in your user database so you know to require two-factor authentication during future logins.

Authentication

Primary Authentication

Primary authentication should be handled by your application. Usually this means checking the user's username and password against your database.

If the user's primary credentials are correct, consult your database to see whether this user has enrolled for (and thus should be challenged with) second-factor authentication. If they haven't enrolled, either prompt them to enroll (if two-factor authentication isn't optional and you're relying on trust-on-first-use) or simply let them in.

If the user is enrolled and secondary authentication is necessary, the fun begins!

Secondary (Duo) Authentication

Call /preauth to get the user's enrollment status and device information from Duo. For example:

$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST \
  --path /auth/v2/preauth --method POST username=david
200 OK
{
  "response": {
    "devices": [
      {
        "capabilities": [
          "push"
        ],
        "device": "DPL8XE3Q1KDDH99KRRLS",
        "display_name": "Samsung Galaxy Nexus",
        "name": "",
        "number": "",
        "type": "phone"
      }
    ],
    "result": "auth",
    "status_msg": "Account is active"
  },
  "stat": "OK"
}

The result will be "auth" if the user is known to Duo, and devices will be a list of authentication devices (although in this case the list will only contain one device). If result is "enroll", see Enrollment. Also see /preauth documentation for other possible result values.

The capabilities array will contain "push" since both iPhone and Android support Duo Push. Note that "passcode" will never be returned in this array — submitting a passcode is always allowed.

After calling /preauth, you may want to show information about the authentication device and let the user choose how to authenticate. For example:

This interface is completely up to you. You can show all available authentication options or only allow one. You might Push automatically and then allow a passcode as a backup. You'll probably also want to show options for any backup methods you choose to implement.

Once you know which authentication method to use, call /auth, either to use Duo Push, call the device, or validate a passcode.

Duo Push

Duo Push is the most convenient method for users with an online smartphone. It sends a push notification to the user's smartphone, which invokes the Duo Mobile app and prompts the user to approve the login request.

The following example calls /auth, specifies push to be used, and requests async in order to later poll for status:

$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST \
  --path /auth/v2/auth --method POST username=david device=DPL8XE3Q1KDDH99KRRLS \
  factor=push ipaddr=141.211.144.186 async=1
200 OK
{
  "response": {
    "txid": "69ea7736-2041-4454-83c0-3a0fbb0564fc"
  },
  "stat": "OK"
}

Your application should long-poll using the /auth_status endpoint to check if the user has approved the authentication request. For example:

$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST \
  --path /auth/v2/auth_status --method GET txid=69ea7736-2041-4454-83c0-3a0fbb0564fc
200 OK
{
  "response": {
    "result": "waiting",
    "status": "pushed",
    "status_msg": "Pushed a login request to your phone..."
  },
  "stat": "OK"
}

Call the endpoint again and then approve the login request:

$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST \
  --path /auth/v2/auth_status --method GET txid=69ea7736-2041-4454-83c0-3a0fbb0564fc
200 OK
{
  "response": {
    "result": "allow",
    "status": "allow",
    "status_msg": "Success. Logging you in..."
  },
  "stat": "OK"
}

Only log the user in when result is "allow". See the /auth documentation to learn how to handle other responses.

Passcode

If the user enters a passcode, use /auth to validate it. This will return immediately so there's no reason to use async. Do not specify a device.

$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST \
  --path /auth/v2/auth --method POST username=david factor=passcode passcode=123456

If the passcode is valid the following is returned:

200 OK
{
  "stat": "OK",
  "response": {
    "result": "allow",
    "status": "allow",
    "status_msg": "Success. Logging you in..."
  }
}

See the /auth documentation to learn how to handle other responses.

Backup Methods

Once two-factor authentication is enabled for a user's account, the user will need the associated smartphone in order to authenticate. If the user's phone is unavailable the user will not be able to login. The Duo secrets are only stored on the specific hardware device used during activation, and by design they are not backed up anywhere — even if users back up their phone data using Apple or Android services. When a user upgrades to a new device an alternative authentication method will be required.

There are a number of different backup methods that can be considered. This section includes the option that Duo recommends.

Application-Generated Backup Code

Your application can generate a one-time use backup code, associate it with the user's account, and display it to the user. This puts your application completely in control of the backup method, entirely independent from Duo.

Best Practices

Usernames

If your service allows your users to change their usernames at any point (or if username privacy is a concern), you may want Duo to store anonymous identifiers instead of usernames. If you don't pass a username to /enroll it will return an anonymous identifier for you to store in your users database and use in future calls. You'll probably also want to use the display_username when calling /auth so that the user sees their actual username on the login request screen.

Remember This Browser

Your application may already support the ability for a user to stay logged in even after the user closes the browser. We recommend you consider offering your users the ability to remember that a specific browser was successfully used for two-factor authentication, and not to challenge for two-factor authentication again from that same browser. If the user decides to remember the browser for purposes of two-factor authentication then the application would set a second cookie to track this state.

This does reduce security in favor of usability. Users are only prompted to perform two-factor authentication when logging in from unknown browsers, or when their two-factor authentication cookie has expired. You can consider remembering a browser indefinitely or for a specified time period (30 days is common).

Troubleshooting

Need some help? Take a look at our Auth API Knowledge Base articles or Community discussions. For further assistance, contact Support.

Ready to Get Started?

Sign Up Free