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 geared/optimized for V5+.

Table of Contents
minLevel1
maxLevel2

Background

The REFEDS MFA Profile is a convention for defining basic criteria needed to plausibly claim that one has applied multi-factor authentication to a subject and a standard SAML AuthnContext class reference value for communicating that between IdPs and SPs so that it's possible to leverage MFA on a wider scale than simple internally to an IdP organization.

...

Note

Do NOT attempt to just cut and paste these examples; in most cases they will NOT work as is because they're excerpts of files and even beans that will contain other settings. They simply demonstrate the specific settings involved in this use case.

Also, this example is NOT suitable for use by anyone using third-party SAML IdP products to actually handle user authentication. That's a much different, and much more complex, problem that involves fairly advanced usage of the SAML proxying support in the IdP. That would require very faithful support of the SAML standard that is unlikely to be found in the commercial IdP space. In particularly, the IdP product would have to support requesting and signaling MFA via the SAML <AuthnContextClassRef> mechanism, or it may be impossible (or at least much more difficult) to pull off what this example is demonstrating for a Shibboleth-only deployment.

Assumptions

The example assumes that the IdP is using the Password and Duo flows together in a simple/standard way by means of the MFA flow that ties them together. The approach shown is usable to support both IdP-based and SP-based rules for applying the second (Duo) factor to a request.

...

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.

Initial Setup

The example proceeds by establishing the configuration to support the internal value, which for example purposes is just "http://example.org/ac/classes/mfa". The REFEDS support will be added later below. For completeness, the example also includes support for expressing MFA as a SAML 1.1 AuthenticationMethod but this is unlikely to matter much at this point.

...

Expand
titleV4.1+

New (or modernized) installs of V4.1+ can be taught what context classes to associate with login flows using conf/authn/authn.properties:

conf/authn/authn.properties
Code Block
languagetext
...

idp.authn.Duo.supportedPrincipals = \
    saml2/http://example.org/ac/classes/mfa, \
    saml1/http://example.org/ac/classes/mfa

...

idp.authn.MFA.supportedPrincipals = \
    saml2/http://example.org/ac/classes/mfa, \
    saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
    saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
    saml1/http://example.org/ac/classes/mfa, \
    saml1/urn:oasis:names:tc:SAML:1.0:am:password
Expand
titleOlder Versions

Earlier versions rely on the conf/authn/general-authn.xml file to associate the class reference, using the supportedPrincipals property on the login flows. The class reference object must be connected to both the Duo and MFA flows because the latter is a superset of the former. The IdP is being told that these flows "support" this context class so it's necessary that both flows are told to do so. This is an excerpt (note the "..."'s) of the complete file and just the relevant aspects are shown; other settings will vary by local need.

conf/authn/general-authn.xml
Code Block
languagexml
<util:list id="shibboleth.AvailableAuthenticationFlows">
...
	<!-- Associates use of Duo with the local MFA context class. -->
    <bean p:id="authn/Duo" parent="shibboleth.AuthenticationFlow">
        <property name="supportedPrincipals">
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="http://example.org/ac/classes/mfa" />
            <bean parent="shibboleth.SAML1AuthenticationMethod"
                c:classRef="http://example.org/ac/classes/mfa" />
        </property>
    </bean>

...

	<!-- Associates MFA flow with both password- and Duo-based authentication. -->
    <bean p:id="authn/MFA" parent="shibboleth.AuthenticationFlow">
        <property name="supportedPrincipals">
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="http://example.org/ac/classes/mfa" />
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" />
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:Password" />
            <bean parent="shibboleth.SAML1AuthenticationMethod"
                c:classRef="http://example.org/ac/classes/mfa" />
            <bean parent="shibboleth.SAML1AuthenticationMethod"
                c:method="urn:oasis:names:tc:SAML:1.0:am:password" />
        </property>
    </bean>

...
</util:list>

Testing

Once this is in place, you can test the behavior (e.g., in development) by temporarily adjusting the default behavior of the IdP to require MFA for all requests by attaching the same local value to the DefaultRelyingParty configuration of the SAML 2.0 SSO profile:

conf/relying-party.xml
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="http://example.org/ac/classes/mfa" />
			        </property>
				</bean>
...
            </list>
        </property>
    </bean>

Aside from seeing MFA applied, you should also find that the resulting assertions carry an <AuthnContextClassRef> element matching the expected value.

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
titleV4.1+
conf/authn/authn.properties
Code Block
languagetext
...

idp.authn.Duo.supportedPrincipals = \
	saml2/https://refeds.org/profile/mfa, \
    saml2/http://example.org/ac/classes/mfa, \
    saml1/http://example.org/ac/classes/mfa

...

idp.authn.MFA.supportedPrincipals = \
	saml2/https://refeds.org/profile/mfa, \
    saml2/http://example.org/ac/classes/mfa, \
    saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
    saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
    saml1/http://example.org/ac/classes/mfa, \
    saml1/urn:oasis:names:tc:SAML:1.0:am:password
Expand
titleOlder Versions
conf/authn/general-authn.xml
Code Block
languagexml
<util:list id="shibboleth.AvailableAuthenticationFlows">
...
	<!-- Associates use of Duo with the local MFA context class. -->
    <bean p:id="authn/Duo" parent="shibboleth.AuthenticationFlow">
        <property name="supportedPrincipals">
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="https://refeds.org/profile/mfa" />			<!-- REFEDS added here -->
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="http://example.org/ac/classes/mfa" />
            <bean parent="shibboleth.SAML1AuthenticationMethod"
                c:classRef="http://example.org/ac/classes/mfa" />
        </property>
    </bean>

...

	<!-- Associates MFA flow with both password- and Duo-based authentication. -->
    <bean p:id="authn/MFA" parent="shibboleth.AuthenticationFlow">
        <property name="supportedPrincipals">
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="https://refeds.org/profile/mfa" />			<!-- REFEDS added here -->
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="http://example.org/ac/classes/mfa" />
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" />
            <bean parent="shibboleth.SAML2AuthnContextClassRef"
                c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:Password" />
            <bean parent="shibboleth.SAML1AuthenticationMethod"
                c:classRef="http://example.org/ac/classes/mfa" />
            <bean parent="shibboleth.SAML1AuthenticationMethod"
                c:method="urn:oasis:names:tc:SAML:1.0:am:password" />
        </property>
    </bean>

...
</util:list>

Testing

Once the REFEDS value is in place, you can test that in the same way as earlier, just changing the trigger value:

conf/relying-party.xml
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.

...

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.

DuoOIDC Use

The example works essentially the same for the Universal Prompt plugin, but due to a bug, the plugin doesn’t actually support a property for turning off the addDefaultPrincipals property yet (a bug has been filed and will be fixed in a subsequent patch). There is a bit of a complex rule used by the plugin now to decide how to set that property and a workaround for now (that avoids the need to declare the whole flow descriptor somewhere) is to add this additional function to conf/authn/duo-oidc-authn-config.xml:

...