PingFederate : Include a complex payload in the SAML assertion

Scenario :
You need to send a complex JSON payload in the SAML. The payload would depend upon the contextual data requested through a querystring . Below is the SP connection would look like:-

https://sso.idp.com/idp/startSSO.ping?PartnerSpId=https://www.serviceprovider.com&Request_Payload={“Param1″:”9999″,”Param2″:”8787879″,”Param3″:”Test”}

The input context is  in the querystring which is a JSON:-
{“Param1″:”9999″,”Param2″:”8787879″,”Param3″:”Test”}

Above request JSON when passed to a service will give a JSON response like below :-

"data": {
"level1": "some data",
"level2": "some data11",
"level3": {
    "level131": "some data 13",
    "level132": [{
        "level1321": {
            "level13211": {
                "level132111": "False",
                "level132112": "True"
            },
            "level13212": "some data",
            "level13213": "some data"
        },
        "level1322": "some data"
    },
    ],
    "level133": "test data",
    "level134": "test data",
    }
}

The output needs to be included in the SAML as a value for a SAML attribute. In this case the attribute is named as “Payload”. When we have implemented the solution, below is the part of the SAML response what It would like showing the attribute key “Payload” and the value from the service.

<saml:AttributeStatement>
      <saml:Attribute Name="Payload" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
        <saml:AttributeValue xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
			"data": {
			"level1": "some data",
			"level2": "some data11",
			"level3": {
		    "level131": "some data 13",
		    "level132": [{
        	"level1321": {
            	"level13211": {
                "level132111": "False",
                "level132112": "True"
            },
            "level13212": "some data",
            "level13213": "some data"
        	},
        	"level1322": "some data"
    		},
    		],
    		"level133": "test data",
 	  	 	"level134": "test data",
    		}
			}
		</saml:AttributeValue>
      </saml:Attribute>
</saml:AttributeStatement>

Now on a first thought one would think this could be done using a custom data-source in PingFederate. However, a custom data-source does not have access to the HTTP request context and therefore cannot access the querystring.
One way to solve this to use an OGNL expression for the “Payload” attribute. The OGNL expression has access to the HttpRequest. So – not only we can get the querystring, but also can get the authentication cookies from the request which may be required by the payload service.

#ServiceURL="http://example.com/payloadservice", 
//Get all the cookies
#cookies=#this.get("context.HttpRequest").getObjectValue().getCookies(), 
#cookiesMap=new java.util.HashMap(), 
#cookies.{ #cookie=#this, #cookiesMap.put(#cookie.getName(),#cookie.getValue()) },  

// Get the querystring
#querystring = #this.get("context.HttpRequest").getObjectValue().getQueryString(), 
#indexOfPayLoadKey = #querystring.indexOf("&Request_Payload="), 
#actualPayload=#querystring.substring(#indexOfPayLoadKey), 

// Get the actual payload request
#actualPayload=#actualPayload.replace("&Request_Payload=",""), 

/* Via a service call, call the service with the payload request 
and get the payload response*/
#restPayloadServiceHelper = new com.pingfederate.utils.payloadservices.RESTPayloadServiceHelper(#ServiceURL), 
restPayloadServiceHelper.GetServiceResponsePayload(#actualPayload, #cookiesMap)

 

image

Below is the skeleton of the helper class the OGNL expression is using. The jar file for the class needs to be put in the “\pingfederate\server\default\deploy” folder and the PingFederate service needs to be restarted.

/*
The constructor takes the URL.
The method GetServiceResponse takes the payload and also the cookie collection
 */

public class RESTPayloadServiceHelper {
    
    String serviceURL = "";
    JSONObject jsonPayloadRequest = null;
    public RESTPayloadServiceHelper(String serviceURL)
    {
        if(serviceURL!= null || serviceURL.length() > 0)
        {
            this.serviceURL = serviceURL;
        }
        else
        {
            throw new IllegalArgumentException(" The supplied service URL " + serviceURL + " is not valid");
        }
    }
    
    public String GetServiceResponsePayload(String adhocPayloadString, HashMap<String,String> cookieCollection) throws UnknownHostException, IOException
    {
        // Call the service and return the payload response
    }

}

, ,

  1. #1 by Matt Tebo on June 21, 2018 - 3:35 pm

    Ashish – great post. I’m going to try and implement this for a project I’m working on. Do you have any sample code for the RESTPayloadServiceHelper?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Random Thoughts

The World as I see it

Simple Programmer

Making The Complex Simple

Ionic Solutions

Random thoughts on software construction, design patterns and optimization.

Long (Way) Off

A tragic's view from the cricket hinterlands

%d bloggers like this: