A system that uses Duo's Auth API v2 today only changes three values. Your code and Duo's official libraries stay the same.
What to Change
| Value | Duo | WiraPass |
|---|---|---|
| API hostname | api-xxxxxxxx.duosecurity.com | auth.wiracode.com |
| Integration key | from the Duo Admin Panel | from the WiraPass operator (how) |
| Secret key | from the Duo Admin Panel | from the WiraPass operator |
# Before (Duo)
auth = duo_client.Auth(ikey="DIXXXXXXXXXXXXXXXXXX", skey="duo-secret", host="api-xxxxxxxx.duosecurity.com")
# After (WiraPass): the same code, three new values
auth = duo_client.Auth(ikey="DIYYYYYYYYYYYYYYYYYY", skey="wirapass-secret", host="auth.wiracode.com")Usernames Are Emails
WiraPass knows users by the email they sign in to the WiraPass app with. Send that email as the username. If your usernames are not emails, map them to the user's email first. The user_id is the WiraPass user id (UUID), not a Duo id.
What Works
- Endpoints
/auth/v2/ping,/auth/v2/check,/auth/v2/logo,/auth/v2/enroll,/auth/v2/enroll_status,/auth/v2/preauth,/auth/v2/auth,/auth/v2/auth_status. - Factors
auto,push(a WiraPass approval) andpasscode(the WiraPass backup code), with or withoutasync. - Signature versions 5, 4 and 2 (HMAC-SHA512 or HMAC-SHA1), JSON or form bodies, and X-Duo-* headers.
- Duo's answer format, Duo's error codes and the status values
allow,deny,fraud,timeout,locked_out,pushed. type,display_username,pushinfo,ipaddr,hostnameshow on the user's phone.
What Does Not Work
- SMS (
factor=sms) and phone callback (factor=phone): refused with40002and a clear message. - Duo Universal Prompt and the OIDC web flow (
/oauth/v1/…), and the old Web SDK v2. - Bypass codes and hardware tokens. The
bypass_codesparameter of enroll is ignored. - Verified Duo Push and Remembered Devices:
client_supports_verified_pushandtrusted_device_tokenare accepted and ignored, and preauth never answersallow. - The Admin API, the Accounts API and signature version 1.
Passcode Is the WiraPass Backup Code
The user opens the WiraPass app, then Settings, Approvals, Backup code. The code has 6 digits, changes every 30 seconds, and each code works once. Send it as passcode with factor=passcode.
mobile_otpin preauth only appears when the user has a backup code.- At most 5 checks per 5 minutes per user, then 42901.
- After 10 wrong codes in a row the code locks and the answer is
status=locked_out. The user creates a new code on an active phone.
Enrolling Users
There is no Duo Mobile activation. Users install WiraPass, sign in with the same email and turn on approvals. The enroll endpoint returns the download page and a QR code of it, and enroll_status answers success as soon as that email has an active approval phone. In WiraPass, username is required on enroll. The enroll_portal_url of preauth points to the download page too.
Other Small Differences
- A push shows on every approval phone of the user that is signed in to WiraPass.
devicemay be auto or a phone id from preauth. - When no phone is signed in, a push is answered
denywithout sending anything, and status_msg suggests the backup code. - After Not me, preauth answers
denyand pushes are refused for 15 minutes for that integration. status_msgis in Indonesian unless you sendAccept-Language: en.numberin devices is always empty.txidis a UUID, and auth_status waits at most 8 seconds per call. Other limits.
TLS Certificates and CA Pinning
Duo's official libraries trust only their own list of root CAs by default (CA pinning). If your library refuses the WiraPass certificate, for example with CERTIFICATE_VERIFY_FAILED, turn pinning off with the option below. Normal TLS certificate checks still run against the system CA list.
| Library | Option |
|---|---|
| duo_client (Python) | duo_client.Auth(..., disable_ca_pinning=True) |
| @duosecurity/duo_api (Node.js) | new Client(ikey, skey, host, 5, false) |
| duo_api_php | $auth->disableCaPinning() |
| duo_api_golang | duoapi.NewDuoApi(ikey, skey, host, ua, duoapi.SetCAPinning(false)) |
| duo-client (Java) | new Http.HttpBuilder(...).disableCaPinning().build() |