Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Localtabgroup
tabStyleflat
Localtab live
titleConfiguration

Once you have configured a Shibboleth ‘Protected Application’ and enabled support for the Universal Prompt in the Duo Admin Panel (see also Duo Universal Prompt), you'll need to copy across your client IDAPI hostname and client secret into the conf/authn/duo-oidc.properties file to form your Duo Integration. The client ID and secret will likely appear as integration key and secret key until you make your first request using the new AuthAPI i.e. actually use this plugin for authentication. Note, you may want to keep the client secret in credentials/secrets.properties for consistency with other IdP secrets

Next, you need to specify a redirection URI as per the OAuth2.0 specification (RFC6749). This is the endpoint the Duo Universal Prompt will redirect the end-users user-agent (browser) to after successful second-factor authentication. By default, Duo does not require you to pre-register redirect URIs (you can request this if desired),  instead taking one supplied by the IdP inside a signed JWT request object. This opens a few different possibilities depending on which of the two clients you choose. The simplest, supported by both clients, is to define a static idp.duo.oidc.redirectURL property in the conf/authn/duo-oidc.properties file:

Redirect URI

idp.duo.oidc.redirectURL = https://<hostname>:<port>/idp/profile/Authn/Duo/2FA/duo-callback

Where <hostname> and <port> match that of your running IdP - the port can be omitted if your IdP uses the standard HTTP/HTTPS port e.g. 80 and 443 respectively. You are free to change part of that path i.e. Authn/Duo/2FA by setting the idp.duo.oidc.externalAuthnPath property. Although in most cases there is little reason for doing so. The endpoint itself needs only be accessible by the end-user’s user-agent (browser).

Alternatively, you can let the plugin determine the redirect URI from the Host header sent from the client that issues the 'first' Duo 2FA request. To do so, comment out the idp.duo.oidc.redirectURL property and then, in order to prevent HTTP Host header injection attacks (and possibly leaking your authorization code to a malicious actor), declare one or more comma-seperated allowed 'origins' [RFC6454] in the property idp.duo.oidc.redirecturl.allowedOrigins. Each origin is a combination of scheme, host, and port. For schemes where the default port is used e.g. HTTPS on port 443, the port can be omitted. For example, assuming a production IdP has a hostname of prod.example.com using the HTTPS scheme over the default port, and a matching development IdP has a hostname of dev.example.com running on a custom port 8443, the following origins would be sufficient:

Allowed Origins

idp.duo.oidc.redirecturl.allowedOrigins = https://prod.example.com, https://dev.example.com:8443

This can be useful if you want to keep a single generic configuration between development, staging, and production servers, etc.

Further to this, if you are using the Shibboleth Nimbus client, the redirect URI will be created dynamically from the Host header per-request - there is no extra configuration for this, it works per-request by default. This could be useful, for example, if a single IdP instance is serving requests from more than one virtual-host, and each Duo 2FA request will need to be redirected back to the originating Hostname in order to successfully complete the request.

As an advanced configuration option, you can specify more than one Duo integration and use a runtime function to determine which is used per authentication request. 

Given that Duo's 'second-factor' authentication runs after a 'first-factor' authentication method, you will also need to enable the "idp.authn.MFA" module in addition to an appropriate first-factor (e.g., the Password flow module, "idp.authn.Password"). See the MFA configuration tab for a sample configuration, and typically will need to configure the MFA flow to make use of this one as a second factor.

Localtab live
titleNon-Browser Use

The flow supports a secondary integration allowing use of the Duo AuthAPI for non-browser use cases such as SAML ECP. To enable support for the AuthAPI, you will need to define an additional integration with Duo. A second set of properties is defined to allow this.

Also, the default settings for this login flow mark it as not supporting non-browser use. This needs to be adjusted by setting the idp.authn.DuoOIDC.nonBrowserSupported property.

By default, a built-in HttpClient bean is used to communicate with the Duo AuthAPI with fairly vanilla TLS behavior that relies on the system defaults. It's possible to customize this heavily using a pair of beans. More advanced documentation is in the HttpClientConfiguration topic.

Localtab live
titleMFA Configuration

Here we describe an example MFA flow using both the MFA and Password flows (in addition to the new DuoOIDC flow). Of course, this example assumes you have also enabled those modules in addition to this plugin/module, as per their documentation pages.

For testing, the credential store of the Password flow can be configured from a simple flat file of usernames/passwords, for example, using the HTPasswdCredentialValidator in conf/authn/password-authn-config.xml as shown below. If this file is missing, you have not yet enabled the "idp.authn.Password" module.

