Precollection of Username During Authentication
There are some use cases for which it’s essential to obtain a username prior to authentication rather than in conjunction with collecting a password. Usually these cases involve scenarios in which the identity of the user (unauthenticated, obviously, so self-asserted by the user) is needed to determine how authentication should happen. It’s obviously essential that a user not be able to subvert a policy in some way; typically this approach is used in order to allow a user to self-nominate that they have the ability to do something that not all users are able to do, such as use a token-based method (because they have such a token).
The IdP does not have such a feature built-in, but it’s relatively simple to construct a simple flow and view template to peform this step and leverage the information in the MFA login flow. This article includes examples showing how to do this while performing an attribute lookup based on the identity to make a login method determination (some scripting is needed but no Java).
Custom Flow Example
The first step is to produce a custom flow to be added to the IdP that prompts for the username. This is not a login flow, but will be executed by the MFA flow as an initial step. The file containing the flow definition must have the appropriate name and be dropped into a like-named subdirectory under the IdP’s flows folder. The flow definition itself is fully “declarative” and does not rely on any Java code.
The flow definition assumes that the (as with the password form convention), the username field will be named “j_username”. In plain English, what the flow does is present a view template to prompt for the username, pull it from the servlet request as a parameter and store it in a dedicated context node designed for that purpose, and then loop back to the form if and only if the username was not supplied.
Place in flows/collectusername/collectusername-flow.xml
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
parent="authn.abstract">
<!--
Example flow to prompt for a username for examination by MFA flow.
-->
<view-state id="PromptForUsername" view="collect-username">
<on-render>
<evaluate expression="environment" result="viewScope.environment" />
<evaluate expression="opensamlProfileRequestContext" result="viewScope.profileRequestContext" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
<evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.CustomViewContext') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.CustomViewContext') : null" result="viewScope.custom" />
</on-render>
<transition on="proceed" to="ExtractUsernameFromFormRequest" />
</view-state>
<action-state id="ExtractUsernameFromFormRequest">
<evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getSubcontext(T(net.shibboleth.idp.authn.context.UsernameContext), true).setUsername(flowRequestContext.getExternalContext().getNativeRequest().getParameter('j_username'))" />
<evaluate expression="'proceed'" />
<!-- Let the validate action handle any problems later. -->