SP-driven Duo opt-in

While the Replicating Multi-Context Broker Functionality (Duo + Username/Password with user-opt-in forcing Duo) page has plenty of information on how to get user-opt-in and also SP-opt-in Duo working in tandem, it is overkill if you are just trying to replicate Duo 2FA On Demand from the old Shibboleth2 IdP. This method replicates the Duo 2FA functionality, where any SP can explicitly request Duo for their application; there is no per-user opt-in or enforcement.

Methodology

The UChicago-sponsored Unicon Duo plugin for Shibboleth defines a new authnContext. From that state, it is trivial for an SP to request that particular context. (The same is not true of the Duo Shibboleth login handler, which hooks the LoginHandler in a clever but less reusable manner.) 


Once the Unicon plugin is correctly installed, it is only necessary to  configure the SP to require the authnContextClassRef "http://www.duosecurity.com/" with forceAuthn set, like this:

<SSO entityID="https://idp.example.org/idp/shibboleth"
 discoveryProtocol="SAMLDS" discoveryURL="https://ds.example.org/DS/WAYF"
 forceAuthn="true"
 authnContextClassRef="http://www.duosecurity.com/"
/>

or possibly this:

<SessionInitiator type="Chaining" Location="/Login" isDefault="true" id="Login"
     entityID="https://idp.example.org/idp/shibboleth" forceAuthn="true" authnContextClassRef="http://www.duosecurity.com/">
</SessionInitiator>

... depending on the SP version/configuration.


Installation

The installation instructions for the Unicon plugin are correct but incomplete (as of this writing). The procedure used at Temple for release version 1.0.1, integrated with IdP 3.1.2:


  1. Download the release tarball and untar it to the IdP root (/opt/shibboleth-idp). This installs a new default duo configuration file, three required jar files, and a Velocity template for Duo.
  2. Add a new Duo authentication flow definition in flows/authn/Duo/duo-authn-flow.xml. This comes from the git source tree, but is not in the distribution tarball: 

    <flow xmlns="http://www.springframework.org/schema/webflow"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
          parent="authn.abstract, authn/conditions">
        <var name="duoCredential" class="net.unicon.iam.shibboleth.idp.authn.duo.authn.DuoCredential"/>
        <view-state id="duo" view="duo" model="duoCredential">
            <binder>
                <binding property="signedDuoResponse"/>
            </binder>
            <on-entry>
                <evaluate expression="environment" result="viewScope.environment"/>
                <evaluate
                        expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))"
                        result="viewScope.authenticationContext"/>
                <set name="viewScope.request" value="flowRequestContext.getExternalContext().getNativeRequest()"/>
                <set name="viewScope.sigRequest"
                     value="duoAuthenticationService.generateSignedRequestToken(authenticationContext)"/>
                <set name="viewScope.apiHost" value="duoAuthenticationService.getApiHost()"/>
            </on-entry>
            <transition on="proceed" bind="true" validate="false" to="validateDuo"/>
        </view-state>
    
        <action-state id="validateDuo">
            <evaluate expression="ValidateDuoResponse"/>
            <evaluate expression="'proceed'"/>
            <transition on="proceed" to="proceed"/>
        </action-state>
    </flow>
  3. Similarly, flows/c14n/Duo/duo-c14n-flow.xml is not bundled, or mentioned in the installation documentation:

    <flow xmlns="http://www.springframework.org/schema/webflow"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
          parent="c14n.abstract">
        <action-state id="DuoSubjectCanonicalization">
            <evaluate expression="'proceed'" />
            <transition on="proceed" to="proceed" />
        </action-state>
    </flow>
  4. Edit system/conf/webflow-config.xml to add this stanza, also not mentioned: 

    --- a/system/conf/webflow-config.xml
    +++ b/system/conf/webflow-config.xml
    @@ -8,6 +8,10 @@
                                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                                http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd">
    
    +    <context:component-scan base-package="net.unicon.iam.shibboleth.idp.authn.duo">
    +        <context:exclude-filter type="assignable" expression="net.unicon.iam.shibboleth.idp.authn.duo.global.Config" />
    +    </context:component-scan>
    +
         <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
             p:placeholderPrefix="%{" p:placeholderSuffix="}" />
  5. Modify edit-webapp/WEB-INF/web.xml as documented in the Unicon install document, adding these two lines: 

    --- a/edit-webapp/WEB-INF/web.xml
    +++ b/edit-webapp/WEB-INF/web.xml
    @@ -8,6 +8,7 @@
             same named beans in previous files. -->
         <context-param>
             <param-name>contextConfigLocation</param-name>
    +        <param-value>classpath*:/META-INF/shibboleth-idp/conf/global.xml</param-value>
             <param-value>${idp.home}/system/conf/global-system.xml</param-value>
         </context-param>
    
    @@ -131,6 +132,7 @@
             <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
             <init-param>
                 <param-name>contextConfigLocation</param-name>
    +           <param-value>classpath*:/META-INF/shibboleth-idp/conf/webflow-config.xml ${idp.home}/system/conf/mvc-beans.xml ${idp.home}/system/conf/webflow-config.xml</param-value>
                 <param-value>${idp.home}/system/conf/mvc-beans.xml ${idp.home}/system/conf/webflow-config.xml</param-value>
             </init-param>
             <init-param>
  6. Modify or replace conf/global.xml to add the AuthenticationFlow beans for Duo, as documented in the Unicon install document; we used this replacement, directly out of their git repository:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:c="http://www.springframework.org/schema/c"
           xmlns:util="http://www.springframework.org/schema/util"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <context:component-scan base-package="net.unicon.iam.shibboleth.idp.authn.duo.global" />
        <bean id="authn/Duo" parent="shibboleth.AuthenticationFlow"
              p:nonBrowserSupported="false" p:forcedAuthenticationSupported="%{duo.forcedAuthenticationSupported:true}">
            <property name="supportedPrincipals">
                <util:list>
                    <bean parent="shibboleth.SAML2AuthnContextClassRef"
                          c:classRef="http://www.duosecurity.com/" />
    		<bean parent="shibboleth.SAML2AuthnContextClassRef"
                          c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" />
                    <bean parent="shibboleth.SAML1AuthenticationMethod"
                          c:method="http://www.duosecurity.com/" />
                </util:list>
            </property>
        </bean>
        <bean id="c14n/Duo" parent="shibboleth.PostLoginSubjectCanonicalizationFlow">
            <property name="activationCondition">
                <bean class="net.unicon.iam.shibboleth.idp.authn.duo.c14n.DuoSubjectCanonicalizationActivationCondition" />
            </property>
        </bean>
    </beans>
  7. Edit the idp.additionalProperties line in conf/idp.properties to include the new duo.properties, as documented by Unicon (or put your duo properties directly in the idp.properties file): 

    idp.additionalProperties= /conf/ldap.properties, /conf/saml-nameid.properties, /conf/services.properties, /conf/duo.properties
  8. Configure conf/duo.properties with your Duo secrets

  9. Edit conf/idp.properties to add the Duo flows by uncommenting / modifying these two lines: 

    idp.authn.flows = Password|Duo
    idp.authn.flows.initial = Password

    Note that the idp.auth.flows.initial property is deprecated and will be removed from V4. See AuthenticationConfigurationfor details

  10. Rebuild the war file using /opt/shibboleth-idp/bin/build.sh

  11. Restart shibboleth and test.