ICF 1.5.20.26

Duo connector

The duo connector lets you manage Duo service accounts and synchronize accounts and groups between Duo and the IDM or Advanced Identity Cloud managed user repository.

This topic describes how to install and configure the Duo connector and how to perform basic tests to ensure that it’s running correctly.

Before you start

The instructions in this guide assume you have a Duo Administrator Account and you have created and authorized a Custom Application, as described in the Duo Documentation.

Before you configure the connector, log in to your administrator account and note the following information:

  1. Navigate to Applications.

  2. Click Protect an Application.

  3. Locate the entry for Admin API in the applications list.

  4. Click Protect to the far-right to configure the application and make note of the following:

    • Integration Key

    • Secret Key

    • API hostname

Install the Duo connector

If you are looking for the Advanced Identity Cloud application for this connector, refer to:

You can download any connector from Backstage, but some are included in the default deployment for Advanced Identity Cloud, IDM, or RCS. When using an included connector, you can skip installing it and move directly to configuration.

Connector included in default deployment
Connector IDM RCS

Duo

No

No

Download the connector .jar file from Backstage.

  • If you are running the connector locally, place it in the /path/to/openidm/connectors directory, for example:

    mv ~/Downloads/duo-connector-1.5.20.26.jar /path/to/openidm/connectors/
  • If you are using a remote connector server (RCS), place it in the /path/to/openicf/connectors directory on the RCS.

Configure the Duo connector

Create a connector configuration using the IDM admin UI:

  1. From the navigation bar, click Configure > Connectors.

  2. On the Connectors page, click New Connector.

  3. On the New Connector page, type a Connector Name.

  4. From the Connector Type drop-down list, select Duo Connector - 1.5.20.26.

  5. Complete the Base Connector Details.

    For a list of all configuration properties, refer to Duo Connector Configuration
  6. Click Save.

When your connector is configured correctly, the connector displays as Active in the admin UI.

Refer to this procedure to create a connector configuration over REST.

The following excerpt shows sample configuration properties:

"configurationProperties": {
  "host" : "_CHANGEME_",
  "integrationKey" : "_CHANGEME_",
  "secretKey" : "_CHANGEME_"
}
Integration Key

The Duo Application Integration Key.

Secret Key

The Duo Application Secret Key.

API hostname

The Duo API hostname.

Test the Duo connector

Test that the configuration is correct by running the following command:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header 'Content-Type: application/json' \
--request POST \
'http://localhost:8080/openidm/system/duo?_action=test'
{
  "name": duo,
  "enabled": true,
  "config": "config/provisioner.openicf/Duo",
  "connectorRef": {
    "bundleVersion": "[1.5.0.0,1.6.0.0)",
    "bundleName": "org.forgerock.openicf.connectors.duo-connector",
    "connectorName": "org.forgerock.openicf.connectors.duo.DuoConnector"
  },
  "displayName": "org.forgerock.openicf.connectors.duo.DuoConnector",
  "objectTypes": [
    "__ACCOUNT__",
    "__ALL__",
    "__GROUP__"
  ],
  "ok": true
}

If the command returns "ok": true, your connector was configured correctly and can authenticate to the Duo server.

Duo remote connector

If you want to run this connector outside of PingOne Advanced Identity Cloud or IDM, you can configure the Duo connector as a remote connector. Java Connectors installed remotely on a Java Connector Server function identically to those bundled locally within PingOne Advanced Identity Cloud or installed locally on IDM.

You can download the Duo connector from here.

Refer to Remote connectors for configuring the Duo remote connector.

Use the Duo connector

You can use the duo connector to perform the following actions on a Duo account.

Accounts: User

Create a Duo user

