Contents
Add two-factor authentication and self-service user enrollment to your application using Duo's Auth API and your own user interface.
Overview
This solution guide will help you use Duo's Auth API to add two-factor authentication with your custom user interface to SaaS or on-premises applications.
As part of Duo's enrollment process, users will install the Duo Mobile app on their iOS or Android devices and activate it for use with our service, and then use the application to approve login verification requests 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.
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.
The security of your Duo application is tied to the security of your secret key (skey). Secure it as you would any sensitive credential. Don't share it with unauthorized individuals or email it to anyone under any circumstances!
$ 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 enroll 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 QR code that contains a unique enrollment code that corresponds to your Duo instance and Auth API application. The user scans the QR code with Duo Mobile to enroll. The user will see a new entry appear in 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=narroway
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": "narroway"
},
"stat": "OK"
}
The returned activation_barcode
is the URL of a QR code. Instruct your users to install Duo Mobile (for iPhone or Android) and then scan the QR code. Duo Mobile uses your phone's camera to scan the enrollment QR code, adding the account to Duo Mobile and activating it for Duo Push and Duo Mobile passcode generation. Your enrollment interface might look something like this:
To check if the user successfully scanned the QR code, 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.
When the user completes Duo enrollment, store this fact in your user database so you know to require two-factor authentication during future logins.
Authentication
Primary Authentication
Your application should continue to handle primary authentication in whatever manner you used before adding Duo. Usually this means checking the user's username and password against your database or identity store.
If the user's primary credentials are correct, consult your application's database to see whether this user has completed enrollment in (and thus should be challenged with) Duo authentication as a second factor. 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 to the application.
If your primary identity store indicates the user completed Duo enrollment and secondary authentication is necessary, proceed to Duo authentication via the API.
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=narroway
200 OK
{
"response": {
"devices": [
{
"capabilities": [
"auto",
"push",
"sms",
"phone",
"mobile_otp"
],
"device": "DPL8XE3Q1KDDH99KRRLS",
"display_name": "Google Pixel 5",
"name": "Pixel 5",
"number": "734-555-2233",
"type": "phone"
}
],
"result": "auth",
"status_msg": "Account is active"
},
"stat": "OK"
}
The result
will be "auth" if the user exists in 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", you can enter the Enrollment flow, to give the user the opportunity to register a device for two-factor authentication during the login process. 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 while "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 Android and iOS phones or tablets. It sends a push notification to the user's device, which invokes the Duo Mobile app and prompts the user to approve the login request.
The following example calls /auth
, specifies sending a push to the first capable device associated with the user, 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=narroway device=auto \
factor=push ipaddr=10.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=narroway 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.
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.