Missing security headers in FaultException - wcf

I'm using a custom binding in my service and client which basically looks like this:
public class UserNameWsTrustBinding : Binding
{
public override BindingElementCollection CreateBindingElements()
{
var coll = new BindingElementCollection();
coll.Add(CreateSecurityBindingElement());
coll.Add(new TextMessageEncodingBindingElement());
coll.Add(new HttpsTransportBindingElement());
return coll;
}
private SecurityBindingElement CreateSecurityBindingElement()
{
var elem = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
elem.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
return elem;
}
}
This binding works prety well. Except for FaultExceptions which I throw e.g. when validating the user's credentials.
I throw non-generic FaultExceptions like this; nothing special I think:
throw new FaultException(new FaultReason("Blah blah"), new FaultCode("Code42"));
WCF then takes care of putting these faults into a SOAP envelope. The problem is, that there don't get security headers (namely a WS-Security timestamp) baked in:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/soap/fault</a:Action>
<a:RelatesTo>urn:uuid:ff1f54d7-53a0-4650-b967-03a75def5fa4</a:RelatesTo>
</s:Header>
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Sender</s:Value>
<s:Subcode><s:Value>Code42</s:Value></s:Subcode>
</s:Code>
<s:Reason><s:Text>Blah blah</s:Text></s:Reason>
</s:Fault>
</s:Body>
</s:Envelope>
When the client application receives this response it complains about it with the following error message:
System.ServiceModel.Security.MessageSecurityException: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.
This is not surprising, since there is no security header included. I can then get the FaultException from the MessageSecurity's InnerException. It works, but it doesn't feel good.
What is really surprising, however, is that unhandled exceptions that are not caught in my server code get wrapped in a FaultException automatically. And these FaultExceptions have a security header!
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault</a:Action>
<a:RelatesTo>urn:uuid:0baffa8b-07ee-4feb-bc44-7e2c7ae85c22</a:RelatesTo>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2022-07-11T13:41:22.007Z</u:Created>
<u:Expires>2022-07-11T13:46:22.007Z</u:Expires>
</u:Timestamp>
</o:Security>
</s:Header>
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Receiver</s:Value>
<s:Subcode><s:Value xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</s:Value></s:Subcode>
</s:Code>
<s:Reason><s:Text>The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the %lt;serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.</s:Text></s:Reason>
</s:Fault>
</s:Body>
</s:Envelope>
So, long story short: What am I doing wrong? Or in other words: What do I have to do to make my self-thrown FaultExceptions include a timestamp as well?

Related

Dynamics CRM SOAP Request : "Request is unsupported."