This example creates a Duo user with the minimum required attributes.

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "userType": "user",
  "__NAME__": "Jhon Doe",
  "email": "jhon_doe@example.com",
}' \
"http://localhost:8080/openidm/system/Duo/__ACCOUNT__?_action=create"
{
  "_id": "DUAAA11BB1C1D1EE1UUU",
  "tokens": [],
  "phones": [],
  "notes": "",
  "email": "jhon_doe@example.com",
  "__GROUPS__": [],
  "__ENABLE__": True,
  "realname": "",
  "userType": "user",
  "__NAME__": "Jhon Doe",
  "alias1": null,
  "alias2": null,
  "alias3": null,
  "alias4": null,
  "last_login": null
}

When you create a new user, you must specify at least the __NAME__,email and userType attributes.

Valid userType values are: user and admin.

Create a Duo full user

This example creates a Duo full user.

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
{
  "__GROUPS__": [
    "DGAA00BB0C0D0EE0GGG"
  ],
  "tokens": [
    "DHAAA00BB0C0D0EE0TTT"
  ],
  "phones": [
    "+12125551212"
  ],
  "notes": "example",
  "email": "jane_doe@example.com",
  "__ENABLE__": True,
  "realname": "Jane Doe",
  "alias1": "Jane",
  "alias2": null,
  "alias3": "Doe",
  "alias4": null,
  "userType": "user",
  "__NAME__": "Jane Doe"
}' \
"http://localhost:8080/openidm/system/Duo/__ACCOUNT__?_action=create"
{
  "_id": "DUAAA00BB0C0D0EE0UUU",
  "realname": "Jane Doe",
  "userType": "user",
  "phones": [
    "+12125551212"
  ],
  "__GROUPS__": [
    "DGAA00BB0C0D0EE0GGG"
  ],
  "tokens": [
    "DHAAA00BB0C0D0EE0TTT"
  ],
  "__NAME__": "Jane Doe",
  "notes": "example",
  "__ENABLE__": True,
  "email": "jane_doe@example.com",
  "alias1": "jane",
  "alias2": null,
  "alias3": "doe,
  "alias4": null,
  "last_login": null"
}

Because phone numbers are automatically created before being assigned to a user, the connector deletes unused phone numbers when you perform a user update or delete. This prevents the accumulation of junk data.

alias is not case-sensitive. Repeated values will be ignored.

Any attribute with a null value will be ignored.

List all Duo users

This example queries all Duo users:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"http://localhost:8080/openidm/system/Duo/__ACCOUNT__?_queryFilter=true"
{
  "result": [
    {
      "_id": "DUAAA00BB0C0D0EE0UUU",
      "realname": "Jane Doe",
      "userType": "user",
      "phones": [
        "+12125551212"
      ],
      "__GROUPS__": [],
      "tokens": [],
      "__NAME__": "jane doe",
      "notes": "example",
      "__ENABLE__": True,
      "email": "jane_doe@example.com",
      "last_login": null,
      "alias1": "jane",
      "alias2": null,
      "alias4": null,
      "alias3": "doe"
    },
    ...
    {
      "_id": "DUAAA11BB1C1D1EE1UUU",
      "realname": "",
      "userType": "user",
      "phones": [],
      "__GROUPS__": [],
      "tokens": [],
      "notes": "",
      "__NAME__": "Jhon Doe",
      "__ENABLE__": True,
      "email": "jhon_doe@example.com",
      "last_login": null,
      "alias1": null,
      "alias2": null,
      "alias3": null,
      "alias4": null
    }
  ],
  "resultCount": 96,
  "pagedResultsCookie": null,
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}

Get Duo user

This example queries a specific Duo user by its ID:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header 'Content-Type: application/json' \
--request GET \
'http://localhost:8080/openidm/system/Duo/__ACCOUNT__/DUAAA11BB1C1D1EE1UUU'
{
  "_id": "DUAAA11BB1C1D1EE1UUU",
  "realname": "",
  "userType": "user",
  "phones": [],
  "__GROUPS__": [],
  "tokens": [],
  "__NAME__": "Jhon Doe",
  "notes": "",
  "__ENABLE__": True,
  "email": "jhon_doe@example.com",
  "last_login": null,
  "alias1": null,
  "alias2": null,
  "alias3": null,
  "alias4": null
}

