Versions Compared

Key

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

...

The example further assumes that you want to define both an internal means of identifying and communicating MFA but also support the REFEDS signal value of "https://refeds.org/profile/mfa". The benefit of this approach is that it isolates internal use from possible changes or shifts in the criteria for asserting the REFEDS value. While this may not be likely, allowing for that possibility is the conservative assumption. As a plausible, if perhaps not imminent, example, if REFEDS were to decide that use of SMS was not an appropriate technology to allow, one could continue supporting SMS internally using the other context class while limiting requests that require the REFEDS context class to disallow SMS.and separates the behavior of a REFEDS Profile deployment from any local policies or exceptions needed because of internal business decisions. For example, locally a decision to exempt certain users from a requirement to perform MFA through some kind of bypass mechanism would violate the REFEDS Profile requirements, so using a different string internally allows for that flexibility.

The example uses a made-up internal value which should be adjusted into a locally-defined/owned URI. The REFEDS value is of course the actual value needed.

...

Code Block
languagexml
    <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
        <property name="profileConfigurations">
            <list>
...
                <bean parent="SAML2.SSO">
			        <property name="defaultAuthenticationMethods">
            			<bean parent="shibboleth.SAML2AuthnContextClassRef"
			                c:classRef="https://refeds.org/profile/mfa" />
			        </property>
				</bean>
...
            </list>
        </property>
    </bean>

Supporting Multiple Duo Policies

As a final extention of this example, suppose that, having deployed separate context class values for REFEDS and local use, a decision is made to implement a Duo policy of some kind that would violate the REFEDS Profile. The only way to prevent that change from contaminating requests for the REFEDS Profile is to establish a second Duo integration for local use where that policy change is implemented. The original integration would be left alone.

The Duo flow documentation describes in general how to support multiple integrations. The same general approach applies to the DuoOIDC plugin (with one exception, noted at the end), so this example can be adapted to that version as well, but for simplicity, the example will use the original one. The point of this example is to demonstrate concretely how to use <AuthnContextClassRef> values in the form of custom Principals to make the determination as to which integration to use at runtime.

The core of this approach is that you want the Duo and MFA flows to support both the local and REFEDS context class values, which was done earlier, but you then need to further subdivide things by explicitly associating each Duo integration object with the specific context class Principals supported by that integration. In other words, the Duo flow as a whole supports both, but the actual integrations don’t. Then you need a scripted function that will properly select among the two based on whether they support the current request or not. This approach actually generalizes to more than just 2 possible context class values, so multiple local values could also be defined to represent different policies, but the example will stick to just two.

The example also makes use of additional custom properties to allow storage of the various keys outside the XML.

A key assumption of this example is that the “local” integration as less strict in some sense than the REFEDS integration. That is, it assumes that a request for local MFA would be satisfied if the user had already performed REFEDS MFA. Because of that assumption, the appropriate configuration is for the “local” integration to support only the local Principal values, but the REFEDS integration to support both local and REFEDS Principal vales. If the local integration were actually more strict, such that a request for REFEDS MFA is satsified by local MFA but not the converse, then this would be reversed.

Expand
titleMultiple Integrations via conf/authn/duo-authn-config.xml
Code Block
    <!-- This integration is for local use. -->
    <bean id="DuoLocal" class="net.shibboleth.idp.authn.duo.BasicDuoIntegration"
        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" class="net.shibboleth.idp.authn.duo.BasicDuoIntegration"
        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 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+, you would simply set the property idp.authn.Duo.addDefaultPrincipals to false in conf/authn/authn/properties.

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