[Informational Post] How to extract attributes from SAML response, when using Foundry (Fabric) SAML Identity Provider?

When using a SAML Identity Provider in Volt MX Foundry, you will see that only FirstName, LastName, Email and Phone No fields can be mapped from the console.

How do we extract additional attributes from SAML Response, e.g. User Group, Custom Attributes, etc.?

After a successful Identity call using a SAML Identity Provider, the SAML response can be extracted using MBAASGateWayWrapper.

Create a Java Service to read the SAML Response and read required attributes. This service can be call after Identity call is successful.

#Step 1 - Retrieve the SAML Response and decode - Here is a sample code snippet.

MBAASGateWayWrapper gateWayWrapper = (MBAASGateWayWrapper) request.getAttribute(MWConstants.GATEWAY_WRAPPER_OBJECT);
if (!CommonUtil.isEmpty(gateWayWrapper)) {
	try {
		Gateway gateWay = gateWayWrapper.getGateway();
		Map<String, Object> securityAttributes = gateWay.getSecurityAttributes(<<IDENTITY_SERVICE_NAME>>, false);
		samlResponse = (String) securityAttributes.get("SAMLResponse");
		if(!CommonUtil.isEmpty(samlResponse)){
			// Decode SAML response which is in Base64 format
			samlResponse = CommonUtil.decodeBase64String(samlResponse);
			logger.debug("getSAMLResponse :decodedSAMLResponse: " + samlResponse);
		}
	} catch(GatewayException gEx) {
		logger.error("getSAMLResponse :GatewayException occurred while retrieving SAML response from ADFS: " + gEx.getMessage());
	}
}

#Step 2 - Parse the SAML Response to extract the required attributes

samlResponse, in the above code snippet, contains the full SAML response as String, which can be further parsed using XML Parsing logic. Here is a sample function to do so where samlResponse should be passed as the first parameter (xmlString).

private List<String> parseSAMLResponse(String xmlString, String xpathExpression) {
		logger.debug("parseSAMLResponse : xpathExpression:" + xpathExpression);
	List&lt;String&gt; userGroupList = new ArrayList&lt;String&gt;();
	
	if (!CommonUtil.isEmpty(xmlString)) {
		
		try {
			InputStream inputStream = new ByteArrayInputStream(xmlString.getBytes(Charset.forName("UTF-8")));
			
			DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
			Document document = documentBuilder.parse(inputStream);
			document.getDocumentElement().normalize();

			XPath xPath = XPathFactory.newInstance().newXPath();
			NodeList nodeList = (NodeList) xPath.compile(xpathExpression).evaluate(document, XPathConstants.NODESET);
			logger.debug("parseSAMLResponse : nodeList: " + nodeList);
			
			if (!CommonUtil.isEmpty(nodeList)) {
				for (int ndx = 0; ndx &lt; nodeList.getLength(); ndx++) {
					Node myNode = nodeList.item(ndx);
					logger.debug("parseSAMLResponse : myNode: " + myNode);
					
					if ((!CommonUtil.isEmpty(myNode)) &amp;&amp; (myNode.getNodeType() == Node.ELEMENT_NODE)) {
						Element myElement = (Element) myNode;
						String nodeValue = myElement.getTextContent();
						logger.debug("parseSAMLResponse : nodeValue: " + nodeValue);
						
						userGroupList.add(nodeValue);
					}
				}
			}
		} catch (ParserConfigurationException pcEx) {
			logger.error("parseSAMLResponse : ParserConfigurationException occurred while parsing SAML response: " + pcEx.getMessage());
		} catch (SAXException sEx) {
			logger.error("parseSAMLResponse : SAXException occurred while parsing SAML response: " + sEx.getMessage());
		} catch (IOException ioEx) {
			logger.error("parseSAMLResponse : IOException occurred while parsing SAML response: " + ioEx.getMessage());
		} catch (XPathExpressionException xpEx) {
			logger.error("parseSAMLResponse : XPathExpressionException occurred while parsing SAML response: " + xpEx.getMessage());
		}
	}
	
	logger.debug("parseSAMLResponse : userGroupList: " + userGroupList);
	
	return userGroupList;
}</code></pre>

This has been tested using Foundry (Fabric) cloud with Microsoft Azure AD (SAML) Identity Provider.

Hope it helps!