Update a Duo user

This example updates a specific Duo user by its ID:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--header "If-Match: *" \
--request POST \
--data '{
  "alias1": "Jhon",
  "alias4": "",
  "notes": "example note",
  "email": "jhon_doe@example.com",
  "__GROUPS__": [],
  "__ENABLE__": True,
  "realname": "Jhon Doe",
  "__NAME__": "Jhon Doe"
}' \
'http://localhost:8080/openidm/system/Duo/__ACCOUNT__/DUAAA11BB1C1D1EE1UUU'
{
  "_id": "DUAAA11BB1C1D1EE1UUU",
  "tokens": [],
  "phones": [
    "+12125551212"
  ],
  "notes": "example note",
  "email": "jhon_doe@example.com",
  "__GROUPS__": [],
  "__ENABLE__": True,
  "realname": "Jhon Doe",
  "userType": "user",
  "__NAME__": "Jhon Doe",
  "last_login": null,
  "alias1": "Jhon",
  "alias2": "Doe",
  "alias3": null,
  "alias4": null
}

Delete a Duo user

This example deletes a Duo user:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header 'Content-Type: application/json' \
--header "If-Match: *" \
--request DELETE \
'http://localhost:8080/openidm/system/Duo/__ACCOUNT__/DUAAA11BB1C1D1EE1UUU'
{
  "_id": "DUAAA11BB1C1D1EE1UUU",
  "realname": "Jhon Doe",
  "userType": "user",
  "phones": [
    "+12125551212"
  ],
  "__GROUPS__": [],
  "tokens": [],
  "__NAME__": "Jhon Doe",
  "notes": "Tests note",
  "__ENABLE__": True,
  "email": "jhon_doe@example.com",
  "last_login": null,
  "alias1": "Jhon",
  "alias2": "Doe",
  "alias3": null,
  "alias4": null
}

The response returns the account object before deletion.

Accounts: Admin

Create a Duo admin

This example creates a Duo admin.

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "role": "Owner",
  "phones": [
    "+12125551212"
  ],
  "email": "jhon_doe@example.com",
  "realname": "Jhon Doe",
  "userType": "admin",
  "__NAME__": "Jhon Doe",
  "restricted_by_admin_units": false
}' \
"http://localhost:8080/openidm/system/Duo/__ACCOUNT__?_action=create"
{
  "_id": "DEAAA11BB1C1D1EE1UUU",
  "phones": [
    "+12125551212"
  ],
  "email": "jhon_doe@example.com",
  "__ENABLE__": False,
  "restricted_by_admin_units": "false",
  "userType": "admin",
  "__NAME__": "Jhon Doe",
  "last_login": null,
  "role": "Owner"
}

When you create a new Admin, you must specify at least the __NAME__ and email attributes.

role value must be one of the following values:

  • Owner

  • Administrator

  • Application Manager

  • User Manager

  • Security Analyst

  • Help Desk

  • Billing

  • Phishing Manager

  • Read-only

The role names are case-sensitive. Defaults to "Owner" if not specified.

phones: Phone number for the new administrator. Limited to one. It cannot be removed once assigned due to API limitations. Formatted in the E.164 standard (+17345551212). If no leading plus sign is provided, then it is assumed to be a United States number and an implicit +1 country code is prepended. Dashes and spaces are ignored. If this parameter is specified, it cannot be empty.

restricted_by_admin_units: Is this administrator restricted by an administrative unit assignment? Either true or false. Defaults to false if not specified. Must be set to true in order to add the admin to an administrative unit using the API. Note that attempting to set to true for admins with the Owner role results in a failure response.

send_email: Optional. If set to 1, the activation link and an introductory message will be emailed to the new administrator. If set to 0, no email is sent, and the link is returned to the API method’s caller only. Defaults to 0.

token_id: Optional. The token_id of the hardware token to associate with the administrator.

valid_days: Optional. Number of days before the activation link expires. Defaults to 7, the maximum allowed value is 31.