Example password validator
Code Block
languagexml
<util:list id="shibboleth.authn.Password.Validators">
    <bean parent="shibboleth.HTPasswdValidator" p:resource="%{idp.home}/conf/authn/htpasswd.txt" />
</util:list>

Or, you may already have configured a 'production' ready password validator e.g. using LDAP. Either way, your next step is to compose both these flows into a suitable multi-factor authentication flow. An example flow defined in conf/authn/mfa-authn-config.xml file is shown below. If this file is missing, you have not yet enabled the "idp.authn.MFA" module.

Example MFA configuration
Code Block
languagexml
<util:map id="shibboleth.authn.MFA.TransitionMap">
        <!-- First rule runs the Password login flow. -->
        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/Password" />
        </entry>

        <!-- Second rule runs a function if Password succeeds, to determine whether an additional factor is required. -->        
        <entry key="authn/Password">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkSecondFactor" />
        </entry>
        <!-- An implicit final rule will return whatever the final flow returns. -->
    </util:map>

    <!-- Example script to see if second factor is required. Currently just returns the DuoOIDC flow -->
    <bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
        <constructor-arg>
            <value>
            <![CDATA[
                nextFlow = "authn/DuoOIDC";

                // Check if second factor is necessary for request to be satisfied.
                //authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
               // mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
                //if (mfaCtx.isAcceptable()) {
                //    nextFlow = null;
                //}
                
                nextFlow;   // pass control to second factor or end with the first
            ]]>
            </value>
        </constructor-arg>
    </bean>

In summary, the IdP will run the "authn/Password" flow followed by the "authn/DuoOIDC" flow. More complex business/orchestration logic can be added to the checkSecondFactor script if required, see the MFA page for a full discussion of the possibilities.

After configuring the MFA orchestration logic appropriately, you should consider how to represent the multi-factor authentication mechanism to the outside world.

Localtab live
titleAuthentication Context Classes (supported principals)

As the DuoOIDC plugin typically runs after a first-factor authentication method orchestrated by the MFA flow, the MFA flow must present to the system a supportedPrincipals collection compatible with this type of authentication mechanism alongside any other factor(s) used.  In the SAML 2.0 world (and nowadays more generally, e.g., OpenID Connect) these are specified as an Authentication Context Class.

Those already set by default on the MFA flow are described by the idp.authn.MFA.supportedPrincipals property in the conf/authn/authn.properties file. (If your system has been upgraded from V4.0 or earlier, then you may not have migrated to the property-centric settings V4.1 allows, and may have the supported Principals enumerated in conf/authn/general-authn.xml.) Either way,these need to be adjusted to also include those that are also exposed by the "authn/DuoOIDC" flow.

This flow defaults to an example set defined by the idp.authn.DuoOIDC.supportedPrincipals property in conf/duo-oidc.properties. If not set, it will fall back to the definition of the idp.authn.Duo.supportedPrincipals property.

As for what these values should be:

Info

Supported Principals

There is no "standard" context class (or SAML 1 authentication method) to represent most forms of MFA, and experience has shown that it's a bad idea to create a strong coupling between applications and the exact technologies that you use for authentication.

As a result, the default configuration contains only a placeholder value to use that you will need to change, but there is currently no standard value to use. One possible choice to consider is the REFEDS profile, but your particular deployment may or may not satisfy its requirements. Regardless of specifics, the approach is a good one in general: a generic URI representing the use of MFA.


For more advanced supportedPrincipal configurations, see below

Localtab live
titleUser Interface

The plugin itself does not provide a native IdP view to customize because all second-factor authentication interactions occur on a Duo-hosted site to which the IdP redirects the user-agent (browser). See the Duo Admin Panel to see what customizations are possible on the Duo site. It is important to note that unsuccessful second-factor authentication terminates on the Duo site. The IdP will not receive notification of the failure, and hence authentication failure cannot be propagated further to the SP.

The non-browser variant has no UI and relies on a set of HTTP request headers from the client. Authentication relies on knowing the type of Duo factor to use, the device to use, and occasionally a passcode. Often none are needed and the whole process is automatic (the factor and device are defaulted to "auto"). Specifying a device is generally done using a name the user must associate with the device themselves. Some factors rely on a passcode being supplied.

The headers can be changed but default to:

  • X-Shibboleth-Duo-Factor

  • X-Shibboleth-Duo-Device

  • X-Shibboleth-Duo-Passcode

Factor is one of "auto", "push", "phone", or "passcode". The "sms" factor does not work, but will fail while resulting in the issuance of codes via SMS for subsequent use.

...