SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)

Andrew Jason Morgan morgan at oregonstate.edu
Sun May 16 15:36:53 UTC 2021


Ah, I understand now.  Don't do it by forcing the MFA authnContextClass.  Use MFA logic to decide which users and SPs will use MFA.  Here is our authn/mfa-authn-config.xml logic:

                nextFlow = "authn/Duo";

                logger = Java.type("org.slf4j.LoggerFactory").getLogger("checkSecondFactor");
                logger.debug('Starting checkSecondFactor');

                // Setup some environment for later
                resCtx = input.getSubcontext("net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext", true);
                usernameLookupStrategyClass = Java.type("net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy");
                usernameLookupStrategy = new usernameLookupStrategyClass();
                username = usernameLookupStrategy.apply(input);
                resCtx.setPrincipal(username);
                stringType = Java.type("net.shibboleth.idp.attribute.StringAttributeValue");

                authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
                mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");

                // Fetch Duo attributes
                resCtx.getRequestedIdPAttributeNames().add("needs_duo");
                resCtx.getRequestedIdPAttributeNames().add("has_duo");
                resCtx.resolveAttributes(custom);
                needs_duo = resCtx.getResolvedIdPAttributes().get("needs_duo");
                has_duo = resCtx.getResolvedIdPAttributes().get("has_duo");

                // If SP requires Duo or needs_duo == 1 (opted-in or mandatory)
                if (! mfaCtx.isAcceptable() || (needs_duo != null && needs_duo.getValues().contains(new stringType("1")))) {
                    logger.debug('Duo is required');
                    if (has_duo == null || (has_duo != null && ! has_duo.getValues().contains(new stringType("1")))) {
                        logger.info("Duo is required but user cannot use Duo (username=" + username + ")");
                        mfaCtx.setEvent('DuoRequired');
                        nextFlow = null;
                    }
                }
                else {
                    nextFlow = null;
                }

                input.removeSubcontext(resCtx);   // cleanup

                nextFlow;   // pass control to second factor or end with the first


We have 2 attributes that we use: needs_duo and has_duo.  needs_duo is another way, besides the authnContextClass override, that we force or exempt a user or SP from Duo.  has_duo is a boolean indicating whether the user is enrolled in Duo.  Here is the definition of needs_duo from attribute-resolver.xml:

    <AttributeDefinition id="needs_duo" xsi:type="ScriptedAttribute">
        <InputDataConnector ref="ONIDLDAP" attributeNames="ismemberof" />
        <Script><![CDATA[
            logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.attribute.resolver.needs_duo
");
            duoflag = "0";
            duooptin = "0";
            for (i=0; i < ismemberof.getValues().size(); i++) {
                tmp = ismemberof.getValues().get(i);
                if (tmp.toLowerCase().equals("cn=duo-opt-in,ou=duo,ou=app,ou=is,ou=org,ou=osu,ou=grouper,ou=groups,o=orst.edu")) {
                    logger.debug("User is opted-in to Duo");
                    duoflag = "1";
                    duooptin = "1";
                }
                if (tmp.toLowerCase().equals("cn=duo-mandatory,ou=duo,ou=app,ou=grouper,ou=groups,o=orst.edu")) {
                    logger.debug("User is mandatory Duo");
                    duoflag = "1";
                }
            }
            rpid = profileContext.getSubcontext("net.shibboleth.idp.profile.context.RelyingPartyContext").getRelyingPartyId();
            logger.debug("rpid=" + rpid);
            // exclude the Duo sign up page for anyone not signed up
            if (duooptin.equals("0") && rpid.match(/^https:\/\/secure\.onid\.oregonstate\.edu\/duo\/sign-up.*/)) {
                logger.debug("skipping Duo for Duo sign up");
                duoflag = "0";
            }
            // exclude the Duo phone reactivation page for anyone signed up
            if (duooptin.equals("1") && rpid.match(/^https:\/\/secure\.onid\.oregonstate\.edu\/duo\/phone-reactivation.*/)) {
                logger.debug("skipping Duo for Duo phone reactivation");
                duoflag = "0";
            }
            // exclude a few SPs that are special
            if (duooptin.equals("1")) {
                if (rpid.equals("https://app.uhds.oregonstate.edu/resdesk/selfservice") ) {
                    logger.debug("skipping Duo for this entityID");
                    duoflag = "0";
                }
            }
            // require Duo for this entityID
            if (duoflag.equals("0")) {
                if (rpid.equals("https://OSU.snowflakecomputing.com")) {
                    logger.debug("requiring Duo for this entityID");
                    duoflag = "1";
                }
            }
            needs_duo.addValue(duoflag);
            logger.debug("needs_duo final value: " + needs_duo.getValues().get(0));
        ]]></Script>
    </AttributeDefinition>


