Configuring the Realtime Webhook
The way to configure a Realtime Webhook that supports integration with DUO.
Before you begin
Ensure that you obtained the access token from your API client.
Procedure
Create the Realtime Webhook by using the following CURL example.
In the example, substitute the following values.
- {{DUO API Hostname}}
- The
API hostname
from your DUO tenant Auth API application configuration. - {{DUO Integration key}}
- The
Integration key
from your DUO tenant Auth API application configuration. - {{tenant}}
- Your Verify tenant name.
- {{access token}}
- The access token obtained from client credentials grant.
Webhook example CURL
curl --location --request POST 'https://{{tenant}}/v1.0/webhooks-mgmt/' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer {{access token}}}}' \
--data-raw '{
"name": "DUO MFA Provider",
"type": "realtime",
"urls": ["{{DUO API Hostname}}"],
"authentication": {
"type": "header",
"header": {
"values":[
{
"key": "hdr_integration_key",
"value": "{{DUO Integration key}}"
},
{
"key": "hdr_signing_key",
"value": "{{DUO Secret key}}"
}
]
}
},
"resources": {
"enrollments": {
"suffix": "/auth/v2/preauth",
"method": "POST",
"transform": {
"outgoing": "{\"body\":jsonToFormURLEncoded({\"username\":body.username}, true), \"skip_authentication\": true, \"header\": header.put(\"date\", formatTime(now, '\''02 Jan 06 15:04 -0700'\'')).put(\"content-type\", \"application/x-www-form-urlencoded\").put(\"accept\", \"application/json\").put(\"authorization\", \"Basic \" + joinStrings([authentication_header.hdr_integration_key, hmacSha1(joinStrings([formatTime(now, '\''02 Jan 06 15:04 -0700'\''),method.toUpper(), host, path.toLower(), jsonToFormURLEncoded({\"username\":body.username}, true)], \"\\n\"), authentication_header.hdr_signing_key)], \":\").base64Encode())}",
"incoming": "{\"body\": has(body.response) && has(body.response.devices) ? body.response.devices.map(d, d.capabilities.filter(c, c != \"auto\").map(c, {\"id\": d.device, \"capability\": c == \"mobile_otp\" ? \"hotp\" : c == \"sms\" ? \"smsotp\" : c, \"attributes\": {\"deviceName\": has(d.name) ? d.name : \"\", \"deliveryAddress\": has(d.number) ? d.number : \"\", \"authExecutionFlow\": c == \"mobile_otp\" ? \"validate\" : c == \"push\" ? \"init_then_poll\" : \"init_then_validate\"}})).flatten() : []}"
}
},
"initiate": {
"suffix": "/auth/v2/auth",
"method": "POST",
"transform": {
"outgoing": "{\"body\":jsonToFormURLEncoded({\"async\":\"1\", \"device\":body.id, \"factor\": body.capability == \"smsotp\" ? \"sms\" : body.capability, \"username\":body.attributes.username}, true), \"skip_authentication\": true, \"header\": header.put(\"date\", formatTime(now, '\''02 Jan 06 15:04 -0700'\'')).put(\"content-type\", \"application/x-www-form-urlencoded\").put(\"accept\", \"application/json\").put(\"authorization\", \"Basic \" + joinStrings([authentication_header.hdr_integration_key, hmacSha1(joinStrings([formatTime(now, '\''02 Jan 06 15:04 -0700'\''),method.toUpper(), host, path.toLower(), jsonToFormURLEncoded({\"async\":\"1\", \"device\":body.id, \"factor\": body.capability == \"smsotp\" ? \"sms\" : body.capability, \"username\":body.attributes.username}, true)], \"\\n\"), authentication_header.hdr_signing_key)], \":\").base64Encode())}",
"incoming": "{\"body\": has(body.response) && has(body.response.txid) ? {\"transactionId\": body.response.txid, \"status\": \"PENDING\"} : {\"status\": \"FAILED\"}}"
}
},
"validate": {
"suffix": "/auth/v2/auth",
"method": "POST",
"transform": {
"outgoing": "{\"body\":jsonToFormURLEncoded({\"factor\": \"passcode\", \"passcode\": body.attributes.passvalue, \"username\":body.attributes.username}, true), \"skip_authentication\": true, \"header\": header.put(\"date\", formatTime(now, '\''02 Jan 06 15:04 -0700'\'')).put(\"content-type\", \"application/x-www-form-urlencoded\").put(\"accept\", \"application/json\").put(\"authorization\", \"Basic \" + joinStrings([authentication_header.hdr_integration_key, hmacSha1(joinStrings([formatTime(now, '\''02 Jan 06 15:04 -0700'\''),method.toUpper(), host, path.toLower(), jsonToFormURLEncoded({\"factor\": \"passcode\", \"passcode\": body.attributes.passvalue, \"username\":body.attributes.username}, true)], \"\\n\"), authentication_header.hdr_signing_key)], \":\").base64Encode())}",
"incoming": "{\"body\": has(body.response) && has(body.response.result) ? {\"status\": body.response.result == \"allow\" ? \"SUCCESS\" : \"FAILED\", \"attributes\": body.response} : {\"status\": \"FAILED\"}}"
}
},
"hotp_1": {
"suffix": "/auth/v2/auth",
"method": "POST",
"transform": {
"outgoing": "{\"body\":jsonToFormURLEncoded({\"factor\": \"passcode\", \"passcode\": body.attributes.passvalue, \"username\":body.attributes.username}, true), \"skip_authentication\": true, \"header\": header.put(\"date\", formatTime(now, '\''02 Jan 06 15:04 -0700'\'')).put(\"content-type\", \"application/x-www-form-urlencoded\").put(\"accept\", \"application/json\").put(\"authorization\", \"Basic \" + joinStrings([authentication_header.hdr_integration_key, hmacSha1(joinStrings([formatTime(now, '\''02 Jan 06 15:04 -0700'\''),method.toUpper(), host, path.toLower(), jsonToFormURLEncoded({\"factor\": \"passcode\", \"passcode\": body.attributes.passvalue, \"username\":body.attributes.username}, true)], \"\\n\"), authentication_header.hdr_signing_key)], \":\").base64Encode())}",
"incoming": "{\"body\": has(body.response) && has(body.response.result) ? {\"status\": body.response.result == \"allow\" ? \"SUCCESS\" : \"FAILED\", \"attributes\": body.response} : {\"status\": \"FAILED\"}}"
}
},
"result": {
"suffix": "/auth/v2/auth_status",
"method": "GET",
"transform": {
"outgoing": "{\"query\": {\"txid\": body.transactionId}, \"skip_authentication\": true, \"header\": header.put(\"date\", formatTime(now, '\''02 Jan 06 15:04 -0700'\'')).put(\"content-type\", \"application/x-www-form-urlencoded\").put(\"accept\", \"application/json\").put(\"authorization\", \"Basic \" + joinStrings([authentication_header.hdr_integration_key, hmacSha1(joinStrings([formatTime(now, '\''02 Jan 06 15:04 -0700'\''),method.toUpper(), host, path.toLower(), jsonToFormURLEncoded({\"txid\": body.transactionId}, true)], \"\\n\"), authentication_header.hdr_signing_key)], \":\").base64Encode())}",
"incoming": "{\"body\": has(body.response) && has(body.response.result) ? {\"status\": body.response.result == \"allow\" ? \"SUCCESS\" : body.response.result == \"waiting\" || (body.response.result == \"deny\" && body.response.status == \"sent\") ?\"PENDING\" : body.response.status == \"timeout\" ? \"TIMEOUT\" : \"FAILED\", \"attributes\": body.response} : {\"status\": \"FAILED\"}}"
}
}
},
"purpose": ["external_mfa"]
}'
Take note of the id
in the response body of the Webhook that is
created. It is needed to configure the DUO external MFA provider.