Integrate with Duo Passwordless

DRAFT - Under Construction

Duo’s Passwordless feature adds support for FIDO 2 passwordless authentication to the Duo service. See Duo Passwordless. This article describes how to integrate this feature with the Shibboleth Identity Provider software, and assumes the use of at least IdP V4.1 and above.

This Duo feature is not integrated with the IdP via the usual Duo 2FA layer; rather it requires enabling the Duo Single Sign-On feature, which is a SAML IdP in its own right. The Shibboleth IdP must be configured to use Duo as an authentication source via SAML 2.0, either for all or a subset of users and services. This article provides an example of the most straightforward way to connect Shibboleth to Duo’s SSO feature.

It is out of scope for this article to describe how to enable the Passwordless feature itself; please refer to Duo’s documentation for that at Duo Passwordless. It is notably a requirement at present to use Duo’s Authentication Proxy software with an LDAP service in support of password authentication on the Duo side. Duo SSO supports SAML-based authentication to their platform (e.g., treating it as an SP to the Shibboleth IdP) but this cannot be used to support the Passwordless feature at this time (that’s a Duo limitation, not a Shibboleth one).

First, the basics of integrating the two products for all logins will be explained, and then some additional advanced examples are provided showing how to pilot this feature and limit its use based on service.

The core product documentation that this draws on includes:

Connecting Shibboleth to Duo SSO

Shibboleth supports SAML-based proxying of authentication and it is a matter of changing a small number of options to set this up. There is only one primary source of variability here, namely how to map the identity of the user into the IdP. This article assumes the default Duo behavior, which is to supply an email address in the SAML <NameID> element.

This part of the article demonstrates how to delegate all authentication in the IdP to the Duo SSO platform.

  1. Not specifically shown; you will first need to use Duo’s Admin site for your tenant create a new Duo “protected application” of type “Generic SAML Service Provider”. The only information you will need from your IdP are the entityID and the hostname.
    You supply the entityID directly, and for the Assertion Consumer Service, use the URL https://<hostname>/idp/profile/Authn/SAML2/POST/SSO
    You may, but do not have to, uncheck the Sign Assertion checkbox, as signing the resonse is sufficient.
    Logout is not supported at this time (or likely, ever) when proxying is involved, so that can be left absent.

  2. Use the button Duo provides to downlod the SAML metadata for the SSO platform (labeled “Download XML”). Copy that metadata securely to your IdP and load it in whatever way you prefer to handle third party metadata sources.

  3. Adjust properties in conf/authn/authn.properties as follows:

    # Regular expression matching login flows to enable, e.g. IPAddress|Password idp.authn.flows = SAML # In the #### SAML #### section... # Obviously this Duo entityID will be specific to your deployment and will be in the Duo metadata. idp.authn.SAML.proxyEntityID = https://sso-123abd.sso.duosecurity.com/saml2/sp/DIN6AKR34RM9BCHEHPO/metadata
  4. Adjust conf/c14n/subject-c14n.xml as follows:

    1. Uncomment the following bean reference to enable support for handling the incoming <NameID> element:

      <ref bean="c14n/SAML2ProxyTransform" />
    2. Uncomment and populate the shibboleth.ProxyNameTransformPredicate bean so the Duo entityID is authorized to supply its <NameID> element for consumption:

      <bean id="shibboleth.ProxyNameTransformPredicate" parent="shibboleth.Conditions.ProxyAuthentication"> <constructor-arg name="collection"> <list> <value>https://sso-123abd.sso.duosecurity.com/saml2/sp/DIN6AKR34RM9BCHEHPO/metadata</value> </list> </constructor-arg> </bean>
    3. OPTIONAL: If you prefer to strip the email address domain from the incoming value (which is perhaps more likely to match your existing system assumptions about usernames), then uncomment and adjust the shibboleth.ProxyNameTransforms bean to strip this (your domain will of course vary):

Other Considerations

The above is all that’s needed to universally punt authentication to Duo and consume the result. You will obviously want to ensure that the value produced from the <NameID> element provided by Duo can then be used to drive your existing attribute resolution process and produce appropriate data for your SPs.

Another issue is the <AuthnContextClassRef> value (or equivalent in other protocols) expressed by the Shibboleth IdP to other SPs. Duo hardwires the value it asserts, so it can’t really be used as the basis of any sort of mapping. To work around this, the easiest thing to do is, contrary to the defaults, set the idp.authn.SAML.addDefaultPrincipals property to true, and adjust the idp.authn.SAML.supportedPrincipals property to include whatever custom value you use in your deployment to signal “strong” authentication.

Conditional Use

The rest of this How To is focused on enabling a more controlled rollout of this Duo feature, in much the same way one might want to limit the rollout of any new authentication-related feature. Much the same approach would apply to, for example, piloting the Duo Universal Prompt, or certainly more directly any other capability exposed by SAML proxying to another IdP.

The MFA login flow provides the means to script logic to conditionally use different login methods at runtime. While there are an infinite number of ways this can be done, just a few examples are included here (to be combined or used as the basis of your own logic).

In these examples, there has to be an “alternative” to using the SAML flow (more or less, whatever you’re doing already). In the examples below, the “Password” flow is used as the fallback, but you should be able to combine these examples with existing MFA scripting/rule logic if your existing deployment is already using the MFA flow to combine, e.g., Password + Duo or DuoOIDC.

Initial Adjustments

The first step is to “inject” the MFA flow into the picture and wrap it around the SAML flow. This step is just mechanical and results in the same outcome as before (punting all authentication to Duo) but allows for more control afterward.

  1. The MFA flow is an IdP “module”, so per the documentation, you’ll need to enable the idp.authn.MFA module if not already enabled.

  2. Change the idp.authn.flows property you set above from “SAML” to “MFA”.

  3. Make sure any principal values you added to the idp.authn.SAML.supportedPrincipals property are also copied over to the idp.authn.MFA.supportedPrincipals property.

  4. In conf/authn/mfa-authn-config.xml, do the following:

That just runs the SAML flow and lets the success or failure of that produce the outcome.

Conditional by Service

For basic testing, you can define a list of SPs for which the SAML flow should be used, with everything else using Password (or, again, whatever you’re already doing). Using this strategy to leverage the RelyingParty layer allows this list to be adjusted and reloaded via that service.

First, define the list of pilot services in a RelyingParty override by adding to conf/relying-party.xml:

Then, you script a rule to leverage this override in conf/authn/mfa-authn-config.xml:

Conditional By User

This use case is theoretically possible, but is much more complex to manage with this scenario because unlike traditional Duo usage, there is no way to know who the user is prior to dispatching control to Duo. As such, no example is currently provided, but conceptually doing this would require introducing a custom flow with a view to prompt the user for their username first, solely to decide whether to use the passwordless option. This should in principle be possible, but is very unlikely to be practical since it would require interjecting such a prompt in front of all users.

It is probably more likely to do a service-based pilot using a service used by limited sets of people.