It's a bit of a mess, but hopefully you get the general idea!

Since we are not overriding the authnContextClass, we can require MFA without breaking services that specifically request PasswordProtectedTransport.  We ran into this issue with snowflake, which you can see in the needs_duo code.

We only force MFA for a small number of SPs (mostly via overrides).  Our "mandatory" Duo is enforced on users, not services, because we allow a grace period before they must use Duo.

Andy



________________________________
From: users <users-bounces at shibboleth.net> on behalf of Ullfig, Roberto Alfredo <rullfig at uic.edu>
Sent: Saturday, May 15, 2021 7:30 AM
To: Shib Users <users at shibboleth.net>
Subject: Re: SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)


[This email originated from outside of OSU. Use caution with links and attachments.]

We want to start rolling out MFA. The plan is to force MFA for some applications at first with essentially the same code as you have in your relying-party.xml but at some point the plan would be to make that the default policy in relying-party (we have over 240 active service providers in use, we only manage a handful of them) - however, when we do that we will likely be breaking some of those applications, the ones that are requesting authn themselves (because of p:disallowedFeatures-ref="SAML2.SSO.FEATURE_AUTHNCONTEXT" setting). We could add some extra logging to the IDP ahead of time to see who is making these authn requests and prepare for that to some degree. Then we will need to decide what to with applications that are making authn requests - as some of them may be requesting Password. We could make exceptions for them or we can decide to not support them - that's a policy decision to be determined.

---
Roberto Ullfig - rullfig at uic.edu
Systems Administrator
Enterprise Applications & Services | Technology Solutions
University of Illinois - Chicago
________________________________
From: users <users-bounces at shibboleth.net> on behalf of Andrew Jason Morgan <morgan at oregonstate.edu>
Sent: Friday, May 14, 2021 4:30 PM
To: Shib Users <users at shibboleth.net>
Subject: Re: SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)

Roberto,

I may have missed the beginning of this thread, so I apologize if I'm asking you to repeat yourself.

What are you trying to accomplish?  Do you have some specific SPs that need MFA?  Are you just starting to rollout MFA to your users?

The context-check flow is an access control check, if you want to implement some access control at the IDP instead of the SP.  We use it for services that can't perform their own access control.  You can ignore that detail - it's not relevant for this discussion.

Thanks,
Andy

________________________________
From: users <users-bounces at shibboleth.net> on behalf of Ullfig, Roberto Alfredo <rullfig at uic.edu>
Sent: Friday, May 14, 2021 2:13 PM
To: Shib Users <users at shibboleth.net>
Subject: Re: SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)


[This email originated from outside of OSU. Use caution with links and attachments.]

OK, but since we don't manage many SPs, metadata-driven configuration doesn't seem very useful. Not one SP in InCommon is doing this that I can see. Is this something that InCommon is planning on supporting in the near future? That Relying Party configuration we do have but we don't have p:postAuthenticationFlows="#{{'context-check'}}"- what is that for? Scott mentioned metadata-driven configuration as the best solution I believe, but exactly how would that be implemented? It has to be done by the Federation no?

---
Roberto Ullfig - rullfig at uic.edu
Systems Administrator
Enterprise Applications & Services | Technology Solutions
University of Illinois - Chicago
________________________________
From: users <users-bounces at shibboleth.net> on behalf of Andrew Jason Morgan <morgan at oregonstate.edu>
Sent: Friday, May 14, 2021 3:42 PM
To: Shib Users <users at shibboleth.net>
Subject: Re: SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)

