BizTalk – Basic Authentication with SOAP 1.1 Web Service

This blog explains how to pass userid/password using Basic Authentication to a SOAP 1.1 web service. It took me about 2 1/2 days of trying different things to get it working. SOAP-UI was working, and I couldn’t get any of the Basic-HTTP, WS-HTTP, or WCF-Custom SendPorts to work, even after creating a “behaviorEnhancement” plug-in.

Since SOAP-UI was working, I finally followed advice of blogs and coworkers to use Fiddler to capture the request with SOAP Headers. Then it only took an hour or two to properly format the request for a message assignment shape in an orchestration.

This was the error I was getting when it wasn’t working, i.e. trying to configure the user/pass in the ports. I knew from SOAP-UI that if I passed a bad password, I would get “Not Authenticated” instead of “Unauthorized”.

<pre>
<soap:Fault xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <faultcode>soap:Server</faultcode>
  <faultstring>Unauthorized</faultstring>
</soap:Fault>
</pre>

Fiddler provided the following capture:

<pre>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://somesite.com/serviceOrder/service/types">
   <soapenv:Header>
   <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
   <wsse:UsernameToken wsu:Id="UsernameToken-E4E3CB1F68472DCF2914369675811859">
     <wsse:Username>myuser</wsse:Username>
     <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">mypass</wsse:Password>
     <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">NgLGMIejoGGLznG/+Qma5g==</wsse:Nonce>
     <wsu:Created>2015-07-15T13:39:41.185Z</wsu:Created>
   </wsse:UsernameToken>
   </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <typ:serviceJobListRequest>
         <!--Various Parms Here:-->
      </typ:serviceJobListRequest>
   </soapenv:Body>
</soapenv:Envelope>
</pre>

The code below worked for me. It goes in a Message Assignment shape (usually after your map) before the Send shape to the web service (as shown in the red box below):

MessageAssignment_Set_Custom_SoapHeaders_Example

<pre>
xmlDocSoapHeader.LoadXml(
   "<headers>" + 
   "<wsse:Security mustUnderstand=\"1\" xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" + 
   "<wsse:UsernameToken wsu:Id=\"UsernameToken-E4E3CB1F68472DCF2914369675811859\">" +   
   "  <wsse:Username>" + strUserFromSSO + "</wsse:Username>" + 
   "  <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" + strPasswordFromSSO + "</wsse:Password>" + 
   "  <wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">NgLGMIejoGGLznG/+Qma5g==</wsse:Nonce>" +
   "  <wsu:Created>" + System.DateTime.UtcNow.ToString("s") + "Z" + "</wsu:Created>" + 
   "</wsse:UsernameToken>" +
   "</wsse:Security>" + 
   "</headers>"  
);

msgListNewCallRequest(WCF.OutboundCustomHeaders) = xmlDocSoapHeader.OuterXml;
</pre>

This also has a big advantage for our specific application. We might need to pass a different user/password for different regions. For example, the same app might run for USA and for Europe; so we can dynamically switch out the user based on some region or country code.

Here’s another trick. To test the XML Load in C#, rather than having to run the orchestration, I created a small console program and tested like this:

<pre>

        static void Main(string[] args)
        {
            testSyntax(); 
            Console.WriteLine("Press enter to end:");
            Console.ReadLine();
        }

        public static void testSyntax()
        {
            System.Xml.XmlDocument xmlDocSoapHeader = new System.Xml.XmlDocument(); 
            string strUserFromSSO = "test"; 
            string strPasswordFromSSO = "test"; 
            Console.WriteLine("TestDate=" + System.DateTime.UtcNow.ToString("s") + "Z"); 
            // 02/10/2015 12:21 with Richard - remove soapenv:Header   THIS ONE WORKS!  
xmlDocSoapHeader.LoadXml(
   "<Header xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">" + 
   "<wsse:Security soapenv:mustUnderstand=\"1\" xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" + 
   "<wsse:UsernameToken wsu:Id=\"UsernameToken-E4E3CB1F68472DCF2914369675811859\">" +   
   "  <wsse:Username>" + strUserFromSSO + "</wsse:Username>" + 
   "  <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" + strPasswordFromSSO + "</wsse:Password>" + 
   "  <wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">NgLGMIejoGGLznG/+Qma5g==</wsse:Nonce>" +
   "  <wsu:Created>" + System.DateTime.UtcNow.ToString("s") + "Z" + "</wsu:Created>" + 
   "</wsse:UsernameToken>" +
   "</wsse:Security>" + 
   "</Header>" 
    );

//msgListNewCallRequest(WCF.OutboundCustomHeaders) = xmlDocSoapHeader.OuterXml;
        }
</pre>

This was my original MSDN social forum post on this issue. Most of the answers were of little value, which why I posted a full blog here.

I found the following links useful in my research of this issue:
1) Showed how to wrap or not wrap the soap headers: Forum Post
2) Similar: Forum Post
3) Similar problem, but from C# not BizTalk: Blog Post
4) Good overview and discussion (but from C# not BizTalk): StackOverflow
5) MSDN – over simplified way to add SoapHeaders: MSDN SoapHeaders
6) Related: Blog Post

Now, below are the blogs that lead me down the path of a custom WCF behavior extension, which still get me the “Unauthorized” result – so that was a loss of almost one day. I’m not saying it would never work, but I didn’t get it working. It required writing a C# program, putting it in the GAC, registering it to BizTalk, then magically it appears in the Behavior tab of the WCF-Custom SendPort. However, it was an interesting learning experience, but rather tedious.

1) Forum Post
2) Blog Post
3) Blog Post
4) How to Register Extensions: Blog Post

Uncategorized  

Leave a Reply