<!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>