<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div class="moz-cite-prefix">On 6/18/2020 12:52 PM, Joseph Fischetti
wrote:<br>
</div>
<blockquote type="cite"
cite="mid:27998C13-9EF8-4C26-A05A-3CF16FADDB08@marist.edu">
<pre class="moz-quote-pre" wrap="">For some reason, error.vm doesn't appear to have the flowExecutionUrl available (and I'm not sure what it's supposed to be part of since I don't see where it's set during login.vm).
That said, this should more or less achieve exactly what I'm trying to do. I'm using attribute-resolver to figure out whether to send the user to SP-B or not, and if the user goes to SP-B and does that they should do, the attribute will be set appropriately. As long as the attribute-resolver is executed after hitting the flowExecutionUrl (assuming SSO takes over, as you said), it should work fine.
</pre>
</blockquote>
<p>You end up needing to reinvoke the resolver to pick up the
changed value. I'll throw our our expired password flow here to
give you an idea of how we put it together. Note the new view
state we defined, and that we push the flowExecutionUrl into it.
There is probably some extra noise that could be cleaned up.
Enable it by adding it to an appropriate relying party definition
with e.g. <span class="pl-e">p</span><span class="pl-e">:</span><span
class="pl-e">postAuthenticationFlows</span>=<span class="pl-s"><span
class="pl-pds">"</span>expiredpw<span class="pl-pds">".</span></span></p>
<p><tt> <decision-state id="CheckPasswordExpired"></tt><tt><br>
</tt><tt> <on-entry></tt><tt><br>
</tt><tt> <evaluate expression="ResolveAttributes" /></tt><tt><br>
</tt><tt> <evaluate
expression="isPasswordExpired.apply(opensamlProfileRequestContext)"
result="flowScope.ispwexpired" /></tt><tt><br>
</tt><tt> <evaluate
expression="isPasswordChangeSP.apply(opensamlProfileRequestContext)"
result="flowScope.ispwchangesp" /></tt><tt><br>
</tt><tt> </on-entry></tt><tt><br>
</tt><tt> </tt><tt><br>
</tt><tt> <if test="ispwexpired &&
!ispwchangesp"</tt><tt><br>
</tt><tt> then="PasswordExpired" else="CleanUpContexts"
/></tt><tt><br>
</tt><tt> </decision-state></tt><tt><br>
</tt><tt><br>
</tt><tt> <view-state id="PasswordExpired"
view="expired-password"></tt><tt><br>
</tt><tt> <on-render></tt><tt><br>
</tt><tt> <evaluate expression="environment"
result="viewScope.environment" /></tt><tt><br>
</tt><tt> <evaluate
expression="opensamlProfileRequestContext"
result="viewScope.profileRequestContext" /></tt><tt><br>
</tt><tt> <evaluate
expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))"
result="viewScope.authenticationContext" /></tt><tt><br>
</tt><tt> <evaluate
expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationErrorContext))"
result="viewScope.authenticationErrorContext" /></tt><tt><br>
</tt><tt> <evaluate
expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationWarningContext))"
result="viewScope.authenticationWarningContext" /></tt><tt><br>
</tt><tt> <evaluate
expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.LDAPResponseContext))"
result="viewScope.ldapResponseContext" /></tt><tt><br>
</tt><tt> <evaluate
expression="T(net.shibboleth.utilities.java.support.codec.HTMLEncoder)"
result="viewScope.encoder" /></tt><tt><br>
</tt><tt> </on-render></tt><tt><br>
</tt><tt> <transition on="proceed"
to="RetryExpiredCheck" /></tt><tt><br>
</tt><tt> </view-state></tt><tt><br>
</tt><tt> <action-state id="RetryExpiredCheck"></tt><tt><br>
</tt><tt> <evaluate
expression="opensamlProfileRequestContext.removeSubcontext(T(net.shibboleth.idp.attribute.context.AttributeContext))"/></tt><tt><br>
</tt><tt> <evaluate
expression="opensamlProfileRequestContext.removeSubcontext(T(net.shibboleth.idp.attribute.resolution.context.AttributeResolutionContext))"/></tt><tt><br>
</tt><tt> <transition to="CheckPasswordExpired"/></tt><tt><br>
</tt><tt> </action-state></tt><tt><br>
</tt><tt> <action-state id="CleanUpContexts"></tt><tt><br>
</tt><tt> <evaluate
expression="opensamlProfileRequestContext.removeSubcontext(T(net.shibboleth.idp.attribute.context.AttributeContext))"/></tt><tt><br>
</tt><tt> <evaluate
expression="opensamlProfileRequestContext.removeSubcontext(T(net.shibboleth.idp.attribute.resolution.context.AttributeResolutionContext))"/></tt><tt><br>
</tt><tt> <transition to="proceed"/></tt><tt><br>
</tt><tt> </action-state></tt><tt><br>
</tt></p>
<p>And a snippet of the beans file:</p>
<p><tt> <bean id="isPasswordChangeSP"
parent="shibboleth.Conditions.RelyingPartyId"></tt><tt><br>
</tt><tt> <constructor-arg></tt><tt><br>
</tt><tt> <list></tt><tt><br>
</tt><tt>
<value><a class="moz-txt-link-freetext" href="https://sp-b-test">https://sp-b-test</a>...</value></tt><tt><br>
</tt><tt>
<value><a class="moz-txt-link-freetext" href="https://sp-b-prod">https://sp-b-prod</a>...</value></tt><tt><br>
</tt><tt> </list></tt><tt><br>
</tt><tt> </constructor-arg></tt><tt><br>
</tt><tt> </bean></tt><tt><br>
</tt><tt><br>
</tt><tt> <bean id="isPasswordExpired"
class="edu.umn.oit.idm.shib.RegexAttributePredicate" <!--
there is a built-in predicate or condition for this now --></tt><tt><br>
</tt><tt> p:pat="^[Xs].*" p:attributeId="umnPasswordStatus"</tt><tt><br>
</tt><tt>
p:attributeContextLookupStrategy-ref="shibboleth.ChildLookupOrCreate.AttributeContext"/></tt><tt><br>
</tt><tt><br>
</tt><tt> <bean id="ResolveAttributes"</tt><tt><br>
</tt><tt>
class="net.shibboleth.idp.profile.impl.ResolveAttributes"
scope="prototype" <!-- WARNING: this instances an "impl"
bean which is not guaranteed to work across idp releases --></tt><tt><br>
</tt><tt>
c:resolverService-ref="shibboleth.AttributeResolverService"</tt><tt><br>
</tt><tt> p:maskFailures="true"
p:attributesToResolve="umnPasswordStatus"</tt><tt><br>
</tt><tt>
p:attributeContextCreationStrategy-ref="shibboleth.ChildLookupOrCreate.AttributeContext"/>
<!-- this tells it to put these attributes in a separate
context --></tt></p>
<p>And the corresponding expired-password view:</p>
<p><tt>#parse("header.vm")</tt><tt><br>
</tt><tt><h1 class="text-center">Password has
expired</h1></tt><tt><br>
</tt><tt><div class="row"></tt><tt><br>
</tt><tt> <div class="col-md-8 col-md-offset-2
col-xs-12"></tt><tt><br>
</tt><tt> <p class="idp3_confirmation-message">Your
password has expired and must be changed before</tt><tt><br>
</tt><tt> you can proceed to the application.</p></tt><tt><br>
</tt><tt>
<p>#springMessageText("idp.login.changePassword", "To
create a new password now, go to")</tt><tt><br>
</tt><tt> <strong><a
href=<a class="moz-txt-link-rfc2396E" href="https://example.edu/change-password">"https://example.edu/change-password"</a>
target="_blank"><a class="moz-txt-link-freetext" href="https://example.edu/change-password">https://example.edu/change-password</a></a></strong>.</p></tt><tt><br>
</tt><tt> <p>After you change your password, click
<a
href="$flowExecutionUrl&_eventId_proceed=1">Continue</a>.</p></tt><tt><br>
</tt><tt> </div></tt><tt><br>
</tt><tt></div></tt><tt><br>
</tt><tt>#parse("footer.vm")</tt></p>
<p><br>
</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>