Problem
How can we enabled multiple delegated authentication service in one Salesforce Organisation ?
Background
As you may noticed, we can configure only one Delegated Authentication URL Service in the Single Sign On Setting of your Org. Now what’s happened if you have multiple different portal (with their own security mechanism) to integrate with Salesforce when you have only one Org instance and want to benefit from the delegated authentication Service provided by Salesforce ?
Answer
We can derive the facade pattern to implement this service. This service can be located anywhere in your network and must be publicly available (HTTPS is recommended here). You can even host this service in your Salesforce’s Org by exposing this service as a SOAP Service.
The delegated authentication Service
Please refer to the following documentation to understand the delegated authentication service:
- Understanding Delegated Authentication Single Sign-On
- Configuring Salesforce for Delegated Authentication
Salesforce provide top-down approach by providing the WSDL interface for this service. It is available in your Org (Setup| Develop | API | Download Delegated Authentication WSDL).
Proof of Concept
Let’s validate this by mocking a SOAPUI Service based on the WSDL provided by Salesforce, and use Groovy scripting functionality to route dynamically the request to the real implementation of this delegated service. For the purpose of this demo, I will used the sourceIp element in my SOAP request to identify the caller of this service. I would recommend to use either SOAP Header or HTTP header to store this “routing” information.
Below the screensot of my SOAPUI Settings:
Below groovy code i have used to this purpose. I have used the groovy-wslite as a WS client (just download the JAR and put it in your SOAPUI library extension to made to available: C:\Program Files\SmartBear\SoapUI-5.1.3\bin\ext\groovy-wslite-1.1.2.jar).
import wslite.soap.* import wslite.http.* import wslite.soap.SOAPClient import wslite.http.HTTPClientException import wslite.http.HTTPRequest import wslite.http.HTTPResponse import wslite.soap.SOAPClient import wslite.soap.SOAPFaultException import wslite.soap.SOAPResponse log.info "context=" + context def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context ) log.info "groovyUtils=" +groovyUtils log.info "mockRequest.requestContent=" + mockRequest.requestContent // create XmlHolder for request content def holder = new com.eviware.soapui.support.XmlHolder( mockRequest.requestContent ) def _username = holder["//*:username"] def _password = holder["//*:password"] def _sourceip = holder["//*:sourceIp"] log.info "_username=" + _username log.info "_password=" + _password log.info "_sourceip=" + _sourceip def DELEGATED_AUTH_URL_PORTAL1 = "http://PORTAL1/wsGCSBFO/DAuth.asmx" def DELEGATED_AUTH_URL_PORTAL2 = "https://PORTAL2/IMS-IdentityManager/services/AuthenticationService" def DELEGATED_AUTH_URL_PORTALDEFAULT = "https://PORTALDEFAULT/services/AuthenticationService" switch (_sourceip) { case ~/^PORTAL1$/: log.info "Routing to PORTAL1 Portal Delegated AuthN Service" context.myContentResponse = call_DelegatedAuthenticationFacade(DELEGATED_AUTH_URL_PORTAL1, _username, _password, _sourceip) break case ~/^PORTAL2$/: log.info "Routing to PORTAL2 Portal Delegated AuthN Service" context.myContentResponse = call_DelegatedAuthenticationFacade(DELEGATED_AUTH_URL_PORTAL2, _username, _password, _sourceip) break default: log.info "Routing to DEFAULT Portal Delegated AuthN Service" context.myContentResponse = call_DelegatedAuthenticationFacade(DELEGATED_AUTH_URL_PORTALDEFAULT, _username, _password, _sourceip) break } String call_DelegatedAuthenticationFacade(String serviceLocationUrl, String _username, String _password, String _sourceip) { def String defaultResponse = ''' <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <AuthenticateResult xmlns="urn:authentication.soap.sforce.com"> <AuthenticateResult> <Authenticated>false</Authenticated> </AuthenticateResult> </AuthenticateResult> </soap:Body> </soap:Envelope> ''' try { SOAPClient client = new SOAPClient(serviceLocationUrl) def response = client.send(SOAPAction:'') { body { Authenticate('xmlns':'urn:authentication.soap.sforce.com') { username(_username) password(_password) sourceIp(_sourceip) } } } HTTPRequest myRequest = response?.httpRequest HTTPResponse myResponse = response?.httpResponse return debug(defaultResponse, myRequest, myResponse) } catch (SOAPFaultException soapEx) { return debug(defaultResponse, soapEx.httpRequest, soapEx.httpResponse) } catch (HTTPClientException httpEx) { return debug(defaultResponse, httpEx.request, httpEx.response) } catch (Exception Ex) { return debug(defaultResponse, Ex.request, Ex.response) } } String debug(String defaultResponse, HTTPRequest request, HTTPResponse response) { log.info("*****************************************************************************************************") log.info("HTTPRequest $request with formatted content:\n${request?.contentAsString}") log.info("HTTPResponse $response with formatted content:\n${response?.contentAsString}") defaultResponse = "${response?.contentAsString}" log.info("RETURNED RESPONSE TO FACADE\n${defaultResponse}") return defaultResponse }
The script response content SOAP response generated by the groovy SCRIPT.
${myContentResponse}
Thanks for reading.