__ENABLE__ will remain False until the admin’s account is validated. In the meantime, __ENABLE__ cannot be changed by the connector.

Get Duo Admin

This example queries a specific Duo admin by its ID:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header 'Content-Type: application/json' \
--request GET \
'http://localhost:8080/openidm/system/Duo/__ACCOUNT__/DEAAA11BB1C1D1EE1UUU'
{
  "_id": "DEAAA11BB1C1D1EE1UUU",
  "phones": [
    "+12125551212"
  ],
  "email": "jhon_doe@example.com",
  "__ENABLE__": True,
  "restricted_by_admin_units": "false",
  "userType": "admin",
  "__NAME__": "Jhon Doe",
  "last_login": null,
  "role": "Owner"
}

Update a Duo Admin

This example updates a specific Duo admin by its ID:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--header "If-Match: *" \
--request POST \
--data '{
  "role": "Read-Only",
  "__ENABLE__": True,
}' \
'http://localhost:8080/openidm/system/Duo/__ACCOUNT__/DEAAA11BB1C1D1EE1UUU'
{
  "_id": "DEAAA11BB1C1D1EE1UUU",
  "phones": [
    "+12125551212"
  ],
  "email": "jhon_doe@example.com",
  "__ENABLE__": True,
  "restricted_by_admin_units": "false",
  "userType": "admin",
  "__NAME__": "Jhon Doe",
  "last_login": null,
  "role": "Read-Only"
}

GROUPS

Create a Duo group

This example creates a Duo group:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "desc": "Generic Description",
  "__ENABLE__": "True,
  "__NAME__": "Group 1"
}' \
'http://localhost:8080/openidm/system/Duo/__GROUP__'
{
  "_id": "DGAA00BB0C0D0EE0GGG",
  "desc": "Generic Description",
  "__ENABLE__": True,
  "__NAME__": "Group 1"
}

Query all Duo groups

This example queries all Duo groups:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header 'Content-Type: application/json' \
--request GET \
'http://localhost:8080/openidm/system/Duo/__GROUP__?_queryFilter=True'
{
  "result": [
    {
      "_id": "DGAA00BB0C0D0EE0GGG",
      "desc": "Generic Description",
      "__ENABLE__": True,
      "__NAME__": "Testing Group"
    },
    ...
    {
      "_id": "DGAA11BB1C1D1EE1GGG",
      "desc": "Working Group",
      "__ENABLE__": True,
      "__NAME__": "Working Group"
    }
  ],
  "resultCount": 56,
  "pagedResultsCookie": null,
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}

The maximum number of records returned is 100 per page.

Get a Duo group

This example gets a Duo group by its ID:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header 'Content-Type: application/json' \
--request GET \
'http://localhost:8080/openidm/system/Duo/__GROUP__/DGAA00BB0C0D0EE0GGG'
{
  "_id": "DGAA00BB0C0D0EE0GGG",
  "desc": "Generic Description",
  "__ENABLE__": True,
  "__NAME__": "Group 1"
}

Update a Duo group

This example updates a Duo group by its ID:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header 'Content-Type: application/json' \
--header "If-Match: *" \
--request PUT \
--data '{
  "desc": "New tests",
  "__NAME__": "New Group tests",
}' \
'http://localhost:8080/openidm/system/Duo/__GROUP__/DGAA00BB0C0D0EE0GGG'
{
  "_id": "DGAA00BB0C0D0EE0GGG",
  "__NAME__": "New Group tests",
  "desc": "New tests",
  "__ENABLE__": True
}

Delete a Duo group

This example deletes a Duo group by its ID:

curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header 'Content-Type: application/json' \
--request DELETE \
'http://localhost:8080/openidm/system/Duo/__GROUP__/DGAA00BB0C0D0EE0GGG'
{
  "_id": "DGAA00BB0C0D0EE0GGG",
  "__NAME__": "New Group tests",
  "desc": "New tests",
  "__ENABLE__": True
}

The response returns the group object before deletion.

Mapping

