WS-Federation Custom SP Attribute Mapper in OpenAM

This solution article demonstrates how to setup a WS-Federation Identity Provider and Service Provider (also called Relying Party) in OpenAM with a custom attribute mapper specific to the SP. The objective is to demonstrate how to use an SP-specific attribute mapper using which you could add custom attributes to a set of claims sent over to a .NET app, or otherwise make those custom attributes available to the OpenAM Session.

WS-Federation IDP

Import the following metadata into OpenAM (instead of using the Wizard). The imported metadata would look like this:

WsFed IDP

IDP Metadata

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <Federation FederationID="wsfedIDP"
 xmlns="http://schemas.xmlsoap.org/ws/2006/12/federation">
 <TokenSigningKeyInfo> <ns1:SecurityTokenReference ns1:Usage="" xmlns:ns1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
 <ns2:X509Data xmlns:ns2="http://www.w3.org/2000/09/xmldsig#"> <ns2:X509Certificate>MIICQDCCAakCBEeNB0swDQYJKoZIhvcNAQEEBQAwZzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMQwwCgYDVQQKEwNTdW4xEDAOBgNVBAsTB09wZW5TU08xDTALBgNVBAMTBHRlc3QwHhcNMDgwMTE1MTkxOTM5WhcNMTgwMTEyMTkxOTM5WjBnMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExDDAKBgNVBAoTA1N1bjEQMA4GA1UECxMHT3BlblNTTzENMAsGA1UEAxMEdGVzdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArSQc/U75GB2AtKhbGS5piiLkmJzqEsp64rDxbMJ+xDrye0EN/q1U5Of+RkDsaN/igkAvV1cuXEgTL6RlafFPcUX7QxDhZBhsYF9pbwtMzi4A4su9hnxIhURebGEmxKW9qJNYJs0Vo5+IgjxuEWnjnnVgHTs1+mq5QYTA7E6ZyL8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQB3Pw/UQzPKTPTYi9upbFXlrAKMwtFf2OW4yvGWWvlcwcNSZJmTJ8ARvVYOMEVNbsT4OFcfu2/PeYoAdiDAcGy/F2Zuj8XJJpuQRSE6PtQqBuDEHjjmOQJ0rV/r8mO1ZCtHRhpZ5zYRjhRC9eCbjx9VrFax0JDC/FfwWigmrW0Y0Q==</ns2:X509Certificate>
 </ns2:X509Data>
 </ns1:SecurityTokenReference>
 </TokenSigningKeyInfo>
 <TokenIssuerName>urn:federation:wsfedIDP</TokenIssuerName>
 <TokenIssuerEndpoint>
 <ns3:Address
 xmlns:ns3="http://www.w3.org/2005/08/addressing">http://openam-idp-hostname:18080/openam/WSFederationServlet/metaAlias/wsfedIDP</ns3:Address>
 </TokenIssuerEndpoint>
 <TokenTypesOffered>
 <TokenType Uri="urn:oasis:names:tc:SAML:1.1"/>
 </TokenTypesOffered>
 <UriNamedClaimTypesOffered>
 <ClaimType Uri="http://schemas.xmlsoap.org/claims/UPN">
 <DisplayName>User Principal Name</DisplayName>
 </ClaimType>
 </UriNamedClaimTypesOffered>
 </Federation>

Note the use of the default “test” certificate alias above. You can easily switch it out with your own certificate.

Side bar: If you have trouble importing the IDP metadata due to the TokenSigningKeyInfo element, then you must update the sunKeyValue attribute under ou=sunFMWSFederationMetaDataService manually in the configuration store. This is because the WSFederation requires the TokenSigningKeyInfo elements if you want assertions signed (good idea mate!). The following picture shows the metadata instance attribute that must be updated with the full TokenSigningKeyInfo manually:

wsfedidp-instance

IDP Extended Metadata

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <FederationConfig FederationID="wsfedIDP" hosted="true"
 xmlns="urn:sun:fm:wsfederation:1.0:federationconfig">
 <IDPSSOConfig metaAlias="/wsfedIDP">
 <Attribute name="displayName">
 <Value>/wsfedIDP </Value>
 </Attribute>
 <Attribute name="upnDomain">
 <Value><openam-idp-hostname></Value>
 </Attribute>
 <Attribute name="signingCertAlias">
 <Value>test</Value>
 </Attribute>
 <Attribute name="autofedEnabled">
 <Value>false</Value>
 </Attribute>
 <Attribute name="autofedAttribute">
 <Value/>
 </Attribute>
 <Attribute name="assertionNotBeforeTimeSkew">
 <Value>600</Value>
 </Attribute>
 <Attribute name="assertionEffectiveTime">
 <Value>600</Value>
 </Attribute>
 <Attribute name="idpAuthncontextMapper">
 <Value>com.sun.identity.wsfederation.plugins.DefaultIDPAuthenticationMethodMapper</Value>
 </Attribute>
 <Attribute name="idpAccountMapper">
 <Value>com.sun.identity.wsfederation.plugins.DefaultIDPAccountMapper</Value>
 </Attribute>
 <Attribute name="idpAttributeMapper">
 <Value>com.sun.identity.wsfederation.plugins.DefaultIDPAttributeMapper</Value>
 </Attribute>
 <Attribute name="attributeMap">
 <Value/>
 </Attribute>
 </IDPSSOConfig>
 </FederationConfig>

