Duo Admin API Integration
7 minute read
Overview
Duo Security’s Admin API provides access to comprehensive audit logs across five different log types. This integration allows you to retrieve authentication events, administrator actions, telephony logs, user activity, and offline enrollment data for security monitoring and compliance.
The Duo Admin API uses HMAC-SHA1 signature authentication and supports both v1 and v2 API versions depending on the log type.
Endpoints
Endpoint | Description | API Version |
---|---|---|
Authentication Logs | User authentication attempts and results | v2 |
Administrator Logs | Administrative actions and config changes | v1 |
Telephony Logs | Phone-based authentication events | v2 |
Activity Logs | User activity and application usage | v2 |
Offline Enrollment | Offline device enrollment events | v1 |
Authentication Method
Duo uses HMAC-SHA1 signature authentication with the following components:
- Integration Key: Your Duo application integration key
- Secret Key: Your Duo application secret key
- API Hostname: Your Duo API hostname (e.g.,
api-xxxxxxxx.duosecurity.com
)
The HMAC signature is calculated using the HTTP method, hostname, path, parameters, and current timestamp.
Environment Variables
Set these environment variables for secure credential management:
# Duo Integration Key (from Admin Panel > Applications)
export DUO_INTEGRATION_KEY="DIXXXXXXXXXXXXXXXXXXXX"
# Duo Secret Key (from Admin Panel > Applications)
export DUO_SECRET_KEY="your_secret_key_here"
# Duo API Hostname (from Admin Panel > Applications)
export DUO_API_HOSTNAME="api-xxxxxxxx.duosecurity.com"
Configuration Examples
This section provides configurations for all five Duo Admin API log types with optimized pull intervals:
Authentication Logs (v2)
Monitor user authentication attempts and outcomes:
nodes:
- name: duo_authentication_logs
type: http_pull_input
endpoint: https://YOUR_API_HOSTNAME/admin/v2/logs/authentication
method: GET
pull_interval: 5m
header_expressions:
- header: "Date"
value_expression: FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700")
- header: "Accept"
value_expression: "application/json"
- header: "Authorization"
value_expression: Concat(["Basic ", EDXEncode(Concat([EDXEnv("DUO_INTEGRATION_KEY", ""), ":", EDXHmac(Format("%s\n%s\n%s\n%s\n%s", [FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700"), "GET", EDXEnv("DUO_API_HOSTNAME", ""), "/admin/v2/logs/authentication", Concat(["limit=1000&maxtime=", String(Int(UnixSeconds(Now())) * 1000), "&mintime=", String(Int(UnixSeconds(Now()) - 360) * 1000), "&sort=ts%3Adesc"], "")]), EDXEnv("DUO_SECRET_KEY", ""), "sha1", "hex")], ""), "base64", false)], "")
parameter_expressions:
- name: "limit"
value_expression: "1000"
- name: "sort"
value_expression: "ts:desc"
- name: "mintime"
value_expression: String(Int(UnixSeconds(Now()) - 360) * 1000)
- name: "maxtime"
value_expression: String(Int(UnixSeconds(Now())) * 1000)
Administrator Logs (v1)
Track administrative actions and configuration changes:
nodes:
- name: duo_administrator_logs
type: http_pull_input
endpoint: https://YOUR_API_HOSTNAME/admin/v1/logs/administrator
method: GET
pull_interval: 6m
header_expressions:
- header: "Date"
value_expression: FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700")
- header: "Accept"
value_expression: "application/json"
- header: "Authorization"
value_expression: Concat(["Basic ", EDXEncode(Concat([EDXEnv("DUO_INTEGRATION_KEY", ""), ":", EDXHmac(Format("%s\n%s\n%s\n%s\n%s", [FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700"), "GET", EDXEnv("DUO_API_HOSTNAME", ""), "/admin/v1/logs/administrator", Concat(["mintime=", String(Int(UnixSeconds(Now()) - 420))], "")]), EDXEnv("DUO_SECRET_KEY", ""), "sha1", "hex")], ""), "base64", false)], "")
parameter_expressions:
- name: "mintime"
value_expression: String(Int(UnixSeconds(Now()) - 420))
Telephony Logs (v2)
Monitor phone-based authentication events:
nodes:
- name: duo_telephony_logs
type: http_pull_input
endpoint: https://YOUR_API_HOSTNAME/admin/v2/logs/telephony
method: GET
pull_interval: 7m
header_expressions:
- header: "Date"
value_expression: FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700")
- header: "Accept"
value_expression: "application/json"
- header: "Authorization"
value_expression: Concat(["Basic ", EDXEncode(Concat([EDXEnv("DUO_INTEGRATION_KEY", ""), ":", EDXHmac(Format("%s\n%s\n%s\n%s\n%s", [FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700"), "GET", EDXEnv("DUO_API_HOSTNAME", ""), "/admin/v2/logs/telephony", Concat(["limit=1000&maxtime=", String(Int(UnixSeconds(Now())) * 1000), "&mintime=", String(Int(UnixSeconds(Now()) - 480) * 1000), "&sort=ts%3Adesc"], "")]), EDXEnv("DUO_SECRET_KEY", ""), "sha1", "hex")], ""), "base64", false)], "")
parameter_expressions:
- name: "limit"
value_expression: "1000"
- name: "sort"
value_expression: "ts:desc"
- name: "mintime"
value_expression: String(Int(UnixSeconds(Now()) - 480) * 1000)
- name: "maxtime"
value_expression: String(Int(UnixSeconds(Now())) * 1000)
Activity Logs (v2)
Track user activity and application usage:
nodes:
- name: duo_activity_logs
type: http_pull_input
endpoint: https://YOUR_API_HOSTNAME/admin/v2/logs/activity
method: GET
pull_interval: 8m
header_expressions:
- header: "Date"
value_expression: FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700")
- header: "Accept"
value_expression: "application/json"
- header: "Authorization"
value_expression: Concat(["Basic ", EDXEncode(Concat([EDXEnv("DUO_INTEGRATION_KEY", ""), ":", EDXHmac(Format("%s\n%s\n%s\n%s\n%s", [FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700"), "GET", EDXEnv("DUO_API_HOSTNAME", ""), "/admin/v2/logs/activity", Concat(["limit=1000&maxtime=", String(Int(UnixSeconds(Now())) * 1000), "&mintime=", String(Int(UnixSeconds(Now()) - 540) * 1000), "&sort=ts%3Adesc"], "")]), EDXEnv("DUO_SECRET_KEY", ""), "sha1", "hex")], ""), "base64", false)], "")
parameter_expressions:
- name: "limit"
value_expression: "1000"
- name: "sort"
value_expression: "ts:desc"
- name: "mintime"
value_expression: String(Int(UnixSeconds(Now()) - 540) * 1000)
- name: "maxtime"
value_expression: String(Int(UnixSeconds(Now())) * 1000)
Offline Enrollment Logs (v1)
Monitor offline device enrollment activities:
nodes:
- name: duo_offline_enrollment_logs
type: http_pull_input
endpoint: https://YOUR_API_HOSTNAME/admin/v1/logs/offline_enrollment
method: GET
pull_interval: 10m
header_expressions:
- header: "Date"
value_expression: FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700")
- header: "Accept"
value_expression: "application/json"
- header: "Authorization"
value_expression: Concat(["Basic ", EDXEncode(Concat([EDXEnv("DUO_INTEGRATION_KEY", ""), ":", EDXHmac(Format("%s\n%s\n%s\n%s\n%s", [FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700"), "GET", EDXEnv("DUO_API_HOSTNAME", ""), "/admin/v1/logs/offline_enrollment", Concat(["mintime=", String(Int(UnixSeconds(Now()) - 720))], "")]), EDXEnv("DUO_SECRET_KEY", ""), "sha1", "hex")], ""), "base64", false)], "")
parameter_expressions:
- name: "mintime"
value_expression: String(Int(UnixSeconds(Now()) - 720))
API Endpoints Reference
Log Type | API Version | Endpoint | Description |
---|---|---|---|
Authentication | v2 | /admin/v2/logs/authentication |
User authentication attempts and results |
Administrator | v1 | /admin/v1/logs/administrator |
Administrative actions and config changes |
Telephony | v2 | /admin/v2/logs/telephony |
Phone-based authentication events |
Activity | v2 | /admin/v2/logs/activity |
User activity and application usage |
Offline Enrollment | v1 | /admin/v1/logs/offline_enrollment |
Offline device enrollment events |
Time Windows and Pull Intervals
Each endpoint uses optimized time windows and pull intervals to prevent data gaps and API rate limiting:
Endpoint | Time Window | Pull Interval | Rationale |
---|---|---|---|
Authentication | 6 minutes | 5 minutes | High-frequency events, needs overlap |
Administrator | 7 minutes | 6 minutes | Moderate frequency admin actions |
Telephony | 8 minutes | 7 minutes | Phone auth events, less frequent |
Activity | 9 minutes | 8 minutes | User activity tracking |
Offline Enrollment | 12 minutes | 10 minutes | Infrequent enrollment events |
HMAC-SHA1 Signature Details
The Duo Admin API requires HMAC-SHA1 signatures calculated from:
- Canonical Request:
DATE\nMETHOD\nHOST\nPATH\nPARAMETERS
- HMAC Calculation: HMAC-SHA1 of canonical request using secret key
- Authorization Header:
Basic base64(integration_key:hmac_signature)
The signature is automatically calculated by the Edge Delta OTTL expressions using:
FormatTime()
for RFC 2822 date formatFormat()
for canonical request string assemblyEDXHmac()
for HMAC-SHA1 calculationEDXEncode()
for Base64 encoding
Important: Single-Line OTTL Requirement
The authorization header expression must be written as a single line. The multi-line format shown above is for readability only. In your actual configuration, ensure the entirevalue_expression
is on one line.
Advanced OTTL Expression Breakdown
Here’s the authentication endpoint OTTL expression broken down for understanding:
# This is for educational purposes - remember to write as single line in actual config
header_expressions:
- header: "Authorization"
value_expression: >
Concat([
"Basic ",
EDXEncode(
Concat([
EDXEnv("DUO_INTEGRATION_KEY", ""),
":",
EDXHmac(
Format("%s\n%s\n%s\n%s\n%s", [
FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700"), # 1. Current date in RFC 2822 format
"GET", # 2. HTTP method
EDXEnv("DUO_API_HOSTNAME", ""), # 3. API hostname
"/admin/v2/logs/authentication", # 4. API endpoint path
Concat([ # 5. Query parameters string
"limit=1000&maxtime=",
String(Int(UnixSeconds(Now())) * 1000), # Current time in milliseconds
"&mintime=",
String(Int(UnixSeconds(Now()) - 360) * 1000), # 6 minutes ago in milliseconds
"&sort=ts%3Adesc" # URL-encoded sort parameter
], "")
]),
EDXEnv("DUO_SECRET_KEY", ""), # Secret key for HMAC
"sha1", # HMAC algorithm
"hex" # Output format
)
], ""),
"base64", # Base64 encode the integration_key:signature
false # Don't URL encode the result
)
], "")
Step-by-Step Process:
- Date Generation:
FormatTime(Now(), "Mon, 02 Jan 2006 15:04:05 -0700")
creates RFC 2822 timestamp - Parameter Assembly: Query parameters are built with current time windows (6-minute lookback)
- Canonical Request: All components are formatted into Duo’s required string format:
DATE METHOD HOSTNAME PATH PARAMETERS
- HMAC Calculation:
EDXHmac()
computes SHA1 hash using the secret key - Credential Assembly: Integration key and HMAC signature are combined with
:
- Base64 Encoding: Final credentials are base64 encoded for HTTP Basic Auth
- Header Formation: Result is prefixed with “Basic " for Authorization header
Time Window Logic:
UnixSeconds(Now()) * 1000
: Current time in milliseconds (maxtime)UnixSeconds(Now()) - 360) * 1000
: 6 minutes ago in milliseconds (mintime)- This creates overlapping windows to ensure no log entries are missed between pulls
API Documentation
For detailed API documentation, visit:
- Duo Admin API Overview - Complete API documentation
- Authentication Logs API - Authentication log endpoint details
- Administrator Logs API - Admin action log details
- HMAC Authentication - Signature calculation method
Rate Limiting
Duo Admin API implements rate limiting:
- Authentication: 120 requests per minute
- Administrator: 60 requests per minute
- Telephony: 60 requests per minute
- Activity: 60 requests per minute
- Offline Enrollment: 60 requests per minute
The configured pull intervals respect these limits while ensuring continuous data collection.
Troubleshooting
401 Unauthorized:
- Verify
DUO_INTEGRATION_KEY
andDUO_SECRET_KEY
environment variables - Check that the Admin API application has “Grant read information” permission
- Ensure API hostname matches your Duo account
400 Bad Request:
- Check time parameters are valid Unix timestamps
- Verify endpoint URLs match your API version requirements
- Ensure signature calculation includes all required components
403 Forbidden:
- Verify the Admin API application has appropriate permissions
- Check if IP restrictions are configured in Duo Admin Panel
Rate Limiting (429):
- The configured intervals should prevent rate limiting
- If needed, increase
pull_interval
values - Monitor Duo Admin Panel for API usage statistics
Security Best Practices
- Credential Management: Store integration and secret keys securely as environment variables
- Network Security: Use HTTPS-only connections (enforced by Duo)
- Access Control: Limit Admin API application permissions to required log types only
- Monitoring: Set up alerts for authentication failures and admin actions
- Key Rotation: Regularly rotate integration and secret keys per security policy
Sample Log Data
Authentication Log Sample
{
"access_device": {
"browser": "Chrome",
"os": "Windows"
},
"application": {
"key": "DIXXXXXXXXXXXXXXXXXXXX",
"name": "Company VPN"
},
"auth_device": {
"name": "iPhone"
},
"result": "SUCCESS",
"timestamp": 1640995200,
"username": "john.doe@company.com"
}
Administrator Log Sample
{
"action": "user_create",
"description": "Created user jane.smith@company.com",
"timestamp": 1640995200,
"username": "admin@company.com",
"object": "User"
}