SOAP - Custom authentication when user/password is in Body
I have to create a SOAP WebService that receives the username/password as part of a field in the Request. I have no control of the client's application.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org">
<soapenv:Body>
<tem:ProcessRequest>
<!--Optional:-->
<tem:myRequest>
<tem:NomUtilisateur>ACGendron</tem:NomUtilisateur>
<tem:MotDePasse>MyPassword</tem:MotDePasse>
<!-- Other request fields -->
<tem:PrenomMere>?</tem:PrenomMere>
<tem:NumeroTelephone>?</tem:NumeroTelephone>
<tem:CodeInstallation>1</tem:CodeInstallation>
</tem:myRequest>
</tem:ProcessRequest>
</soapenv:Body>
</soapenv:Envelope>
...I'm looking for advice on the best way to authenticate the user. I could verify that the user/password is correct using %session.Login(user, pass) in my WebMethod and throwing a SoapFault when this fails but I'm not sure it's the best way of doing things in my %SOAP.WebService. Perhaps there is a callback I can implement or any properties I should set (Username, UsernameToken, etc.) for the proper SOAP Service internal work
Any help is welcome,
Kind regards
Comments
I ended-up creating a %CSP.Rest class that acts as a proxy which reordered the fields in order to generate a standard SOAP request with the proper headers. Works great for a minimum of code.
Hi Andre-Claude, could you please explain your solution. I have a similar problem and could need a explination. Because i have also some troubles with the WS-Security...
Here are a few code samples that could help :
Class YourPackage.REST.CSoapAuthenticator Extends %CSP.REST
{
Parameter CONTENTTYPE = "text/xml";
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/:soapService" Method="POST" Call="PostSOAP"/>
</Routes>
}
ClassMethod PostSOAP(
strSoapService As %String = "",
test As %Integer = 0) As %Status
{
#Dim %request As %CSP.Request
#Dim %response As %CSP.Response
#Dim httpClient As %Net.HttpRequest = ..GetLoopbackSOAPClient()
do httpClient.EntityBody.Write(..UpdateSoapBody(%request.Content.Read($$$MaxStringLength)))
do httpClient.SetHeader("SOAPAction", %request.SoapAction)
do httpClient.Post($System.CSP.GetDefaultApp($Namespace) _ "/" _ strSoapService, test)
set %response.ContentType = "text/xml"
write httpClient.HttpResponse.Data.Read($$$MaxStringLength)
return $$$OK
}
// -- Private utils --
ClassMethod GetLoopbackSOAPClient() As %Net.HttpRequest [ Private ]
{
set httpClient = ##class(%Net.HttpRequest).%New()
set httpClient.Server = ...
set httpClient.Port = ...
set httpClient.Timeout = 5
set httpClient.Https = 1
set httpClient.SSLConfiguration = ...
return httpClient
}
ClassMethod UpdateSoapBody(strInput As %String) As %String [ Private ]
{
#Dim strSoap As %String = ""
#Dim username As %String = ... (Extract from Input)
#Dim password As %String = ... (Extract from Input)
set strSoap = strSoap _ ... (Copy soap enveloppe from input)
set strSoap = strSoap _ " <soap:Header>" _ ..GenerateSecurityHeader(username, password) _ " </soap:Header>"
set strSoap = strSoap _ " <soap:Body>"
...
set strSoap = strSoap _ " </soap:Body>"
set strSoap = strSoap _ "</soap:Envelope>"
return strSoap
}
ClassMethod GenerateSecurityHeader(
strUsername As %String,
strPassword As %String) As %String [ Private ]
{
set header = ##class(%SOAP.Security.Header).%New()
set usernameToken = ##class(%SOAP.Security.UsernameToken).Create(strUsername, strPassword)
do header.AddSecurityElement(usernameToken)
return ... (Use XML Writer to Output header to a String.)
}
}