[SalesForce] Dynamically adding custom attributes to SAML response

I've implemented Single Sign On with Salesforce as Identity Provider scenario using SAML. I want to pass additional attributes via SAML response to the Service Provider. Out of the box the Connected App allows adding custom attributes based on User/Profile/System objects:

enter image description here

I want to add attributes from other objects.

Is it even possible? Should I implement some interface to have specific fields visible?

Best Answer

Looks like you pretty much have covered all the steps an admin can perform to configure salesforce as an Identity provider. He can update the custom attributes to be sent back as part of assertion, but is limited to available dropdown options provided by Salesforce (Mentioned in your question).

However, there is a possible way to extend the custom attribute response using code. Salesforce allows developers to extend the connected app using Custom Connected App Handler. This class needs to extend ConnectedAppPlugin Class to extend the Connect app behavior, as mentioned below:

Contains methods for extending the behavior of a connected app, for example, customizing how a connected app is invoked depending on the protocol used. This class gives you more control over the interaction between Salesforce and your connected app.

Create a class following the below sample code, to populate a map of custom attributes from your business data.

global class ConnectedAppPluginExample extends Auth.ConnectedAppPlugin{

    // Return a user’s permission set assignments
    global override Map<String,String> customAttributes(Id userId, Map<String,String> formulaDefinedAttributes) 
    {  
        //Query Data from your business objects
        List< ObjectName > businessOjbects = [Select [fieldNames] From ObjectName Where [certain conditions]];

        for ( ObjectName businessData :businessOjbects)
        {
            //Just a sample map populated with Unique Name and id
            formulaDefinedAttributes.put(businessData.Name,businessData.id);
        }
        return formulaDefinedAttributes;
    }
}

You can find more details here about the same https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_class_Auth_ConnectedAppPlugin.htm#apex_class_Auth_ConnectedAppPlugin

Update

Out of curiosity, I tried to implement it by myself (Between two SF orgs) and was able to pass additional SAML attributes as part of response.

This is what I did -

  1. Follow steps listed down in this article to implement SSO between two SF orgs https://developer.salesforce.com/page/Implementing_Single_Sign-On_Across_Multiple_Organizations
  2. Created a sample ConnectedApp handler class and selected it on the Connected App under Custom Connected App Handler section:
global class ConnectedAppPluginExample extends Auth.ConnectedAppPlugin{
    global override Map<String,String> customAttributes(Id userId, Map<String,String> formulaDefinedAttributes) 
    {  
        formulaDefinedAttributes.put('test','test');
        return formulaDefinedAttributes;
    } 
}

Note: There is a catch here. For some reason this class with version >37 is not working. So update its version to 36.

  1. Installed google chrome extension to track SAML assertions. https://chrome.google.com/webstore/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace
  2. Tested the SSO and verified the SAML response with the custom attribute. Test attribute was present in the SAML response.

Update 2

Morover if you want to use higher API version you have to override customAttributes method like this:

  global override Map<String,String> customAttributes(Id userId, Id 
  connectedAppId, Map<String,String> formulaDefinedAttributes, 
  Auth.InvocationContext context) 
      {  ...

With additional connectedAppId and context attributes.

<saml:Attribute Name="userId" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
    <saml:AttributeValue
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">0057F000000dRXp 
    </saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="username" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
    <saml:AttributeValue
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">sfsmarty@fsl.com
    </saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
    <saml:AttributeValue
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">sfsmarty@yahoo.co.in
    </saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="is_portal_user" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
    <saml:AttributeValue
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">false
    </saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="test" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
    <saml:AttributeValue
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">test 
    </saml:AttributeValue>
</saml:Attribute>
Related Topic