Via metadata-driven configuration, by adding this to the metadata for the SP:

  <Extensions>
    <mdattr:EntityAttributes>
      <saml:Attribute Name="http://shibboleth.net/ns/profiles/defaultAuthenticationMethods"
          NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
          <saml:AttributeValue>https://refeds.org/profile/mfa</saml:AttributeValue>
      </saml:Attribute>
      <!-- The disallowedFeatures setting is a bitmask, and 0x1 blocks SPs requesting authentication types. -->
      <saml:Attribute Name="http://shibboleth.net/ns/profiles/disallowedFeatures"
          NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
          <saml:AttributeValue>0x1</saml:AttributeValue>
      </saml:Attribute>
    </mdattr:EntityAttributes>
  </Extensions>

If you need to do it via relying-party.xml, just list only the MFA principal bean as allowed.  Here is an example:

    <!-- MFA authenticationContextClass setup -->
    <bean id="MFASAML2Principal" parent="shibboleth.SAML2AuthnContextClassRef" c:classRef="https://refeds.org/profile/mfa" />

        <!-- Require Duo -->
        <bean parent="RelyingPartyByName" c:relyingPartyIds="#{{'<entityID1>', '<entityID2>'}}">
            <property name="profileConfigurations">
                <list>
                    <bean parent="SAML2.SSO" p:disallowedFeatures-ref="SAML2.SSO.FEATURE_AUTHNCONTEXT" p:postAuthenticationFlows="#{{'context-check'}}">
                       <property name="defaultAuthenticationMethods">
                            <list>
                                <ref bean="MFASAML2Principal" />
                            </list>
                        </property>
                    </bean>
                </list>
            </property>
        </bean>


Thanks,
Andy


________________________________
From: users <users-bounces at shibboleth.net> on behalf of Ullfig, Roberto Alfredo <rullfig at uic.edu>
Sent: Friday, May 14, 2021 1:26 PM
To: Shib Users <users at shibboleth.net>
Subject: Re: SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)


[This email originated from outside of OSU. Use caution with links and attachments.]

Hello Andy, how exactly do you force MFA via metadata?

---
Roberto Ullfig - rullfig at uic.edu
Systems Administrator
Enterprise Applications & Services | Technology Solutions
University of Illinois - Chicago
________________________________
From: users <users-bounces at shibboleth.net> on behalf of Andrew Jason Morgan <morgan at oregonstate.edu>
Sent: Friday, May 14, 2021 3:16 PM
To: Shib Users <users at shibboleth.net>
Subject: Re: SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)

Roberto,

Our IDP uses the following MFA logic:

  *   If the SP requests MFA or we force MFA via metadata, perform MFA (or return a SAML error if the user cannot MFA)
  *   If the SP requests PasswordProtectedTransport or does not specify an authncontextclass:
     *   If the user is enrolled in Duo, perform MFA
     *   If the user is not enrolled in Duo, no not perform MFA

Basically, we make our users MFA all the time if they are enrolled in Duo.  We also force most of our users to enroll in Duo.  Only early admits are excluded from mandatory enrollment.

Do you need help configuring your IDP as described above, assuming that is your goal?

Thanks,

​Andy Morgan, Identity & Access Management, IT Operations and Identity
Oregon State University | University Information and Technology | 541-737-8877

________________________________
From: users <users-bounces at shibboleth.net> on behalf of Ullfig, Roberto Alfredo <rullfig at uic.edu>
Sent: Friday, May 14, 2021 1:05 PM
To: Shib Users <users at shibboleth.net>
Subject: Re: SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)


[This email originated from outside of OSU. Use caution with links and attachments.]

Is this an accurate summation of MFA/Password selection?

The attacker (that knows the password) can send a non-MFA Authn request (like Password) to the IDP and the IDP must accept it or reject it. If accepted, the attacker is in without MFA (Scenario Three below), if rejected SSO just breaks (Scenario One below).

scenarios:

One:

  1.  SP sends authn request (MFA or Password) to IDP
  2.  IDP rejects authn request
  3.  There is no SSO

Two:

  1.  SP sends authn (MFA) request to IDP
  2.  IDP accepts authn request
  3.  SSO with MFA

Three:

  1.  SP sends authn (Password) request to IDP
  2.  IDP accepts authn request
  3.  SSO without MFA

