API Documentation

Quick Start

From preauth to the approval result in a few lines of code.

Documentation Pages

Before You Start

  • An integration key, a secret key and the API hostname from the operator (how to get them).
  • A WiraPass account to test with: install WiraPass on a phone, sign in with your email and turn on approvals in the Approvals tab.
  • An accurate server clock (NTP). Signatures are refused when your clock is more than 5 minutes off.

The Flow

  1. POST /auth/v2/preauth with username set to the user's WiraPass email. Continue only when result is auth.
  2. POST /auth/v2/auth with factor=push, device=auto, async=1. The answer is a txid.
  3. GET /auth/v2/auth_status?txid=… again and again while result is waiting. Each call waits up to 8 seconds, so no pause is needed in between.
  4. allow: let the user in. deny: refuse. If status is fraud, the user pressed Not me: alert your administrators.

Python with Duo's Official Library

The duo_client library works as it is: pip install duo_client.

Python (duo_client)
import duo_client

auth = duo_client.Auth(
    ikey="DIXXXXXXXXXXXXXXXXXX",
    skey="your-secret-key-from-the-operator",
    host="auth.wiracode.com",
)
USERNAME = "rina@example.com"  # the user's WiraPass email

pre = auth.preauth(username=USERNAME, ipaddr="203.0.113.7")
if pre["result"] == "auth":
    # Send an approval request to the user's phone; do not block.
    txid = auth.auth(
        factor="push",
        username=USERNAME,
        device="auto",
        async_txn=True,
        type="Sign in to the Staff Portal",
        ipaddr="203.0.113.7",
    )["txid"]
    # auth_status long-polls: each call waits up to 8 seconds for a change.
    status = auth.auth_status(txid)
    while status["waiting"]:
        status = auth.auth_status(txid)
    if status["success"]:
        print("allow: access granted")
    else:
        print("deny:", status["status"], status["status_msg"])
else:
    # "enroll": the user has no approval phone yet; "deny": paused after "Not me".
    print(pre["result"] + ":", pre["status_msg"])

Node.js Without Libraries

This example uses duo.mjs from Signing Requests.

Node.js (with duo.mjs from the Signing page)
import { duoCall } from './duo.mjs';

const username = 'rina@example.com'; // the user's WiraPass email

const pre = (await duoCall('POST', '/auth/v2/preauth', { username })).response;
if (pre.result === 'auth') {
  const { txid } = (await duoCall('POST', '/auth/v2/auth', {
    username, factor: 'push', device: 'auto', async: '1', type: 'Sign in to the Staff Portal',
  })).response;
  let status;
  do {
    // Long-poll: each call waits up to 8 seconds for a change.
    status = (await duoCall('GET', '/auth/v2/auth_status', { txid })).response;
  } while (status.result === 'waiting');
  console.log(status.result === 'allow' ? 'allow: access granted' : `deny: ${status.status} ${status.status_msg}`);
} else {
  console.log(`${pre.result}: ${pre.status_msg}`);
}

When the Phone Cannot Be Reached

When no approval phone is signed in to WiraPass, preauth still answers auth, but status_msg suggests the backup code, and a push is answered deny at once without sending anything. Ask the user for the backup code from the WiraPass app:

Python (duo_client)
result = auth.auth(factor="passcode", username=USERNAME, passcode=code_typed_by_user)
if result["result"] == "allow":
    print("allow: access granted")

Waiting for the Answer in One Call

Without async, the auth call waits until the user answers or it times out (60 seconds), then gives the final result and status. Set your HTTP client timeout above 65 seconds. If the client hangs up first, the request on the phone is cancelled.

Duo is a trademark of Cisco. WiraPass is not affiliated with Duo or Cisco and only offers API compatibility.