<html>
  <head>
    <meta content="text/html; charset=windows-1252"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <p>Hi Scott, <br>
    </p>
    <div class="moz-cite-prefix">sorry for the delay...<br>
      <br>
      On 15/12/2016 16:52, Cantor, Scott wrote:<br>
    </div>
    <blockquote
cite="mid:9846A6064BD102419D06814DD0D78DE11BF6C8BE@CIO-TNC-D2MBX02.osuad.osu.edu"
      type="cite">
      <blockquote type="cite">
        <pre wrap="">For normal authentication requests (no special requirements from SP or
custom relying-party config) we want to authenticate our users by:
  - their previous session if available and still active for achieving SSO
  - SPNEGO authentication if the client resides in a subnet so it can
request a Kerberos ST. Therefore we set an activationCondition based on
several subnets on the authn/SPNEGO flow. Since not every user-agent is
automatically able to use SPNEGO we rely on the cookie to "opt-in" and
to actually run SPNEGO. (from a user's perspective this provides SSO as
well)
</pre>
      </blockquote>
      <pre wrap="">
And so the initial choice is always "password" unless the user opts-in, correct?</pre>
    </blockquote>
    Correct<br>
    <blockquote
cite="mid:9846A6064BD102419D06814DD0D78DE11BF6C8BE@CIO-TNC-D2MBX02.osuad.osu.edu"
      type="cite">
      <pre wrap="">

</pre>
      <blockquote type="cite">
        <pre wrap="">  - password authentication if none of the above succeeded

Our IAM-system allows users to register a 2nd factor. Users can also
indicate whether they want to use this 2nd factor for every login or
only when the application requests for it. The former sets the attribute
eduPersonAssurance to a certain value, for the latter eduPersonAssurance
is empty.
</pre>
      </blockquote>
      <pre wrap="">