WS-Federation SP

You could create the SP simply by using the OpenAM wizard under Federation. Your SP would look like this (note the Custom SP attribute mapper):

WSFed SP

 

Also import the SP remote and IDP remote metadata into different instances of OpenAM. That is left as an exercise to you!

Hint: 

SP Remote Metadata

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <Federation FederationID="wsfedSP"
 xmlns="http://schemas.xmlsoap.org/ws/2006/12/federation">
 <TokenIssuerName>urn:federation:wsfedSP</TokenIssuerName>
 <TokenIssuerEndpoint>
 <ns1:Address
 xmlns:ns1="http://www.w3.org/2005/08/addressing">http://openam-sp-hostname:28080/openam</ns1:Address>
 </TokenIssuerEndpoint>
 <SingleSignOutNotificationEndpoint>
 <ns2:Address
 xmlns:ns2="http://www.w3.org/2005/08/addressing">http://openam-sp-hostname:28080/openam</ns2:Address>
 </SingleSignOutNotificationEndpoint>
 </Federation>

Custom SP Attribute Mapper

We will not show the entire class but the pertinent code that builds a map of values to return is shown below:

        // build our own values
        Set<String> values1 = new HashSet();
        Set<String> values2 = new HashSet();
        Set<String> values3 = new HashSet();

        values1.add("claim1Value");
        values2.add("claim2Value");
        values3.add("claim3Value");

        // build our own claims and assign the values
        map.put("claim1", values1);
        map.put("claim2", values2);
        map.put("claim3", values3);

 

The CustomWsFedSPAttributeMapper.java needs to be added to the com.sun.identity.wsfederation.plugins package under the OpenFM module and the module must be compiled into the OpenFM-13.0.0.jar for deployment.

Code Changes

There were a few changes necessary in my setup to the following classes for this to work. I suggest you *not* make these changes and test the WS-Federation login first. If it fails with a 403 Forbidden, or a Signature Element error, then proceed to make these following changes.

FMSigProvider

This class is located in the com.sun.identity.saml2.xmlsig package. In the verify method, alter the code to check for an AssertionID attribute in the assertion XML string, if the ID attribute is not found.

public boolean verify(String xmlString, String idValue, X509Certificate senderCert) 
    throws SAML2Exception { 
...
        // JSHAH, for WSFed assertions that are missing the ID attribute
        // but instead, contain the AssertionID attribute
        if ( signedId == null || signedId == "" ) {
            signedId = ((Element) sigElement.getParentNode()).getAttribute(SAMLConstants.TAG_ASSERTION_ID);
            doc.getDocumentElement().setIdAttribute(SAMLConstants.TAG_ASSERTION_ID, true);
        } else {
            doc.getDocumentElement().setIdAttribute(SAML2Constants.ID, true);
        }
...

WSFederationMetaUtils

This class is located in the com.sun.identity.wsfederation.meta package. Rewrite the getAttribute method as follows:

public static String getAttribute(BaseConfigType config, String key)    {
        List list = config.getAttribute();
        for(Iterator iter = list.iterator(); iter.hasNext();) {
            AttributeType avp = (AttributeType)iter.next();
            // JSHAH Handle empty array in the assertion XML string
            if ( avp.getName().equals(key) ) {
               if (!avp.getValue().isEmpty()) {
                   return (String)avp.getValue().get(0);
               } else {
                   return null;
               }
            }
        }
        return null;
    }

RPSignInResponse

This class is activated on the SP and is responsible for handling the response from the IDP. Think of it as the last gate before successful sign-on and session creation on the SP. It is also responsible for instantiating the SP Account Mapper and SP Attribute Mapper classes. In the process method, comment the following line:

Map attrMap = null;
//JSHAH, Map attributes even if the RSTR does not contain an AttributeStatement
//if (attrs != null) { 
        attrMap = attrMapper.getAttributes(attrs, userName,
        spEntityId, idpEntityId, realm);
//}

Please note that this final code change may not be necessary if you are building an Attribute map in the IDP and not require a custom SP attribute mapper. As you can see from the IDP picture above, I am not building an attribute map in the IDP and I do require a custom SP attribute mapper (check the title of this article!), so I preferred to make this change and force-invoke the custom SP attribute mapper. This should probably be a configuration setting!

Compile all these classes into the openam-federation-library module. Again, you might not need one or more of these code changes so please test incrementally, and only use the code change you actually need.

Disclaimer: None of the code changes are recommended for production use. This solution article and attached code samples are strictly intended for demonstration purposes only. Even with the suggested code and configuration in this article, your mileage may vary.