From Duo users to IDM or Advanced Identity Cloud users

Attributes Grid: Where the columns represent the attribute name mapped from source to target and the necessary data transformation to synchronize successfully.

SOURCE TARGET TRANSFORMATION SCRIPT

_id

user_id

N/A

__NAME__

username

N/A

alias1

alias1

N/A

alias2

alias2

N/A

alias3

alias3

N/A

alias4

alias4

N/A

aliases

aliases

N/A

realname

realname

N/A

email

email

N/A

notes

notes

N/A

phones

phones

N/A

__ENABLE__

status

N/A

u2f_tokens

u2f_tokens

N/A

enable_auto_prompt

enable_auto_prompt

N/A

external_id

external_id

N/A

__GROUPS__

groups

N/A

is_enrolled

is_enrolled

N/A

last_directory_sync

last_directory_sync

N/A

lockout_reason

lockout_reason

N/A

tokens

tokens

N/A

webauthncredentials

webauthncredentials

N/A

From IDM or Advanced Identity Cloud users to Duo users

Attributes Grid: Where the columns represent the attribute name mapped from source to target and the necessary data transformation to synchronize successfully.

SOURCE TARGET TRANSFORMATION SCRIPT

user_id

_id

N/A

username

__NAME__

N/A

alias1

alias1

N/A

alias2

alias2

N/A

alias3

alias3

N/A

alias4

alias4

N/A

aliases

aliases

N/A

realname

realname

N/A

email

email

N/A

notes

notes

N/A

phones

phones

N/A

status

__ENABLE__

N/A

u2f_tokens

u2f_tokens

N/A

enable_auto_prompt

enable_auto_prompt

N/A

external_id

external_id

N/A

groups

__GROUPS__

N/A

is_enrolled

is_enrolled

N/A

last_directory_sync

last_directory_sync

N/A

lockout_reason

lockout_reason

N/A

tokens

tokens

N/A

webauthncredentials

webauthncredentials

N/A

From Duo groups to IDM or Advanced Identity Cloud groups

Attributes Grid: Where the columns represent the attribute name mapped from source to target and the necessary data transformation to synchronize successfully.

SOURCE TARGET TRANSFORMATION SCRIPT

group_id

group_id

N/A

__NAME__

name

N/A

desc

desc

N/A

__ENABLE__

status

N/A

From IDM or Advanced Identity Cloud groups to Duo groups

Attributes Grid: Where the columns represent the attribute name mapped from source to target and the necessary data transformation to synchronize successfully.

SOURCE TARGET TRANSFORMATION SCRIPT

group_id

group_id

N/A

name

__NAME__

N/A

desc

desc

N/A

status

__ENABLE__

N/A

OpenICF Interfaces Implemented by the Duo Connector

The Duo Connector implements the following OpenICF interfaces. For additional details, see ICF interfaces:

Create

Creates an object and its uid.

Delete

Deletes an object, referenced by its uid.

Schema

Describes the object types, operations, and options that the connector supports.

Script on Connector

Enables an application to run a script in the context of the connector.

Any script that runs on the connector has the following characteristics:

  • The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access.

  • The script has access to a connector variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration.

  • The script has access to any script arguments passed in by the application.

Search

Searches the target resource for all objects that match the specified object class and filter.

Test

Tests the connector configuration.

Testing a configuration checks all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid.

This operation might need to connect to a resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out).

You can invoke the test operation before a connector configuration has been validated.

Update

Updates (modifies or replaces) objects on a target resource.

Duo Connector Configuration

The Duo Connector has the following configurable properties:

Basic Configuration Properties

Property Type Default Encrypted(1) Required(2)

host

String

null

Yes

URL API Host (api-xxxxxx.duosecurity.com)

integrationKey

String

null

Yes

App Integration Key

secretKey

GuardedString

null

Yes

No

App Secret Key

(1) Whether the property value is considered confidential, and is therefore encrypted in IDM.

(2) A list of operations in this column indicates that the property is required for those operations.