The Duo Auth API is a low-level, RESTful API for adding strong two-factor authentication to your website or application. Check out the Two-Factor Authentication for SaaS Apps Solution Guide for an Auth API tutorial.
Duo's Auth API is included in the Duo Premier, Duo Advantage, and Duo Essentials plans.
Before starting:
Log in to the Duo Admin Panel and navigate to Applications.
Click Protect an Application and locate the entry for Auth API in the applications list. Click Protect to the far-right to configure the application and get your integration key, secret key, and API hostname. You'll need this information to complete your setup. See Protecting Applications for more information about protecting applications in Duo and additional application options.
Review the API Details to see how to construct your first API request. Adding Duo requires some understanding of your application's language and authentication process.
Duo Security also provides demonstration clients available on Github to call the Duo API methods. Examples are available in: Python, Java, C#, C, Go, Node, Swift, Ruby, Perl, and PHP.
Documented properties will not be removed within a stable version of the API. Once a given API endpoint is documented to return a given property, a property with that name will always appear (although certain properties may only appear under certain conditions, like if the customer is using a specific edition).
Properties that enumerate choices may gain new values at any time, e.g. the device platform
value could return new device platforms that did not previously exist. Duo will update our API documentation with new values in a timely fashion.
New, undocumented properties may also appear at any time. For instance, Duo may make available a beta feature involving extra information returned by an API endpoint. Until the property is documented here its format may change or it may even be entirely removed from our API.
Please note that all Unix timestamps are in seconds, except where noted.
This application communicates with Duo's service on SSL TCP port 443.
Firewall configurations that restrict outbound access to Duo's service with rules using destination IP addresses or IP address ranges aren't recommended, since these may change over time to maintain our service's high availability. If your organization requires IP-based rules, please review Duo Knowledge Base article 1337.
Effective June 30, 2023, Duo no longer supports TLS 1.0 or 1.1 connections or insecure TLS/SSL cipher suites. See Duo Knowledge Base article 7546 for additional guidance.
The /ping
endpoint acts as a "liveness check" that can be called to verify that Duo is up before trying to call other Auth API endpoints. Unlike the other endpoints, this one does not have to be signed with the Authorization header.
This endpoint is also suitable for use with Duo's v2 Web SDK to verify that Duo's service is responding before initializing frame authentication.
GET /auth/v2/ping
Parameters
None.
Response Codes
Response | Meaning |
---|---|
200 | Success. |
Response Format
Key | Value |
---|---|
time
|
Current server time. Formatted as a UNIX timestamp. Integer. |
Example Response
{
"stat": "OK",
"response": {
"time": 1357020061
}
}
The /check
endpoint can be called to verify that the Auth API integration and secret keys are valid, and that the signature is being generated properly.
This endpoint is also suitable for use with Duo's v2 Web SDK to verify integration information before initializing frame authentication.
GET /auth/v2/check
Parameters
None required.
Response Codes
Response | Meaning |
---|---|
200 | Success. |
401 |
The "Authorization" and/or "Date"
headers were missing or invalid.
|
Response Format
Key | Value |
---|---|
time
|
Current server time. Formatted as a UNIX timestamp. Integer. |
Example Response
{
"stat": "OK",
"response": {
"time": 1357020061
}
}
The /logo
endpoint provides a programmatic way to retrieve your stored logo.
GET /auth/v2/logo
Parameters
None required.
Response Codes
Response | Meaning |
---|---|
200 | Success. |
404 | No logo was found. |
Response Format
On success, the response body is Content-Type image/png, containing the logo.
On failure, the response is the standard error JSON.
The /enroll
endpoint provides a programmatic way to enroll new users with Duo two-factor authentication. It creates the user in Duo and returns a code (as a QR code) that Duo Mobile can scan with its built-in camera. Scanning the QR code adds the user's account to the app so that they receive and respond to Duo Push login requests.
POST /auth/v2/enroll
Parameters
Parameter | Required? | Description |
---|---|---|
username
|
Optional | Username for the created user. If not given, a random username will be assigned and returned. |
valid_secs
|
Optional | Seconds for which the activation code will remain valid. Default: 86400 (one day). |
Response Codes
Response | Meaning |
---|---|
200 | Success. |
400 |
Invalid or missing parameters, or a user with username already exists.
|
Response Format
Key | Value |
---|---|
activation_barcode
|
URL for an image of a scannable QR code with the activation code. |
activation_code
|
Code to enter into the Duo Mobile app to add the account. On phones with Duo Mobile already installed it will be a clickable link. |
expiration
|
Time at which this activation code will expire. Formatted as a UNIX timestamp. Integer. |
user_id
|
Permanent, unique identifier for the user in Duo. Always generated. |
username
|
Unique name for the user in Duo. Either specified as a parameter or auto-generated. |
Example Response
{
"stat": "OK",
"response": {
"activation_barcode": "https://api-eval.duosecurity.com/frame/qr?value=8LIRa5danrICkhHtkLxi-cKLu2DWzDYCmBwBHY2YzW5ZYnYaRxA",
"activation_code": "duo://8LIRa5danrICkhHtkLxi-cKLu2DWzDYCmBwBHY2YzW5ZYnYaRxA",
"expiration": 1357020061,
"user_id": "DU94SWSN4ADHHJHF2HXT",
"username": "49c6c3097adb386048c84354d82ea63d"
}
}
Check whether a user has completed enrollment.
POST /auth/v2/enroll_status
Parameters
Parameter | Required? | Description |
---|---|---|
user_id
|
Required | ID of the user. |
activation_code
|
Required |
Activation code, as returned from /enroll .
|
Response Codes
Response | Meaning |
---|---|
200 | Success. |
400 | Invalid or missing parameters. |
Response Format
One of the following strings:
Value | Meaning |
---|---|
success
|
The user successfully added the account to Duo Mobile. |
invalid
|
The code is expired or otherwise not valid for the specified user. |
waiting
|
The code has not been claimed yet. |
Example Response
{
"stat": "OK",
"response": "success"
}
The /preauth
endpoint determines whether a user is authorized to log in, and (if so) returns the user's available authentication factors.
POST /auth/v2/preauth
Parameters
Param | Required? | Description |
---|---|---|
user_id
|
Required if username is not specified
|
Permanent, unique identifier for the user as generated by Duo upon user creation (e.g. DUYHV6TJBC3O4RITS1WC). Exactly one of user_id or username must be specified.
|
username
|
Required if user_id is not specified
|
Unique identifier for the user that is commonly specified by your application during user creation (e.g. user@domain.com). This value may also represent a username alias assigned to a user Exactly one of user_id or username must be specified.
|
ipaddr
|
Optional | The IP address of the user to be authenticated, in dotted quad format. This will cause an "allow" response to be sent if appropriate for requests from a trusted network. |
hostname
|
Optional | The host name of the device accessing the application. |
trusted_device_token
|
Optional |
If the trusted_device_token is present and the Auth API application has an effective policy that enables Remembered Devices for each browser-based application, return an "allow" response for the lifetime of the token as set by the Duo administrator in the policy.
|
Response Codes
Response | Meaning |
---|---|
200 | Success. |
400 | 400 Invalid or missing parameters, or the specified username is not in a state that allows preauth to complete successfully |
Response Format
Key | Value | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
devices
|
A list of the user's devices, where each device is a series of key/value pairs. This field will only be present if
|
||||||||||||||||||||||||||||
enroll_portal_url
|
If This enrollment portal URL should be used instead of calling This field will only be present if |
||||||||||||||||||||||||||||
result
|
One of the following strings:
|
||||||||||||||||||||||||||||
status_msg
|
Human-readable message describing the result. This string is intended for display to the user. |
Example Responses
If result
was "auth"
:
{
"stat": "OK",
"response": {
"devices": [
{
"capabilities": [
"auto",
"push",
"sms",
"phone",
"mobile_otp"
],
"device": "DPFZRS9FB0D46QFTM891",
"display_name": "iOS (XXX-XXX-0100)",
"name": "",
"number": "XXX-XXX-0100",
"type": "phone"
},
{
"device": "DHEKH0JJIYC1LX3AZWO4",
"name": "0",
"type": "token"
}
],
"result": "auth",
"status_msg": "Account is active"
}
}
If result
was "enroll"
:
{
"stat": "OK",
"response": {
"enroll_portal_url": "https://api-abcd1234.duosecurity.com/portal?code=48bac5d9393fb2c2&akey=DIXXXXXXXXXXXXXXXXXX",
"result": "enroll",
"status_msg": "Enroll an authentication device to proceed"
}
}
If the result
was "allow"
because the request included a valid trusted_device_token
:
{
"stat": "OK",
"response": {
"result": "allow",
"status_msg": "Device is trusted."
},
}
The /auth
endpoint performs second-factor authentication for a user by sending a push notification to the user's smartphone app, verifying a passcode, or placing a phone call. It is also used to send the user a new batch of passcodes via SMS.
POST /auth/v2/auth
Parameters
Param | Required? | Description | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
user_id
|
Required if username is not specified
|
Permanent, unique identifier for the user as generated by Duo upon user creation (e.g. DUYHV6TJBC3O4RITS1WC). Exactly one of user_id or username must be specified.
|
||||||||||||
username
|
Required if user_id is not specified
|
Unique identifier for the user that is commonly specified by your application during user creation (e.g. user@domain.com). This value may also represent a username alias assigned to a user. Exactly one of user_id or username must be specified.
|
||||||||||||
factor
|
Required |
Factor to use for authentication. Currently, the following choices are supported:
Also see below for additional parameters that are necessary depending on the factor you specify. |
||||||||||||
ipaddr
|
Optional | The IP address of the user to be authenticated, in dotted quad format. This will cause an "allow" response to be sent if appropriate for requests from a trusted network. | ||||||||||||
hostname
|
Optional | The host name of the device accessing the application. | ||||||||||||
async
|
Optional |
If this parameter is not provided, then the If you enable |
Additionally, you will need to pass some factor
-specific parameters depending on the authentication method.
Duo Push
Parameter | Required? | Description |
---|---|---|
device
|
Required |
ID of the device. This device must have the You may also specify |
type
|
Optional |
This string is displayed in the Duo Mobile app push notification and UI. You may wish to specify some alternate phrase for this parameter. The default English string in Duo Mobile v4 is "Verify your identity" and "Are you logging in to" followed by the application's name in the push request notification text, and "Are you logging in to" followed by the application's name in the request details screen as shown in Duo Mobile. With In Duo Mobile v3, the default string shown is "Login Request" followed by the application's name in both the push notification text and on the request details screen. |
display_username
|
Optional | String to display in Duo Mobile in place of the user's Duo username. |
pushinfo
|
Optional |
A set of URL-encoded key/value pairs with additional contextual information associated with this authentication attempt. The Duo Mobile app will display this information to the user. For example: The URL-encoded string's total length must be less than 20,000 bytes. |
Passcode
Parameter | Required? | Description |
---|---|---|
passcode
|
Required | Passcode entered by the user. |
Phone callback
Parameter | Required? | Description |
---|---|---|
device
|
Required |
ID of the device to call. This device must have the You may also specify |
Sending new SMS passcodes
Parameter | Required? | Description |
---|---|---|
device
|
Required |
ID of the device to send passcodes to. This device must have the You may also specify |
The response will be returned in the container format described above. For successful responses, the payload will contain the following key/value pairs:
Response Codes
Response | Meaning |
---|---|
200 | Success. |
400 | Invalid or missing parameters. For example, the user does not exist or does not have any device capable of performing the requested authentication. |
Response formats
If async
was not enabled:
Key | Value | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
result
|
Either "allow" or "deny". If "allow" was returned, your application should grant access to the user. If "deny", it should not. | ||||||||||||||||||||||||
status
|
String detailing the progress or outcome of the authentication attempt.
|
||||||||||||||||||||||||
status_msg
|
Human-readable string describing the status of the authentication attempt. If the authentication attempt was denied, it may identify a reason. This string is intended for display to the user. | ||||||||||||||||||||||||
trusted_device_token
|
A string containing a token for that trusted device, which can be passed into the next /preauth call to bypass 2FA for the lifetime of the token. Requires an effective policy on the Auth API application that enables Remembered Devices per application, not for all applications.
|
If async
was enabled:
Key | Value |
---|---|
txid
|
A transaction ID to be used to query the authentication status using the /auth_status endpoint.
|
Example Responses
If async
was not enabled and the auth attempt succeeded:
{
"stat": "OK",
"response": {
"result": "allow",
"status": "allow",
"status_msg": "Success. Logging you in..."
}
}
If async
was not enabled and the auth attempt failed due to factor restriction as a result of Risk-based Factor Selection policy:
{
"stat": "OK",
"response": {
"result": "deny",
"status": "deny",
"status_msg": "Selected number is restricted from Duo Push authentication"
},
}
If async
was enabled:
{
"stat": "OK",
"response": {
"txid": "45f7c92b-f45f-4862-8545-e0f58e78075a"
}
}
If async
was not enabled and a Remembered Devices per-application policy was effective on the Auth API application:
{
"stat": "OK",
"response": {
"result": "allow",
"status": "allow",
"status_msg": "Success. Logging you in...",
"trusted_device_token": "REkxSzP00Ld4ddEVTRZOUlYMEl8RFVVQkdJ05HwUldRRThJR1VTNE0=||1627133735|8356ef7779bb0ec4c28ca9b04dc50493c4d2e05e"
},
}
The /auth_status
endpoint "long-polls" for the next status update from the authentication process for a given transaction. That is to say, if no status update is available at the time the request is sent, it will wait until there is an update before returning a response.
GET /auth/v2/auth_status
Parameters
Param | Required? | Description |
---|---|---|
txid
|
Required |
The transaction ID of the authentication attempt, as returned by the /auth endpoint.
|
Response Codes
Response | Meaning |
---|---|
200 | Success. |
400 | Invalid or missing parameters. |
Response Format
Key | Value | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
result
|
One of the following values:
|
||||||||||||||||||||||||
status
|
String detailing the progress or outcome of the authentication attempt.
|
||||||||||||||||||||||||
status_msg
|
Human-readable string describing the status of the authentication attempt. If the authentication attempt was denied, it may identify a reason. This string is intended for display to the user. | ||||||||||||||||||||||||
trusted_device_token
|
When /auth was called with async enabled, the value of trusted_device_token will be a string containing a token for that trusted device. This can be passed into the next /preauth call to bypass 2FA for the lifetime of the token. Requires an effective policy on the Auth API application that enables Remembered Devices
|
Example Response
{
"stat": "OK",
"response": {
"result": "waiting",
"status": "pushed",
"status_msg": "Pushed a login request to your phone..."
}
}
All API methods use your API hostname,
https://api-XXXXXXXX.duosecurity.com
. Obtain this value from the Duo Admin Panel and use it exactly as shown there.
Methods always use HTTPS. Unsecured HTTP is not supported.
All requests must have "Authorization" and "Date" headers.
If the request method is GET or DELETE, URL-encode parameters and send them in the URL query string like this: ?realname=First%20Last&username=root
. They still go on a separate line when creating the string to sign for an Authorization header.
Send parameters for POST requests in the body as URL-encoded key-value pairs (the same request format used by browsers to submit form data).
The header "Content-Type: application/x-www-form-urlencoded
" must also be present.
When URL-encoding, all bytes except ASCII letters, digits, underscore ("_"), period ("."), tilde ("~"), and hyphen ("-") are replaced by a percent sign ("%") followed by two hexadecimal digits containing the value of the byte. For example, a space is replaced with "%20" and an at-sign ("@") becomes "%40". Use only upper-case A through F for hexadecimal digits.
A request with parameters, as a complete URL, would look something like this: https://api-XXXXXXXX.duosecurity.com/admin/v1/users?realname=First%20Last&username=root
(substituting the actual API method path and parameters used in your request).
Responses are formatted as a JSON object with a top-level stat
key.
Successful responses will have a stat
value of "OK" and a
response
key. The response
will either
be a single object or a sequence of other JSON types, depending
on which endpoint is called.
{
"stat": "OK",
"response": {
"key": "value"
}
}
Values are returned as strings unless otherwise documented.
Unsuccessful responses will have a
stat
value of "FAIL", an integer
code
, and a
message
key that further describes the failure.
A message_detail
key may be present if additional information is available (like the specific parameter that caused the error).
{
"stat": "FAIL",
"code": 40002,
"message": "Invalid request parameters",
"message_detail": "username"
}
The HTTP response code will be the first three digits of the more
specific code
found inside the JSON object. Each
endpoint's documentation lists HTTP response codes it can return.
Additionally, all API endpoints that require a signed request can
return the following HTTP response codes:
Response | Meaning |
---|---|
200 | The request completed successfully. |
401 | The "Authorization", "Date", and/or "Content-Type" headers were missing or invalid. |
403 |
This integration is not authorized for this endpoint or the ikey was created for a different integration type (for example, using an Auth API ikey with Admin API endpoints). |
405 | The request's HTTP verb is not valid for this endpoint (for example, POST when only GET is supported). |
429 | The account has made too many requests of this type recently. Try again later. |
The API uses HTTP Basic Authentication to authenticate requests. Use your Duo application's integration key as the HTTP Username.
Generate the HTTP Password as an HMAC signature of the request. This will be different for each request and must be re-generated each time.
To construct the signature, first build an ASCII string from your request, using the following components:
Component | Description | Example |
---|---|---|
date
|
The current time, formatted as RFC 2822. This must be the same string as the "Date" header. |
Tue, 21 Aug 2012 17:29:18 -0000
|
method
|
The HTTP method (uppercase) |
POST
|
host
|
Your API hostname (lowercase) |
api-xxxxxxxx.duosecurity.com
|
path
|
The specific API method's path |
|
params
|
The URL-encoded list of If the request does not have any parameters one must still include a blank line in the string that is signed. Do not encode unreserved characters. Use upper-case hexadecimal digits A through F in escape sequences. |
An example realname=First%20Last&username=root
|
Then concatenate these components with (line feed) newlines. For example:
Tue, 21 Aug 2012 17:29:18 -0000
POST
api-xxxxxxxx.duosecurity.com
/auth/v2/auth
device=auto&factor=push&hostname=wks01&ipaddr=10.2.3.4&username=narroway
GET requests also use this five-line format (note the blank line as this request has no parameters):
Tue, 21 Aug 2012 17:29:18 -0000
GET
api-xxxxxxxx.duosecurity.com
/auth/v2/check
Lastly, compute the HMAC-SHA1 of this canonical representation, using your Duo application's secret key as the HMAC key. Send this signature as hexadecimal ASCII (i.e. not raw binary data). Use HTTP Basic Authentication for the request, using your integration key as the username and the HMAC-SHA1 signature as the password.
For example, here are the headers for the above POST request to api-XXXXXXXX.duosecurity.com/auth/v2/auth
, using DIWJ8X6AEYOR5OMC6TQ1
as the integration key and Zh5eGmUq9zpfQnyUIu5OL9iWoMMv5ZNmk3zLJ4Ep
as the secret key:
Date: Tue, 21 Aug 2012 17:29:18 -0000
Authorization: Basic RElXSjhYNkFFWU9SNU9NQzZUUTE6OGZlNGQzMTg2OTMyMTc1OTE0ZGQ4ZTk3YWU0YjhjZjNhZDQ2NTNmMQ==
Host: api-XXXXXXXX.duosecurity.com
Content-Length: 92
Content-Type: application/x-www-form-urlencoded
Separate HTTP request header lines with CRLF newlines.
The following Python function can be used to construct the "Authorization" and "Date" headers:
import base64, email.utils, hmac, hashlib, urllib
def sign(method, host, path, params, skey, ikey):
"""
Return HTTP Basic Authentication ("Authorization" and "Date") headers.
method, host, path: strings from request
params: dict of request parameters
skey: secret key
ikey: integration key
"""
# create canonical string
now = email.utils.formatdate()
canon = [now, method.upper(), host.lower(), path]
args = []
for key in sorted(params.keys()):
val = params[key].encode("utf-8")
args.append(
'%s=%s' % (urllib.parse.
quote(key, '~'), urllib.parse.quote(val, '~')))
canon.append('&'.join(args))
canon = '\n'.join(canon)
# sign canonical string
sig = hmac.new(bytes(skey, encoding='utf-8'),
bytes(canon, encoding='utf-8'),
hashlib.sha1)
auth = '%s:%s' % (ikey, sig.hexdigest())
# return headers
return {'Date': now, 'Authorization': 'Basic %s' % base64.b64encode(bytes(auth, encoding="utf-8")).decode()}
import base64, email, hmac, hashlib, urllib
def sign(method, host, path, params, skey, ikey):
"""
Return HTTP Basic Authentication ("Authorization" and "Date") headers.
method, host, path: strings from request
params: dict of request parameters
skey: secret key
ikey: integration key
"""
# create canonical string
now = email.Utils.formatdate()
canon = [now, method.upper(), host.lower(), path]
args = []
for key in sorted(params.keys()):
val = params[key]
if isinstance(val, unicode):
val = val.encode("utf-8")
args.append(
'%s=%s' % (urllib.quote(key, '~'), urllib.quote(val, '~')))
canon.append('&'.join(args))
canon = '\n'.join(canon)
# sign canonical string
sig = hmac.new(skey, canon, hashlib.sha1)
auth = '%s:%s' % (ikey, sig.hexdigest())
# return headers
return {'Date': now, 'Authorization': 'Basic %s' % base64.b64encode(auth)}
Need some help? Take a look at our Auth API Knowledge Base articles or Community discussions. For further assistance, contact Support.