The Shibboleth IdP V3 software has reached its End of Life and is no longer supported. This documentation is available for historical purposes only. See the IDP4 wiki space for current documentation on the supported version.

AttributePostLoginC14NConfiguration

Current File(s): conf/c14n/attribute-sourced-subject-c14n-config.xml

Format: Native Spring

Overview

The c14n/attribute post-login subject canonicalization flow extracts a username from an IdPAttribute resolved using the attribute resolution function within the IdP. A common use for this flow is to perform a directory or database lookup to map information derived from the authentication process (e.g. the subject of a certificate) into a different value for use within the IdP.

General Configuration

Use conf/c14n/attribute-sourced-subject-c14n-config.xml to configure this flow, along with the AttributeResolverConfiguration. Typically you will supply a list of attributes to resolve and a list of attributes to search for in the results. The first such attribute with a suitable value will supply the username to return.

By default, the only transform applied to the result is a trim of leading or trailing whitespace. Case-folding and regular expression replacements can be added, per the reference section below.

Attribute Resolver Considerations

Usually, the attribute resolver relies on the canonical principal name to do its work (the value of the $resolutionContext.principal variable in scripts or search templates). By definition that isn't possible here (this is the process that provides that value later), but with V3.3 and above the value of that variable in the resolver can be set by means of a function bean (e.g., using a script) named shibboleth.c14n.attribute.PrincipalNameLookupStrategy. In most cases this function would peek inside the Java Subject being canonicalized and pull out custom bits of information.

The Java Subject can be accessed via an expression of the form:

profileContext.getSubcontext("net.shibboleth.idp.authn.context.SubjectCanonicalizationContext").getSubject()

While it would not be a typical case to need to access a value entered by a user except in one specific case noted below, that value will be present inside the Subject as a principal object of type net.shibboleth.idp.authn.principal.UsernamePrincipal. In most other cases, it's simpler to just leave such a value as the canonical principal name and adjust your resolver configuration to accomodate whatever that might be than to try and use the resolver ahead of time to turn it into some other value.

One Common Use of Attribute-based Canonicalization

One situation where c14n/attribute post-login subject canonicalization is frequently useful:

  • you need to use a Duo or other 2FA authentication plugin that would rely on the username entered by the user to send to the 2FA service
  • you want to allow the user to enter their username as one of several different identifiers when they do the initial password authentication

In that case, the same user could end up having multiple different identifiers with the 2FA service, because whichever one they choose to enter at login would be the one sent. This can be solved by using this attribute-based canonicalization, so that you ensure the same user identifier is sent to the 2FA service every time.

The following example (which requires V3.3 or above) illustrates how one could do this, where one has chosen to allow the user to enter either their uid or their e-mail address as the username. Changes need to be made to all of the following, and sample config highlights the changes to each that would enable such. (One would also need to configure your authn handler to allow for either input, such as changing idp.authn.LDAP.userFilter to '(|(uid={user})(mail={user}))'.)

  • idp_home/conf/c14n/subject-c14n.xml
  • idp_home/conf/c14n/attribute-sourced-subject-c14n-config.xml
  • idp_home/conf/attribute-resolver.xml

The example takes the user's input name and supplies it to the attribute resolver to do a search of LDAP against the two possible candidate attributes and returns the uid attribute from the directory so that either input maps to a fixed output. There are other ways to configure the resolver that might be more aligned to other searches you need to perform, cache results, etc. but this illustrates the idea.

subject-c14n.xml
        <!-- Remove comment tags to enable Attribute-based c14n -->
        <bean id="c14n/attribute" parent="shibboleth.PostLoginSubjectCanonicalizationFlow" />
attribute-sourced-subject-c14n-config.xml
    <!--
    A list of attributes to resolve for normalizing the subject. For example, you might
    intend to lookup a name in a directory based on what the user entered. You can make this
    an empty list if you just want to resolve everything you normally would.
    -->
    <util:list id="shibboleth.c14n.attribute.AttributesToResolve">
        <value>uid</value>
    </util:list>

    <!--
    A list of attributes to search for a value to produce as the normalized subject name.
    This will normally be something you resolve above.
    -->
    <util:list id="shibboleth.c14n.attribute.AttributeSourceIds">
        <value>uid</value>
    </util:list>

	<bean id="shibboleth.c14n.attribute.PrincipalNameLookupStrategy"
            parent="shibboleth.ContextFunctions.Scripted"
            factory-method="inlineScript">
        <constructor-arg>
            <value>
            <![CDATA[
            var principalName = null;
            var subject = profileContext.getSubcontext("net.shibboleth.idp.authn.context.SubjectCanonicalizationContext").getSubject();
            var princs = subject.getPrincipals(Java.type("net.shibboleth.idp.authn.principal.UsernamePrincipal").class);
            if (princs.size() == 1) {
                principalName = princs.iterator().next().getName();
            }
            principalName;
            ]]>
            </value>
        </constructor-arg>
    </bean>
attribute-resolver.xml
 
    <resolver:AttributeDefinition xsi:type="ad:Simple" id="uid" sourceAttributeID="uid">
        <resolver:Dependency ref="myLDAP" />
        <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:uid" />
        <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" />
    </resolver:AttributeDefinition>
 
....
 
    <!-- Example LDAP Connector -->
    <resolver:DataConnector id="myLDAP" xsi:type="dc:LDAPDirectory"
       ldapURL="ldap://localhost:10389"
       baseDN="ou=People,dc=example,dc=edu"
       principal="cn=admin,dc=example,dc=edu"
       principalCredential="password">
       <dc:FilterTemplate>
           <![CDATA[
               (|(uid=$resolutionContext.principal)(mail=$resolutionContext.principal))
           ]]>
       </dc:FilterTemplate>
       <dc:ReturnAttributes>uid</dc:ReturnAttributes>
    </resolver:DataConnector>

Reference

Beans

The beans defined in conf/c14n/attribute-sourced-subject-c14n-config.xml follow:

Bean IDTypeDefaultFunction
shibboleth.c14n.attribute.AttributesToResolve                      List<String>
A list of attributes to resolve (an empty list directs the resolver to resolve everything it can)
shibboleth.c14n.attribute.AttributeSourceIdsList<String>
A list of attributes to search for in the results, looking for a StringAttributeValue or ScopedStringAttributeValue
shibboleth.c14n.attribute.PrincipalNameLookupStrategy 3.3Function<ProfileRequestContext,String>
Provides a principal name value for the AttributeResolutionContext during attribute resolution (i.e., $resolutionContext.principal will be set)
shibboleth.c14n.attribute.LowercaseBooleanfalseWhether to lowercase the username
shibboleth.c14n.attribute.UppercaseBooleanfalseWhether to uppercase the username
shibboleth.c14n.attribute.TrimBooleantrueWhether to trim leading and trailing whitespace from the username
shibboleth.c14n.attribute.TransformsPair<String,String>
Pairs of regular expressions and replacement expressions to apply to the username