Versions Compared

Key

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

This material is specific to V4 (and to some extent V3) of the IdP and is not yet geared/optimized for V5+.

...

Adding REFEDS

The final step is to simply extend what's already been done to include the REFEDS context class so that SPs that need it can request it. This is a straight addition to the principal sets in the earlier examples, and the original values are included for completeness. You could also add the value as a SAML 1.1 option in an obvious way but that isn't likely to matter or come into play given that SAML 1.1 SPs, if they exist, can't actually request anything.

...

Expand
titleMultiple Integrations via conf/authn/duo-authn-config.xml
Code Block
    <!-- This integration is for local use. -->
    <bean id="DuoLocal" parent="shibboleth.authn.Duo.DuoIntegration"
        p:APIHost="%{idp.duo.apiHost}"
        p:applicationKey="%{idp.duo.applicationKey}"
        p:integrationKey="%{idp.duo.integrationKey}"
        p:secretKey="%{idp.duo.secretKey}">
        <property name="supportedPrincipals">
            <list>
                <bean parent="shibboleth.SAML2AuthnContextClassRef"
                    c:classRef="http://example.org/ac/classes/mfa" />
                <bean parent="shibboleth.SAML1AuthenticationMethod"
                    c:method="http://example.org/ac/classes/mfa" />
            </list>
        </property>
    </bean>

    <!-- This integration supports either local or REFEDS use. -->
    <bean id="DuoREFEDS" parent="shibboleth.authn.Duo.DuoIntegration"
        p:APIHost="%{idp.duo.apiHost.refeds}"
        p:applicationKey="%{idp.duo.applicationKey.refeds}"
        p:integrationKey="%{idp.duo.integrationKey.refeds}"
        p:secretKey="%{idp.duo.secretKey.refeds}">
        <property name="supportedPrincipals">
            <list>
                <bean parent="shibboleth.SAML2AuthnContextClassRef"
                    c:classRef="http://example.org/ac/classes/mfa" />
                <bean parent="shibboleth.SAML1AuthenticationMethod"
                    c:method="http://example.org/ac/classes/mfa" />
                <bean parent="shibboleth.SAML2AuthnContextClassRef"
                    c:classRef="https://refeds.org/profile/mfa" />
                <bean parent="shibboleth.SAML1AuthenticationMethod"
                    c:method="https://refeds.org/profile/mfa" />
            </list>
        </property>
    </bean>
    
    <!-- Order matters here; we want the most lenient integration first. -->
    <util:list id="DuoIntegrationList">
       <ref bean="DuoDefault" />
       <ref bean="DuoREFEDS" />
    </util:list>

    <!-- This simple function just iterates over each integration until it finds an acceptable one. -->
    <bean id="shibboleth.authn.Duo.DuoIntegrationStrategy" parent="shibboleth.ContextFunctions.Scripted"
            factory-method="inlineScript"
            p:customObject-ref="DuoIntegrationList">
        <constructor-arg name="scriptSource">
        <value>
        <![CDATA[
        duo = null;
        authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
        iter = custom.iterator();
        while (duo == null && iter.hasNext()) {
            duo = iter.next();
            if (!authCtx.isAcceptable(duo)) {
                duo = null;
            }
        }
        duo;
        ]]>
        </value>
        </constructor-arg>
    </bean>

Finally, to make this all workFor this to work properly with the MFA flow, you need to prevent to perform an additional step, and attach a “reuseCondition” to the Duo flow from automatically attaching all of the Principals the flow supports to the resulting Subject. This allows only the Principals supported by the integration that was actually used to be included, preventing an earlier local MFA result from being reused for REFEDS requests.In V4.1+, you would simply set the property that determines whether or not the Duo result can actually be reused if it exists, because not every result will satisfy a particular request. This requires a bit of scripting like so, which you can place in global.xml:

Expand
titleglobal.xml
Code Block
    <bean id="my.DuoReuseCondition" parent="shibboleth.Conditions.Scripted" factory-method="inlineScript">
        <constructor-arg>
            <value>
            <![CDATA[
                var reuse = false;
                var authnCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
                if (authnCtx != null) {
                    var mfaCtx = authnCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
                    if (mfaCtx != null) {
                        var active = mfaCtx.getActiveResults().get("authn/Duo");
                        if (active != null) {
                            reuse = authnCtx.isAcceptable(active);
                        }
                    }
                }
                reuse;
            ]]>
            </value>
        </constructor-arg>
    </bean>

Finally, to make this all work, you need to prevent the Duo flow from automatically attaching all of the Principals the flow supports to the resulting Subject. This allows only the Principals supported by the integration that was actually used to be included, preventing an earlier local MFA result from being reused for REFEDS requests.

In V4.1+, to attach the condition above, and turn off the auto-attach behavior, you modify conf/authn/authn.properties:

Code Block
...
idp.authn.Duo.addDefaultPrincipals

...

 = false
idp.authn.Duo.reuseCondition = my.DuoReuseCondition
...

In earlier versions, you would need to edit the Duo flow descriptor bean and set the addDefaultPrincipals property to false these in conf/authn/general-authn.xml.

...