<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <div class="moz-cite-prefix">On 4/17/2024 8:36 AM, Cantor, Scott
      wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:65B56075-3F75-4ACE-BA57-B0597CA8F73D@osu.edu">
      <blockquote type="cite">
        <pre class="moz-quote-pre" wrap="">How about an entry in the transition map from authn/External to catch 
NoCredentials (which for me is the "usual" case of no special SSO 
cookies present) that runs a next flow strategy function that 
clears/overwrites whatever is getting passed to Password 
(AuthenticationError/WarningContext? Spring event?)?
</pre>
      </blockquote>
      <pre class="moz-quote-pre" wrap="">
The event should not be messed with. I don't think that the others would be there, but if they are then yes, they could be cleared or removed without hurting anything.</pre>
    </blockquote>
    <p><span style="white-space: pre-wrap">
</span></p>
    <p><span style="white-space: pre-wrap">Closing the loop - this seems to be working for me.</span></p>
    <p><span style="white-space: pre-wrap">mfa-authn-config.xml excerpt:</span></p>
    <p><span style="white-space: pre-wrap"><font face="monospace">        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/External" />
        </entry>

        <entry key="authn/External">
            <bean parent="shibboleth.authn.MFA.Transition">
                <property name="nextFlowStrategyMap">
                    <map>
                        <!-- if external auth succeeded, stop here, else goto password -->
                        <entry key="proceed" value="proceed" />
                        <entry key="*" value-ref="clearExternalError" />
                    </map>
                </property>
            </bean>
        </entry>
[...]
<bean id="clearExternalError" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
        <constructor-arg>
            <value>
            <![CDATA[
                authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
                errCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationErrorContext");
                if (errCtx != null) {
                   errs = errCtx.getClassifiedErrors();
                   if (errs != null && errs.contains("NoCredentials")) {
                      authCtx.removeSubcontext(errCtx);
                   }
                }
                "authn/Password";   // regardless, go to password flow next
            ]]>
            </value>
        </constructor-arg>
</bean>
</font></span></p>
    <p><span style="white-space: pre-wrap"></span></p>
    <hr width="100%" size="2"><span style="white-space: pre-wrap">external.jsp:</span>
    <p><span style="white-space: pre-wrap"><font face="monospace"><%@ page pageEncoding="UTF-8" %>
<%@ page import="net.shibboleth.idp.authn.ExternalAuthentication" %>
<%@ page import="net.shibboleth.idp.authn.ExternalAuthenticationException" %>
<%@ page import="org.opensaml.profile.context.ProfileRequestContext" %>
<%@ page import="java.time.Instant" %>
<%@ page import="java.security.Principal" %>
<%@ page import="javax.security.auth.Subject" %>
<%@ page import="net.shibboleth.idp.authn.principal.UsernamePrincipal" %>
<%@ page import="net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal" %>
<%@ page import="org.opensaml.saml.saml2.core.AuthnContext" %>
<%@ page import="net.shibboleth.idp.session.context.SessionContext" %>
<%@ page import="net.shibboleth.idp.session.IdPSession" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.HashSet" %>


<%
try {
    final String key = ExternalAuthentication.startExternalAuthentication(request);


    /* check for an existing IdPSession inside a possibly null SessionContext */
    final ProfileRequestContext prc = ExternalAuthentication.getProfileRequestContext(key, request);
    final SessionContext sc = prc.getSubcontext(SessionContext.class);
    IdPSession idpsession = null;
    if (sc != null) {
        idpsession = sc.getIdPSession();
    }


    /* only check cookie if no existing session - this SSO only works for the first SP you hit */
    if (idpsession == null) {
        try {
            final CAHUser cahUser = processSSOCookies(request);
            if (cahUser != null) {
              /* got a yummy cookie, lets make a subject out of it */
              HashSet<Principal> principals = new HashSet<Principal>();
              principals.add(new UsernamePrincipal(cahUser.getUid()));
              principals.add(new AuthnContextClassRefPrincipal(AuthnContext.UNSPECIFIED_AUTHN_CTX));
              final Subject subj = new Subject(false, principals, Collections.emptySet(), Collections.emptySet());
              request.setAttribute(ExternalAuthentication.SUBJECT_KEY, subj);
              request.setAttribute(ExternalAuthentication.AUTHENTICATION_INSTANT_KEY, Instant.ofEpochSecond(cahUser.getTimestamp()));
              request.setAttribute(ExternalAuthentication.PREVIOUSRESULT_KEY, true);
            } else {
              /* got yucky or no cookie */
              /* do nothing; idp will throw NO_CREDENTIALS event which we want */
            }
        } catch (final CAHException e) {
             request.setAttribute(ExternalAuthentication.AUTHENTICATION_EXCEPTION_KEY, new ExternalAuthenticationException("CAH handling error", e));
        }
    } else {
        /* do nothing; idp will throw NO_CREDENTIALS event which we want */
    }
    ExternalAuthentication.finishExternalAuthentication(key, request, response);
} catch (final ExternalAuthenticationException e) {
    throw new ServletException("Error processing external authentication request", e);
}
%></font>

</span></p>
    <p><span style="white-space: pre-wrap">There are no methods to remove ClassifiedErrors from the AuthenticationErrorContext, so I just remove the whole context if I find the NoCredentials present.</span></p>
    <p><span style="white-space: pre-wrap">Thanks Scott for the direction as always, and thanks Steve taking a stab at it too!
</span></p>
    <p><span style="white-space: pre-wrap">
</span></p>
    <pre class="moz-signature" cols="72">-- 
%%  Christopher A. Bongaarts   %%  <a class="moz-txt-link-abbreviated" href="mailto:cab@umn.edu">cab@umn.edu</a>          %%
%%  OIT - Identity Management  %%  <a class="moz-txt-link-freetext" href="http://umn.edu/~cab">http://umn.edu/~cab</a>  %%
%%  University of Minnesota    %%  +1 (612) 625-1809    %%
</pre>
  </body>
</html>