And the second factor doesn't stand alone, so it has to be combined with...either password or SPNEGO? Just password?</pre>
    </blockquote>
    The way we implemented it, we require the user to be know, either by
    password or SPNEGO (1st factor)<br>
    As a side note: our strong authn solution
    (<a class="moz-txt-link-freetext" href="https://www.n-auth.com/technology/">https://www.n-auth.com/technology/</a>) could be used as a true two
    factor solution, but that's not how we implemented it for now.
    <blockquote
cite="mid:9846A6064BD102419D06814DD0D78DE11BF6C8BE@CIO-TNC-D2MBX02.osuad.osu.edu"
      type="cite">
      <blockquote type="cite">
        <pre wrap="">If a SP requests for the authnContextClass
'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', but
the user already authenticated using its first and 2nd factor, he must
not re-authenticate because he already authenticated using a higher
level of trust.
</pre>
      </blockquote>
      <pre wrap="">
That works if your earlier method was password or password plus something else, but if you did SPNEGO, that technically isn't PasswordProtectedTransport. Once you start playing games like that, the IdP is going to deliberately make your life painful because it's not designed to lie. And once you manipulate the configuration to lie, that makes other lies automatic.

Of course, this is why using contexts that refer to specific technologies is a problem, and why requesting weaker methods is a bad idea.

But, if you want to configure SPNEGO as a flow to support that Principal type, it shouldn't do too much damage. Not every "lie" is the same and some won't cause as many weird side effects.</pre>
    </blockquote>
    Ok, I'll have to look into that.
    <blockquote
cite="mid:9846A6064BD102419D06814DD0D78DE11BF6C8BE@CIO-TNC-D2MBX02.osuad.osu.edu"
      type="cite">
      <blockquote type="cite">
        <pre wrap="">I was not using the ExtendedFlows feature of the Password flow to run
SPNEGO as a subflow. I now configured SPNEGO as an extended flow for
Password.
</pre>
      </blockquote>
      <pre wrap="">
I didn't recall whether that went hand in hand with that SWITCH built, it may have. I will grant that the extended flows stuff is awful, but that's because it was built as a stopgap until the MFA design was done. It's not as bad as that initial-authn disaster, but it's not great.

I suspect that you can implement whatever logic you're using the extended flow hook to do in the MFA flow, and if you can, that would probably be better. The MFA flow can do things with cookies just as easily as anything else can. Limiting the different types of mechanics used is better than trying to combine a lot of features and make it all work together.

The big advantage is all the logic around principal types. With the MFA flow, it's all much cleaner: you tell the system exactly which principals each flow supports, by themselves, and then the MFA flow can easily coordinate which ones to run for different situations.

With the extended flows thing, I think you have to hack in a lot of extra weirdness and lie about which principals are supported to get things to work. It's just not good.</pre>
    </blockquote>
    I will not use the extended flow feature, to not further complicate
    things.<br>
    <br>
    This is what I got so far (not everything works... questions below)<br>
    <br>
        <util:map id="shibboleth.authn.MFA.TransitionMap"><br>
            <entry key=""><br>
                <bean parent="shibboleth.authn.MFA.Transition"
    p:nextFlowStrategy-ref="selectFirstFactor" /><br>
            </entry><br>
    <br>
            <entry key="authn/SPNEGO"><br>
                <bean parent="shibboleth.authn.MFA.Transition"
    p:nextFlowStrategy-ref="checkSecondFactor" /><br>
            </entry><br>
    <br>
            <entry key="authn/Password"><br>
                <bean parent="shibboleth.authn.MFA.Transition"
    p:nextFlowStrategy-ref="checkSecondFactor" /><br>
            </entry><br>
    <br>
            <!-- An implicit final rule will return whatever the
    final flow returns. --><br>
        </util:map><br>
    <br>
        <import
    resource="../../system/flows/authn/spnego-authn-beans.xml" /><br>
        <br>
        <util:map id="customObjectsToSelectFirstFactor"><br>
            <entry key="SPNEGOAutoLoginManager"
    value-ref="SPNEGOAutoLoginManager" /><br>
            <entry key="SPNEGOActicationCondition"
    value-ref="shibboleth.SPNEGO.ActivationCondition" /><br>
        </util:map><br>
    <br>
        <!-- See which first factor we can use (SPNEGO or Password)
    --><br>
        <bean id="selectFirstFactor"
    parent="shibboleth.ContextFunctions.Scripted"
    factory-method="inlineScript"
    p:customObject-ref="customObjectsToSelectFirstFactor"><br>
            <constructor-arg><br>
                <value><br>
                <![CDATA[<br>
                nextFlow = 'authn/Password';<br>
    <br>
                logger =
    Java.type("org.slf4j.LoggerFactory").getLogger("selectFirstFactor");<br>
                logger.debug('Starting selectFirstFactor');<br>
    <br>
                authCtx =
input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");<br>
                mfaCtx =
authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");<br>
    <br>
                    //check to see if user opt-in for SPNEGO using
    cookie<br>
                    SPNEGOAutoLoginManager =
    custom.get("SPNEGOAutoLoginManager");<br>
                    isSPNEGOOptIn = SPNEGOAutoLoginManager.isEnabled();<br>
                    logger.debug('Check if SPNEGO opt-in was done
    through cookie: ' + isSPNEGOOptIn );<br>
                    //check to see if SPNEGO can be activated<br>
                    SPNEGOActicationCondition =
    custom.get("SPNEGOActicationCondition");<br>
                    isSPNEGOActivated =
    SPNEGOActicationCondition.apply(input);<br>
                    logger.debug('Check if SPNEGO can be activated: ' +
    isSPNEGOActivated );<br>
    <br>
                    if (SPNEGOAutoLoginManager.isEnabled() &&
    SPNEGOActicationCondition.apply(input)) {<br>
                        logger.debug('Opt-in for SPNEGO and
    activationcondition is set');<br>
                        nextFlow = 'authn/SPNEGO';<br>
                    }<br>
    <br>
                nextFlow;<br>
                ]]><br>
                </value><br>
            </constructor-arg><br>
        </bean><br>
    <br>
        <!-- Example script to see if second factor is required.
    --><br>
        <bean id="checkSecondFactor"
    parent="shibboleth.ContextFunctions.Scripted"
    factory-method="inlineScript"<br>
            p:customObject-ref="shibboleth.AttributeResolverService"><br>
            <constructor-arg><br>
                <value><br>
                <![CDATA[<br>
                nextFlow = 'authn/multifactor';<br>
    <br>
                logger =
    Java.type("org.slf4j.LoggerFactory").getLogger("checkSecondFactor");<br>
                logger.debug('Starting checkSecondFactor');<br>
    <br>
                    // Go straight to second factor if we have to, or
    set up for an attribute lookup first.<br>
                    authCtx =
input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");<br>
                    mfaCtx =
authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");<br>
                    c14nCtx =
input.getSubcontext("net.shibboleth.idp.authn.context.SubjectCanonicalizationContext");<br>
    <br>
                    //check is the current MFA-conftext is already
    acceptable, if so check if the user did an opt-in, otherwise finish
    (with value 'null')<br>
                    if (mfaCtx.isAcceptable()) {<br>
                        logger.debug('MFA context is already acceptable
    but now we will check if there is an opt-in set for multifactor');<br>
                        // Attribute check is required to decide if
    first factor alone is enough.<br>
                        resCtx = input.getSubcontext(<br>
                           
"net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext",
    true);<br>
                        username = null;<br>
                        logger.debug('Try to find username from
    SubjectCanonicalizationContext or SessionContext...');<br>
                        c14nCtx =
input.getSubcontext("net.shibboleth.idp.authn.context.SubjectCanonicalizationContext");<br>
                        sessionContext =
input.getSubcontext("net.shibboleth.idp.session.context.SessionContext");<br>
                        if (c14nCtx != null &&
    c14nCtx.getPrincipalName() != null) {<br>
                            username = c14nCtx.getPrincipalName();<br>
                            logger.debug('Found username from
    SubjectCanonicalizationContext: ' + username);<br>
                        } else if (sessionContext != null &&
    sessionContext.getIdPSession() != null) {<br>
                            username =
    sessionContext.getIdPSession().getPrincipalName();<br>
                            logger.debug('Found username from
    SessionContext: ' + username);<br>
                        }<br>
                        resCtx.setPrincipal(username);<br>
                        logger.debug('resolving attrs for ' + username);<br>
                       
    resCtx.getRequestedIdPAttributeNames().add("eduPersonAssurance");<br>
                        resCtx.resolveAttributes(custom);<br>
    <br>
                        // Check for an attribute that authorizes use of
    first factor.<br>
                        attribute =
    resCtx.getResolvedIdPAttributes().get("eduPersonAssurance");<br>
                        valueType = 
    Java.type("net.shibboleth.idp.attribute.StringAttributeValue");<br>
                        // set the nextFlow to null and exit MFA-flow
    when:<br>
                        // a: There are no values for eduPersonAssurance
    (attribute==null) -> not true in case of opt-in<br>
                        // b: eduPersonAssurance is set, but does not
    contain the opt-in value -> not true in case of opt-in<br>
                        if (attribute == null || (attribute != null
    && ! attribute.getValues().contains(new valueType(<a
      class="moz-txt-link-rfc2396E"
      href="https://example.com/auth/multifactor">"https://example.com/auth/multifactor"</a>))))
    {<br>
                            nextFlow = null;<br>
                        }<br>
    <br>
                        input.removeSubcontext(resCtx);   // cleanup<br>
                    }<br>
    <br>
                    nextFlow;   // pass control to second factor or end
    with the first<br>
                ]]><br>
                </value><br>
            </constructor-arg><br>
        </bean><br>
    <br>
    <br>
    Most use-cases are working. SPNEGO/Password is called when it
    should. I will certainly have to do some further tests to ensure
    everything is working (especially when SPNEGO is selected and then
    fails).<br>
    <br>
    But I already have a question as SPNEGO + multifactor flow fails
    because of the subject c14n when the MFA-flow finishes.<br>
    SPNEGO produces a subject like '<a class="moz-txt-link-abbreviated" href="mailto:username@domain.com">username@domain.com</a>' which has to be
    canonicalized to 'username'. I have 2 c14n active: c14n/attribute
    & c14n/simple.<br>
    When I only use one factor the c14n works, but if I add the 2nd
    factor, c14n fails.<br>
    I think c14n/simple won't run because it finds multiple subjects,
    after MFA-flow merged the results:<br>
    2017-01-10 16:59:18,989 - INFO
[net.shibboleth.idp.authn.impl.AttributeSourcedSubjectCanonicalization:189]
    - 10.34.165.120|Profile Action
    AttributeSourcedSubjectCanonicalization: Attribute sources [uid] did
    not produce a usable identifier<br>
    2017-01-10 16:59:18,989 - INFO
    [net.shibboleth.idp.authn.impl.SelectSubjectCanonicalizationFlow:62]
    - 10.34.165.120|Profile Action SelectSubjectCanonicalizationFlow:
    Moving incomplete flow c14n/attribute to intermediate set,
    reselecting a different one<br>
    2017-01-10 16:59:18,990 - DEBUG
    [net.shibboleth.idp.authn.impl.SelectSubjectCanonicalizationFlow:100]
    - 10.34.165.120|Profile Action SelectSubjectCanonicalizationFlow:
    Checking canonicalization flow c14n/simple for applicability...<br>
    2017-01-10 16:59:18,990 - DEBUG
    [net.shibboleth.idp.authn.impl.SelectSubjectCanonicalizationFlow:106]
    - 10.34.165.120|Profile Action SelectSubjectCanonicalizationFlow:
    Canonicalization flow c14n/simple was not applicable to this request<br>
    2017-01-10 16:59:18,990 - ERROR
    [net.shibboleth.idp.authn.impl.SelectSubjectCanonicalizationFlow:78]
    - 10.34.165.120|Profile Action SelectSubjectCanonicalizationFlow: No
    potential flows left to choose from, canonicalization will fail<br>
    <br>
    Do I need to write my own merge strategy for this?<br>
    Why doesn't the MFA-flow user the principalnames that already have
    been canonicalized?<br>
    <br>
    Thx,<br>
    Philip<br>
    <br>
    <br>
    <br>
    <br>
    <blockquote
cite="mid:9846A6064BD102419D06814DD0D78DE11BF6C8BE@CIO-TNC-D2MBX02.osuad.osu.edu"
      type="cite">
    </blockquote>
    <br>
  </body>
</html>