The Windows Live ID authentication we used to connect via SOAP to our Dynamics stopped working, after years without problem.
Here is the SOAP Request:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">
http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:MessageID>urn:uuid:56476fb1-26d4-4525-a62a-4a1c65e71e85</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">
https://login.microsoftonline.com/extSTS.srf</a:To>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2022-05-05T15:13:25.00Z</u:Created>
<u:Expires>2022-05-06T15:13:25.00Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="devicesoftware">
<o:Username>user here</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password here</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
<t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<a:EndpointReference>
<a:Address>http://passport.net/tb</a:Address>
</a:EndpointReference>
</wsp:AppliesTo>
<t:RequestType>
http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>`
And here is the answer :
<?xml version="1.0" encoding="utf-8"?><S:Envelope xmlns:wsa="http://www.w3.org/2005/08/addressing" 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" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:S="http://www.w3.org/2003/05/soap-envelope"><S:Header><psf:pp xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault"><psf:serverVersion>1</psf:serverVersion><psf:authstate>0x80048800</psf:authstate><psf:reqstatus>0x80048800</psf:reqstatus><psf:serverInfo ServerTime="2022-05-05T14:54:49.1744415Z">ESTS-PUB-NEULR2-AZ1-FD071-001.ProdSlices rid:f2378b15-e610-4168-a77e-8572e61ba900</psf:serverInfo></psf:pp></S:Header><S:Body xmlns:S="http://www.w3.org/2003/05/soap-envelope"><S:Fault><S:Code><S:Value>S:Sender</S:Value><S:Subcode><S:Value>wst:FailedAuthentication</S:Value></S:Subcode></S:Code><S:Reason><S:Text xml:lang="en-US">Authentication Failure</S:Text></S:Reason><S:Detail><psf:error xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault"><psf:value>0x80048800</psf:value><psf:internalerror><psf:code>0x80048800</psf:code><psf:text>AADSTS90083: Request is unsupported.</psf:text></psf:internalerror></psf:error></S:Detail></S:Fault></S:Body></S:Envelope>
What could have gone wrong please ?
As #Guido Mentions in the comments.
You need to shift over to passing a JWT token now.. Dataverse has not supported LiveID since 2017, so I assume your using WS-TRUST there :)
That said, if your handcrafting calls to Dataverse, you should be using the WebAPI.
if you're using .net. You can use the CrmServiceClient or DataverseServiceClient(cross platform) to connect to and interact with Dataverse.
You can find more information here: https://learn.microsoft.com/en-us/power-apps/developer/data-platform/authenticate-office365-deprecation

How to customize operation params appearance in SOAP message?

I have WCF service with operation string GetData(DataRequest request).
When I'm calling it, request SOAP message is something like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
...
<s:Body ...>
<GetData xmlns="http://...">
<request>
...
</request>
</GetData>
</s:Body>
</s:Envelope>
And I want to customize request XML element without renaming my operation's parameter (using some attribute or so on). Is there a way to do this? And get something like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
...
<s:Body ...>
<GetData xmlns="http://...">
<myRequest>
...
</myRequest>
</GetData>
</s:Body>
</s:Envelope>
I've found the solution.
Attribute MessageParameter can be used to achieve desired look:
string GetData([MessageParameter(Name = "myRequest")] DataRequest request)

Security processor was unable to find a security header in the message.

In my many trials to debug this
Exception: `System.ServiceModel.Security.MessageSecurityException: Security processor was unable to find a security header in the message.
This might be because the message is an unsecured fault or because there is a
binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security`
How Do I debug a fault exception
I am not sure if this is why my response says 'Rejected by policy from client'
This is just a guess if it could be because of the additional junk which in soap header .
My code generated a soap header which looks like this
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1" u:Id="_3"/>
<a:MessageID u:Id="_4">urn:uuid:9659b138-7fc0-4bb6-8c0a-bae00336ba78</a:MessageID>
<a:ReplyTo u:Id="_5">
<a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
</a:ReplyTo>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">
uIDPo/RnkzjA3fBPjgXUnYt8J3IAAAAAoMwUVXqfw0yigCfFtptf4RNq4s3l6eJLuuLNNdxRoH4ACQAA
</VsDebuggerCausalityData>
<a:To s:mustUnderstand="1" u:Id="_6">https://service100.emedny.org:9047/MHService</a:To>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:BinarySecurityToken u:Id="uuid-8d1465b7-c0fd-4137-9361-d0a818286435-53" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
<!--Removed-->
</o:BinarySecurityToken>
<o:BinarySecurityToken u:Id="uuid-8d1465b7-c0fd-4137-9361-d0a818286435-52" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
<!--Removed-->
</o:BinarySecurityToken>
</o:Security>
</s:Header>
...
</s:Envelope>
This is the sample soap request for vendor
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:mhs="http://org/emedny/mhs/" xmlns:urn="urn:hl7-org:v3">
<soapenv:Header>
<wsse:Security soap:mustUnderstand="1" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:BinarySecurityToken ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-e00c8062-83d2-4f04-88fc-996218e7bb3d">MIICeDCC....(eMedNY signed user MLS cert).......</wsse:BinarySecurityToken>
<wsse:BinarySecurityToken ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-c0cc2cd4-cb77-4fa5-abfa-bd485afd1685">MIIDFj.....( eMedNY MLS web-service end-point public cert)........</wsse:BinarySecurityToken>
This is the additional stuff my client code generates
<a:Action s:mustUnderstand="1" u:Id="_3"/>
<a:MessageID u:Id="_4">urn:uuid:9659b138-7fc0-4bb6-8c0a-bae00336ba78</a:MessageID>
<a:ReplyTo u:Id="_5">
<a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
</a:ReplyTo>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">
uIDPo/RnkzjA3fBPjgXUnYt8J3IAAAAAoMwUVXqfw0yigCfFtptf4RNq4s3l6eJLuuLNNdxRoH4ACQAA
</VsDebuggerCausalityData>
<a:To s:mustUnderstand="1" u:Id="_6">https://service100.emedny.org:9047/MHService</a:To>
How would I remove this?
Internet recommends using Imessageinspector and custombehviour
public class CustomMessageInspector : IClientMessageInspector
{
#region IClientMessageInspector Members
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
request.Headers.RemoveAll("Action", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
request.Headers.RemoveAll("MessageID", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
request.Headers.RemoveAll("ReplyTo", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
request.Headers.RemoveAll("To", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
return null;
}
#endregion
}
public class CustomBehavior : IEndpointBehavior
{
<--removed some more classes-->
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior)
{
//Add the inspector
behavior.MessageInspectors.Add(new CustomMessageInspector());
}
}
then finally in the call to the proxyclient
MHSClient proxy = new MHSClient(GetCustomBinding(),
new EndpointAddress(new Uri("https://service100.emedny.org:9047/MHService"),
EndpointIdentity.CreateDnsIdentity("DPMedsHistory"));
proxy.Endpoint.EndpointBehaviors.Add(new CustomBehavior());
private static Custombinding GetCustomBinding()
{
var b = new CustomBinding();
var sec = (AsymmetricSecurityBindingElement)SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10);
sec.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
sec.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
sec.MessageSecurityVersion =
MessageSecurityVersion.
WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
sec.IncludeTimestamp = false;
sec.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.EncryptBeforeSign;
TextMessageEncodingBindingElement textEncBE = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
HttpsTransportBindingElement httpsBE = new HttpsTransportBindingElement();
CustomBinding myBinding = new CustomBinding();
myBinding.Elements.Add(sec);
myBinding.Elements.Add(textEncBE);
myBinding.Elements.Add(httpsBE);
return myBinding;
}
THis doesn't work or rather gives me an error:
No signature message parts were specified for messages with the '' action.

Invoke WCF service dynamically during runtime

I am trying to invoke WCF service dynamically. I am able connect to the service and invoke methods that do not require any parameters.
ChannelFactory<IRequestChannel> factory = new ChannelFactory<IRequestChannel>(this.ServiceBinding, this.EndPoint.Address);
IRequestChannel channel = factory.CreateChannel();
However, I am unable to invoke Operation Contracts that require composite Entity as a parameter.
The following code is used to instantiate the request Message:
Message requestMessage = Message.CreateMessage(this.ServiceBinding.MessageVersion, contractNameSpace, new SimpleMessageBody(value));
The value used in SimpleMessageBody class is serialized value of the entity using DataContractSerializer.
<Person xmlns="http://schemas.datacontract.org/2004/07/WcfService.Test.Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Name>John Smith</Name></Person>
Operation Contract
public string GetData(Person value)
{
using (MemoryStream ms = new MemoryStream())
{
value = new Person { Name = "John Smith" };
DataContractSerializer ser = new DataContractSerializer(typeof(Person));
ser.WriteObject(ms, value);
var result = UnicodeEncoding.UTF8.GetString(ms.ToArray());
}
return string.Format("You entered: {0}", value.Name);
}
Entity
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
}
The following SOAP message is generated from the above createmessage code:
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IService1/GetData</a:Action>
<a:MessageID>urn:uuid:cf78d5b7-333b-40eb-a71c-d81cb9c37b5d</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://localhost:52724/Service1.svc</a:To>
</s:Header>
<s:Body><Person xmlns="http://schemas.datacontract.org/2004/07/WcfService.Test.Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Name>John Smith</Name></Person></s:Body>
</s:Envelope>
However, in order for the Person entity to be populated and the correct operation contract to be executed the SOAP has to be as follows:
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IService1/GetData</a:Action>
<a:MessageID>urn:uuid:d49bd525-0f30-46fe-94fb-0248c2cb1ea2</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
</s:Header>
<s:Body>
<GetData xmlns="http://tempuri.org/">
<value xmlns:d4p1="http://schemas.datacontract.org/2004/07/WcfService.Test.Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:Name>John Smith</d4p1:Name>
</value>
</GetData>
</s:Body>
</s:Envelope>
Please NOTE the message body.
Thanks
I don't know why are you doing it this hard way but if you want to call the method expecting SOAP request you shown you must first provide message contract to your client:
[MessageContract(WrapperName="GetName")]
public class MessageContract
{
[MessageBodyMember(Name="value")]
public Person Person { get; set; }
}
And you will also need similar contract for response.
Default serialization uses wrappers inferred from operation contract names but because you are not providing service contract your serializer doesn't know about existing wrappers because of that you have to provide this additional knowledge manually or redefine your service so that it doesn't expect wrappers elements (it is also done with message contracts and setting their IsWrapped properties to false).

BizTalk Orchestration: Respond with Untyped SOAP Fault

I have a BizTalk 2009 orchestration with a request-response port type that is published as a WCF Basic-HTTP web service. The port has one operation, and that operation has request and response messages with appropriate schemas. After receiving a request on this port, there are a few cases where a fault message should be returned to the client instead of the standard response message. I'm having difficulty getting the correct fault message back to the client. I'd like to be able to set both the faultcode and faultstring elements of the SOAP fault message. Here's what I've tried:
Adding a Fault Message of Type String:
I tried adding a fault message with a message type of string to the operation. Within the orchestration, I constructed a string message and sent it as the response. The fault that was delivered back to the client looked like:
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode>
<faultstring xml:lang="en-US"><?xml version="1.0" encoding="utf-8"?>
<string>This is the error message.</string></faultstring>
<detail>
<ExceptionDetail xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<HelpLink i:nil="true"/>
<InnerException i:nil="true"/>
<Message><?xml version="1.0" encoding="utf-8"?>
<string>This is the error message.</string></Message>
<StackTrace>at Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkAsyncResult.End() ...</StackTrace>
<Type>Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkNackException</Type>
</ExceptionDetail>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
This almost works, except the faultstring element contains the xml serialized version of my string instead of the string itself. I also cannot set the faultcode element.
Adding a Fault Message of Type http://schemas.xmlsoap.org/soap/envelope/#Fault
I thought I might be able to convince BizTalk to return a fault message along the lines of what I'd expect if I constructed the Fault element and sent that. So I added a fault message with a type of http://schemas.xmlsoap.org/soap/envelope/#Fault, constructed the appropriate message and sent that as the response. The result was the same as above, except instead of a string, the faultstring element contained a CDATA section with the entire xml message I had constructed inside.
So I'm stuck now; I feel like this should be a simple task in BizTalk. The documentation on MSDN, How to Throw Fault Exceptions from Orchestrations Published as WCF Services, tells you nothing about "how" to throw fault exceptions, except that they can be thrown and that you need to set includeExceptionDetailInFaults in the configuration (which I've already done).
Does anyone have any suggestions on how this could be accomplished in BizTalk 2009?
I solved this exact problem by adding a custom WCF IDispatchMessageInspector where i read the reason text from the serialized message and then return a new System.ServiceModel.FaultException message using the deserialized reason text.
In the BizTalk orchestration i use System.String as the PortType fault message-type.
public class HandleUntypedSoapFault : IDispatchMessageInspector
{
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (reply.IsFault)
{
MessageBuffer buffer = reply.CreateBufferedCopy(int.MaxValue);
MessageFault messageFault = MessageFault.CreateFault(buffer.CreateMessage(), int.MaxValue);
if (!messageFault.HasDetail)
{
reply = buffer.CreateMessage();
return;
}
using (XmlReader reader = XmlReader.Create(new StringReader(messageFault.Reason.ToString())))
{
reader.MoveToContent();
string _faultText = reader.ReadElementContentAsString();
}
reply = Message.CreateMessage(
reply.Version,
new FaultException(
_faultText,
new FaultCode("client")).CreateMessageFault(),
null);
}
}
}
Now the SoapFault will look more like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="en-US">An untyped SoapFault from my BizTalk orchestration. This text was set in the orchestration.</faultstring>
</s:Fault>
</s:Body>
</s:Envelope>
Reminded of this long thread I participated in a while back:
http://social.msdn.microsoft.com/forums/en-US/biztalkr2adapters/thread/f69ec7af-a490-4229-81d4-3d1b41bf9c48/
They refer to an SDK sample that might help you, but it's a typed (not un-typed as you requested) fault exception.