Four

  1.  SP does NOT send authn request to IDP
  2.  IDP does whatever it wants (Password or MFA) - this is when we can force it to MFA

ITrust Federation Manager for some reason is sending BOTH MFA and Password authn requests so what's used by the IDP is undefined but it looks like our IDP is just using the last one sent which is Password because that's what's being used.

The authn request always has priority over anything that we have configured for that SP. If we accept the authn request the IDP must do what the SP requests. If we reject it, there is no SSO.

Finally, Scenario Four is not the best solution, the better solution is to control MFA via metadata.


---
Roberto Ullfig - rullfig at uic.edu
Systems Administrator
Enterprise Applications & Services | Technology Solutions
University of Illinois - Chicago
________________________________
From: users <users-bounces at shibboleth.net> on behalf of Cantor, Scott <cantor.2 at osu.edu>
Sent: Tuesday, April 27, 2021 8:53 AM
To: Shib Users <users at shibboleth.net>
Subject: Re: SP Requiring/Requesting MFA (was Re: Customizing Second Factor Configuration in mfa-authn-config.xml)

On 4/27/21, 9:20 AM, "users on behalf of Ullfig, Roberto Alfredo" <users-bounces at shibboleth.net on behalf of rullfig at uic.edu> wrote:

>    OK I thought that might be the case, but then that brings me back to my first question. If I have only MFA
> enabled then every service provider goes through this MFA Transition Map, right?

Barring SSO, yes.

> If I want to enable Duo for just a few SPs I asked if I should put that code here but then you said I should be
> using a relying party profile for each SP, but if MFA is the only one enabled how is that going to work?

Firstly, I said overrides, not "overrides for each SP". I would never say that. There are many ways to control behavior, and doing it per-SP is thw worst. The best is the metadata-driven configuration feature. The second best is probably a custom metadata tag/EntityAttribute and a by-tag override, and sometimes that's even the best. The worst is by a specific SP or list of SPs hardcoded into a file.

To address the main point of confusion, MFA (the flow ID) does not mean multi-factor authentication, and it was a mistake to call it that. The MFA flow is an orchestration layer for login flows that simply replaces the original IdP login selection process with a different layer that's scriptable and more capable. It should have been the original design to start with.

It doesn't do MFA itself. Combining flows like Duo, or running flows that do multi-factor alone is what produces multi-factor authentication organically.

The rules you provide are what determine how authentication is done.

You are meant to control the system's behavior by associating custom Principal objects that in some cases represent AuthnContext classes with login flows and other configuration objects such as relying party overrides, metadata tags, etc. That is less brittle and is dynamic without requiring restarts to change every detail.

The orchestration, when done properly, automatically figures out what has to happen to satisfy a request by running the isAcceptable() methods scattered around the API, principally the one hanging off the MultiFactorAuthenticationContext object which is what the examples I have provided show how to do. They simply decide whether to run flows based on whether the previous results are already sufficient to complete the request.

When isAcceptable returns true, the results that have been produced are already sufficient to meet the requirements of a request, which by implication means there's no need to do anything else. When that's done, you get step-up automatically and the amount of configuration is extremely minimal and never has to be touched.

-- Scott


--
For Consortium Member technical support, see https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwiki.shibboleth.net%2Fconfluence%2Fx%2FcoFAAg&data=04%7C01%7Crullfig%40uic.edu%7C88ba1b4203e64625074408d90983e5c8%7Ce202cd477a564baa99e3e3b71a7c77dd%7C0%7C0%7C637551284374420403%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=QY%2BpX7CGWRahbQcGswjKMknMJzEWIZNQS93pwRz%2Bf4o%3D&reserved=0<https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwiki.shibboleth.net%2Fconfluence%2Fx%2FcoFAAg&data=04%7C01%7C%7Cfa435929b6f946e5c93d08d917ae00c0%7Cce6d05e13c5e4d6287a84c4a2713c113%7C0%7C0%7C637566859119300262%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=3nH5bUXf%2FpoI%2B4sData6RoLbHlJNoRE0JCb3rsNxYIk%3D&reserved=0>
To unsubscribe from this list send an email to users-unsubscribe at shibboleth.net
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://shibboleth.net/pipermail/users/attachments/20210516/a5d13b36/attachment.htm>


More information about the users mailing list