I have a client that sends a simple web request to a SOAP service. It is a simple C# program that uses the WSDL file of the service to create a client. The service is hosted on IIS 8.5 and Windows Server 2012. It works fine when using anonymous authentication but it fails with Windows authentication. Both client and service are in the same domain, user permissions are also fine.
I configured IIS so that it disables all forms of authentication except Windows authentication (Negotiate, NTLM). The client is configured so that it uses Windows as the client credential type.
When I send a request I get the following error:
"The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate, NTLM'"
I then tried out a tool I found on github called "WebServiceStudio". With that tool I set the WSDL, selected my request method and it worked, even with Windows authentication.
I looked at both attempts with Wireshark and noticed that the WebServiceStudio request immediately sends the Negotiate token with the first request while my own client sends the token in the second request, which to my understanding is how Windows authentication usually works.
I tried on IIS side but nothing worked so far:
Changed authentication order (Negotiate, NTLM and NTLM, Negotiate)
Changed authentication to only Negotiate
Changed extended protection in the advanced settings (neither option made a difference)
Verified that the WindowsAuthentication and WindowsAuthenticationModule were both installed
My goal is that my own C# client can successfully authenticate with Windows authentication.
Here's the C# client's configuration:
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<system.serviceModel>
<client>
<endpoint address="server address" binding="basicHttpBinding"
bindingConfiguration="MyContractSoap" contract="MyContract.MyContractSoap" />
</client>
<bindings>
<basicHttpBinding>
<binding name="MyContractSoap">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" proxyCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
And here is the wireshark data of my client's request:
POST /ABC/ShipmentDocuments.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "ABC/DocumentShipped"
Host: sdespte3
Content-Length: 333
Expect: 100-continue
Accept-Encoding: gzip, deflate
<!-- Server rejects request and states authentication method -->
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Tue, 18 Feb 2020 10:20:01 GMT
Content-Length: 1344
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>401 - Nicht autorisiert: Zugriff aufgrund ung.ltiger Anmeldeinformationen verweigert.</title>
</head>
<body>
<div id="header"><h1>Serverfehler</h1></div>
<div id="content">
<div class="content-container"><fieldset>
<h2>401 - Nicht autorisiert: Zugriff aufgrund ung.ltiger Anmeldeinformationen verweigert.</h2>
<h3>Die angegebenen Anmeldeinformationen berechtigen Sie nicht, dieses Verzeichnis oder diese Seite anzuzeigen.</h3>
</fieldset></div>
</div>
</body>
</html>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>Request data here</s:Body></s:Envelope>
<!-- We send the negotiate token -->
POST /ABC/ShipmentDocuments.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "ABC/DocumentShipped"
Accept-Encoding: gzip, deflate
Authorization: Negotiate YIIHog...Token here
Host: abc
Content-Length: 333
Expect: 100-continue
<!-- Rejected again, unsure why -->
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Tue, 18 Feb 2020 10:20:01 GMT
Content-Length: 1344
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>401 - Nicht autorisiert: Zugriff aufgrund ung.ltiger Anmeldeinformationen verweigert.</title>
<style type="text/css">
And finally the wireshark data of the other tool that worked:
POST /ABC/ShipmentDocuments.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "ABC/DocumentShipped"
Authorization: Negotiate YIILV...Token here
Host: sdespiis1
Content-Length: 415
Expect: 100-continue
HTTP/1.1 100 Continue
<?xml version="1.0" encoding="utf-8"?><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>Request body here</soap:Body></soap:Envelope>
<!-- Accepted -->
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
Persistent-Auth: false
X-Powered-By: ASP.NET
WWW-Authenticate: Negotiate oYG2MIGzo... Token here
Date: Tue, 18 Feb 2020 15:24:39 GMT
Content-Length: 295
<?xml version="1.0" encoding="utf-8"?><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>Body here</soap:Body></soap:Envelope>
Update: Here is the client's source code to call the service.
Program:
class Program
{
static void Main(string[] args)
{
sendWebRequest();
}
static int _orderId = 1;
static int _mandant = 1;
static string _sId = "0123456789012345678901";
static string _isShipped = "eingeliefert";
static void sendWebRequest()
{
Console.WriteLine("Start webrequest Orderid: {0}, mandant: {1}, sId: {2}, isShipped: {3}", _orderId, _mandant, _sId, _isShipped);
WebserviceManager wm = new WebserviceManager();
wm.Open();
wm.SetStateToShipped(_orderId, _mandant, _sId, _isShipped);
wm.Close();
Console.WriteLine("Webrequest erfolgreich");
}
}
WebserviceManager:
public class WebserviceManager
{
protected MyContract.MyContractSoapClient _soapClient;
public WebserviceManager()
{
}
public void Open()
{
_soapClient = createWebServiceClient();
try
{
_soapClient.Open();
}
catch (Exception ex)
{
Logging.Error("Open", ex);
throw ex;
}
Logging.Info("_soap-Client open");
}
public void Close()
{
_soapClient.Close();
}
public void SetStateToShipped(int orderNo, int mandant, string sId, string isShipped)
{
_soapClient.DocumentShipped(orderNo, mandant, sId, isShipped);
}
protected MyContract.MyContractSoapClient createWebServiceClient()
{
return new MyContract.MyContractSoapSoapClient();
}
}
So it looks like the impersonation was not properly set up. I added the following line in my client program, right after creating the client object:
protected MyContract.MyContractSoapClient createWebServiceClient()
{
var client = new MyContract.MyContractSoapSoapClient();
client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
return client;
}
And now Windows authentication works as expected!
Related
I have a Winform Application that I can use TCP client to connect to WCF service, but I don't know how to call its function.
I've been looking at sending SOAP-XML request to WCF service through TCP client, but always get "Bad Request" respond.
Here's my request:
POST /MyService.svc HTTP/1.1
Host: MyHost.com
Content-Type: text/xml; charset=utf-8
Content-Length: 700
SOAPAction: "http://MyHost.com/MyService/MyFunction"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<Action soap:must Understand="1"
xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">
http://MyHost.com/MyService/MyFunction
</Action>
</soap:Header>
<soap:Body>
<MyFunction xmlns="http://MyHost.com/">
</MyFunction>
</soap:Body>
And here's response:
HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Tue, 07 Jul 2015 01:32:13 GMT
Connection: close
Content-Length: 339
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Bad Request - Invalid Header</h2>
<hr><p>HTTP Error 400. The request has an invalid header name.</p>
</BODY></HTML>
I have a working implementation of OData WCF service which now need to be published in IIS with basic custom authentication.
Implementation is based on Microsoft OData example and works perfectly fine under the IIS Express. When I publish it to IIS 7.5 with only Basic Authentication enabled, AuthenticateRequest handler is only called on initial request, which returns status code 401 and asks to authenticate.
AuthenticateRequest is no longer called on subsequent requests. When debugging the service on IIS, BeginRequest is definitely called, it's just AuthenticateRequest not being present in the pipeline? Both are called every request in IIS Express.
IIS Authentication configuration:
IHttpModule code:
public class BasicAuthModule: IHttpModule
{
// based on http://msdn.microsoft.com/en-gb/data/gg192997.aspx
public void Init(HttpApplication app)
{
app.AuthenticateRequest += AuthenticateRequest;
app.BeginRequest += BeginRequest;
}
private void BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if(app.Context == null)
{
throw new Exception("Will not happen");
}
}
private void AuthenticateRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if(!app.Request.Headers.AllKeys.Contains("Authorization"))
{
CreateNotAuthorizedResponse(app, 401, 1, "Please provide Authorization headers with your request.");
app.CompleteRequest();
}
else if(!BasicAuthProvider.Authenticate(app.Context))
{
CreateNotAuthorizedResponse(app, 401, 1, "Logon failed.");
app.CompleteRequest();
}
}
private static void CreateNotAuthorizedResponse(HttpApplication app, int code, int subCode, string description)
{
var response = app.Context.Response;
// response.Status = "401 Unauthorized";
response.StatusCode = code;
response.SubStatusCode = subCode;
response.StatusDescription = description;
// response.AppendHeader("WWW-Authenticate", "Basic");
// response.End();
}
public void Dispose()
{
}
}
Web.config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="BasicAuthModule" type="WcfTestService.BasicAuthModule"/>
</modules>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
And the question: why authentication works in Visual Studio 2012 debug server but not in IIS 7.5?
Complete test project can be downloaded from here.
Edit:
I commented out excessive testing code in the CreateNotAuthorizedResponse function and response.End() which caused an exception (I added it last minute before posting).
When inspecting requests it looks like everything should work except that Cookie may be causing IIS to skip authentication for some reason. Below first 2 raw request - reply pairs:
Request 1:
GET http://localhost:8080/test/ HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,pl;q=0.6
Cookie: ASPSESSIONIDAQRDDBTR=BPCFKGDDCGJLPFKHEPOLPMFK; __RequestVerificationToken_L2RlbGl2ZXJ50=j0o-RDC12Z_E1o1nnXU_9iFaThUEPXRXDNKepqoX2fmgjg8gRB6Hi9fs3MSGxUvYQs6tJ0Jxsf6U20WKWpOrj4azgL_VpVzQHcNyJghUrKg1; __RequestVerificationToken=uOeCVgZDguOs3mRA7O4nhj88wJ_mFR6t1QN7vl7mOPGaNBoEnVFmIQVoUwxim8NbODJKMz5fBuAoPKo7Ek-4JeujsOIyIxjRB1xS_JaFF381; .ASPXAUTH=C2965A60E4BB162123A2CDDA8FD825C9DF3625116E5722C9B873BA64F041CCDCAB098EA3A208C2061D8D5746BC0832413105BA274C1B37DB8276471D49DE12562E4E93933289828427F559057519E75421493909E215EAA0DFB4C8DBE213EAC19AB6025EA715658A8D57CAFA308F7AC4A9051687777D2E82B7A2552917466E7C0BFA0C23EEE272F7E83C3718371375358B1199F155FB882EF8F5082CB28F6E030146DE365B5E4D8FE25E55EDD3F03788
Reply 1: (created by CreateNotAuthorizedResponse method)
HTTP/1.1 401 Please provide Authorization headers with your request.
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm="localhost"
X-Powered-By: ASP.NET
Date: Mon, 20 Oct 2014 13:03:14 GMT
Content-Length: 6607
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>IIS 7.5 Detailed Error - 401.1 - Please provide Authorization headers with your request.</title>
Request 2 (when entered test:test - dGVzdDp0ZXN0):
GET http://localhost:8080/test/ HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Authorization: Basic dGVzdDp0ZXN0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,pl;q=0.6
Cookie: ASPSESSIONIDAQRDDBTR=BPCFKGDDCGJLPFKHEPOLPMFK; __RequestVerificationToken_L2RlbGl2ZXJ50=j0o-RDC12Z_E1o1nnXU_9iFaThUEPXRXDNKepqoX2fmgjg8gRB6Hi9fs3MSGxUvYQs6tJ0Jxsf6U20WKWpOrj4azgL_VpVzQHcNyJghUrKg1; __RequestVerificationToken=uOeCVgZDguOs3mRA7O4nhj88wJ_mFR6t1QN7vl7mOPGaNBoEnVFmIQVoUwxim8NbODJKMz5fBuAoPKo7Ek-4JeujsOIyIxjRB1xS_JaFF381; .ASPXAUTH=C2965A60E4BB162123A2CDDA8FD825C9DF3625116E5722C9B873BA64F041CCDCAB098EA3A208C2061D8D5746BC0832413105BA274C1B37DB8276471D49DE12562E4E93933289828427F559057519E75421493909E215EAA0DFB4C8DBE213EAC19AB6025EA715658A8D57CAFA308F7AC4A9051687777D2E82B7A2552917466E7C0BFA0C23EEE272F7E83C3718371375358B1199F155FB882EF8F5082CB28F6E030146DE365B5E4D8FE25E55EDD3F03788
Response 2 (BeginRequest called but not AuthenticateRequest):
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm="localhost"
X-Powered-By: ASP.NET
Date: Mon, 20 Oct 2014 13:03:17 GMT
Content-Length: 6531
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>IIS 7.5 Detailed Error - 401.1 - Unauthorized</title>
I think you are mixing up IIS built-in basic authentication with your own custom authentication module. The short answer is to disable Basic Authentication in IIS and enable Anonymous. This will pass all the auth work onto asp.net.
If your are testing in VS I'm assuming you are doing so via a browser that is auto launched when you hit F5.
With basic auth turned on IIS initially responds with a 401 which causes the browser to display the login form.
The credentials you enter there have to be valid windows credentials, which IIS validates against your windows accounts. Once IIS has validated these credentials it will pass the request along to your code.
If you enter valid windows credentials the event is raised but your code will reject it because the credentials are not test/test and return a 401.1
If you enter test/test then IIS is rejecting the credentials and sends back a 401 so your event is never called.
Final word: You should be testing your web service using an http client (e.g. unit test with System.Net.WebClient), or use a chrome plugin (postman/devhttp) to test at the http level. If you are already doing this then forgive my assumption.
I'm programmatically trusting my yammer app through .NET. While debugging the POST request to trust an app, the responses can seemingly arbitrarily render either a 302 or a 404 as the response tries to redirect to a SharePoint MySite host.
If I during the same debug session loop my requests, I get the same type of response. I have to restart debugging to have a chance at a different response. I have tried setting minute long sleeps to ensure that time has nothing to do with which type of response I get. Same rule seem to apply: One debug session, one response type.
My question is now: What do I need to do to avoid these 404's?
Here's the fiddler responses:
THE 302 RESPONSE:
POST https://www.yammer.com/MYNETWORK/oauth2/decision?client_id=MYAPPCODE&redirect_uri=http%3a%2f%2fmy.devmachine.contoso.com&response_type=code HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: www.yammer.com
Cookie: yamtrak_id=[GUID]; _workfeed_session_id=[ID] Content-Length: 90
Expect: 100-continue
utf8=%E2%9C%93&authenticity_token=[TOKEN]=&allow=Allow
HTTP/1.1 302 Found
Server: nginx
Date: Mon, 29 Sep 2014 13:21:51 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Status: 302 Found
Location: http://my.devmachine.contoso.com?code=[CODE] X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: SAMEORIGIN
Cache-Control: no-cache
P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"
X-UA-Compatible: IE=Edge,chrome=1
Set-Cookie: yamtrak_id=[ID]; path=/; expires=Tue, 29-Sep-2015 13:21:51 GMT; secure; HttpOnly
Set-Cookie: auth_token=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT; secure
Set-Cookie: auth_token_sso=; domain=yammer.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT; secure
X-Date: 1411996911966
X-Runtime: 0.073263
7e
<html><body>You are being redirected.</body></html>
0
THE 404 RESPONSE:
POST https://www.yammer.com/MYNETWORK/oauth2/decision?client_id=MYAPPCODE&redirect_uri=http%3a%2f%2fmy.devmachine.contoso.com&response_type=code HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: www.yammer.com
Cookie: yamtrak_id=[GUID]; _workfeed_session_id=[ID]
Content-Length: 90
Expect: 100-continue
utf8=%E2%9C%93&authenticity_token=[TOKEN]=&allow=Allow
HTTP/1.1 404 Not Found
Server: nginx
Date: Mon, 29 Sep 2014 13:26:03 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Status: 404 Not Found
Cache-Control: no-cache
P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"
X-UA-Compatible: IE=Edge,chrome=1
X-Date: 1411997163223
X-Runtime: 0.068703
a45
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=8,chrome=1" />
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>The page you were looking for doesn't exist (404)</title>
<link href="/stylesheets/yamkit/yam.css" media="screen, projection" rel="stylesheet" type="text/css" />
<style type="text/css">
body {
...
<div id="parallax-static">
<div id="parallax-static-text">
<h1>Oops!</h2>
<h2>The page you were looking for could not be found.</h2>
Let's go back to your happy place.
</div>
</div>
...
</body>
</html>
0
I'm aware of the other yammer threads in this forum, and I'm using the new login_csrf_token cookie to authenticate, so that shouldn't be the issue.
Thanks for Reading! I'm grateful for any suggestion on how to solve this.
EDIT: I've tried setting another site (google) as my redirect url, but the alternating behaviour persists.
Found it: Turns out that sometimes the authenticity token contains plus characters (+) which needs to be URL encoded. The tokens can also contain front slashes (/) but they don't trip up the succeeding call to /session or /oauth2/decision, only plus does.
I wrote a WCF C# client that consumes a Java webservice:
var client = new abcClient("abc");
var response = client.AbcTransaction(msg);
The WCF binding info from web.config is:
<customBinding>
<binding name="abcSOAP">
<textMessageEncoding messageVersion="Soap12" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
It looks pretty straight-forward, right? ...And indeed, SoapFaults are easy to consume:
HTTP/1.1 500 Internal Server Error
Content-Length: 783
Content-Type: application/soap+xml;charset=UTF-8
Server: Microsoft-IIS/8.0
Date: Mon, 18 Nov 2013 14:06:18 GMT
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body><soap:Fault>...
However, the webservice sends "regular" responses in the multipart/related content-type:
HTTP/1.1 200 OK
Content-Type: multipart/related; type="application/xop+xml"; boundary="uuid:c79210c3-bbef-4aa3-82ae-6a20c7a96564"; start="<root.message#cxf.apache.org>"; start-info="application/soap+xml"
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
Date: Mon, 18 Nov 2013 14:11:25 GMT
Content-Length: 658
--uuid:c79210c3-bbef-4aa3-82ae-6a20c7a96564
Content-Type: application/xop+xml; charset=UTF-8; type="application/soap+xml";
Content-Transfer-Encoding: binary
Content-ID: <root.message#cxf.apache.org>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">...
This leads to a ProtocolException in the WCF client because the WCF client does not expect a multipart/related answer. The ProtocolException message is (in German):
Der Inhaltstyp "multipart/related; type="application/xop+xml";
boundary="uuid:ead716a3-4b8b-4207-ad66-b9f18ae368b2";
start="";
start-info="application/soap+xml"" der Antwortnachricht stimmt nicht
mit dem Inhaltstyp der Bindung (application/soap+xml; charset=utf-8)
überein. Wenn Sie einen benutzerdefinierten Encoder verwenden, sollten
Sie sicherstellen, dass die IsContentTypeSupported-Methode korrekt
implementiert ist. Die ersten 1024 Bytes der Antwort waren: ...
In English:
The content type "multipart/related; type="application/xop+xml";
boundary="uuid:ead716a3-4b8b-4207-ad66-b9f18ae368b2";
start="";
start-info="application/soap+xml"" of the response message
does not match the content type of the binding (application/soap+xml; charset=utf-8). If using a custom encoder, be sure that the
IsContentTypeSupported method is implemented properly. The first 1024
bytes of the response were: ...
Does anyone have an idea how I can consume this multipart/related message with a WCF client (without using the HttpWebRequest class)? Is there any configuration available for this szenario?
Max' and Mehmet's hints showed the right direction but I had to change a bit more.
Since I used the element in , the wcf configuration ignored the messageEncoding="Mtom" attribute.
Instead of using attribute, it seems better to use the element directly:
<binding name="energylinkSOAP">
<mtomMessageEncoding messageVersion="Soap12" />
<httpsTransport requireClientCertificate="true" />
</binding>
By that you also can define more configurations, such as messageVersion.
I think that converting system into to MTOM will work for that.
This is a strange one. I'm running MVC 3 and have a custom action result that wraps exceptions and returns a message along with the standard HTTP error.
public class ExceptionResult : ActionResult
{
private readonly Exception _exception;
public ExceptionResult(Exception exception)
{
_exception = exception;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ClearHeaders();
response.Cache.SetNoStore();
response.ContentType = ContentType.Json;
var baseEx = _exception as BaseException ?? new ServerException(_exception);
var result = baseEx.GetResult();
var json = result.ToJSON();
response.Write(json);
response.StatusCode = (int)result.Status.Code;
}
}
When I run this locally I get exactly what I expect:
HTTP/1.1 400 Bad Request
Cache-Control: no-store
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Thu, 01 Dec 2011 19:00:03 GMT
Content-Length: 81
{"error":"invalid_request","error_description":"Parameter grant_type is missing"}
But when I try to connect from a different machine I get the standard IIS error message instead:
HTTP/1.1 400 Bad Request
Cache-Control: no-store
Content-Type: text/html
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Thu, 01 Dec 2011 19:02:33 GMT
Content-Length: 11
Bad Request
UPDATE
There must be some http module somewhere in the IIS pipeline that is swallowing the response and rewriting the content. I wrote a module to log the request and response and it's returning exactly what I expect however what actually makes it to the browser is wrong.
2011-12-02 15:39:00,518 - ======== Request ========
2011-12-02 15:39:00,518 - GET /oauth/2/token HTTP/1.1
2011-12-02 15:39:00,519 - Cache-Control: max-age=0
2011-12-02 15:39:00,519 - Connection: keep-alive
2011-12-02 15:39:00,519 - Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
2011-12-02 15:39:00,519 - Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
2011-12-02 15:39:00,519 - Accept-Encoding: gzip,deflate,sdch
2011-12-02 15:39:00,519 - Accept-Language: en-US,en;q=0.8
2011-12-02 15:39:00,519 - Host: micah-pc:8095
2011-12-02 15:39:00,519 - User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2
2011-12-02 15:39:00,519 - =========================
2011-12-02 15:39:00,519 - OAuth exception occurred.
BoomTown.OAuth.OAuthException: Parameter grant_type is missing
at BoomTown.OAuth.Request.TokenRequest.GetRequestValidator() in C:\code\BoomTown\Api\BoomTown.OAuth\Request\TokenRequest.cs:line 19
at BoomTown.OAuth.Request.OAuthRequestBase.Validate() in C:\code\BoomTown\Api\BoomTown.OAuth\Request\OAuthRequestBase.cs:line 33
at BoomTown.OAuth.Request.OAuthRequestBase..ctor(HttpRequestBase request, IOAuthServiceLocator serviceLocator) in C:\code\BoomTown\Api\BoomTown.OAuth\Request\OAuthRequestBase.cs:line 28
at BoomTown.OAuth.Request.TokenRequest..ctor(HttpRequestBase request, IOAuthServiceLocator serviceLocator) in C:\code\BoomTown\Api\BoomTown.OAuth\Request\TokenRequest.cs:line 13
at BoomTown.Api.Web.Controllers.OAuth.V2.OAuthController.Token() in C:\code\BoomTown\Api\BoomTown.Api.Web\Controllers\OAuth\V2\OAuthController.cs:line 26
2011-12-02 15:39:00,520 - ======= Response =======
2011-12-02 15:39:00,520 - HTTP/1.1 400 Bad Request
2011-12-02 15:39:00,520 - Cache-Control: no-store
2011-12-02 15:39:00,520 - X-AspNet-Version: 4.0.30319
2011-12-02 15:39:00,520 - Content-Type: application/json; charset=utf-8
2011-12-02 15:39:00,520 - {"error":"invalid_request","error_description":"Parameter grant_type is missing"}
SOLUTION
Thanks to a little sleuthing I was able to figure it out. I setup IIS tracing which confirmed my suspicions that it was related to the customerrormodule which was intercepting my requests and overwriting my error messages. I kept monkeying with the
<system.web>
<customErrors />
<system.web>
settings but to no avail. I was on the right track, but since it's IIS 7 that I'm running I needed to change the correct web.config section like this:
<system.webServer>
<httpErrors errorMode="Detailed" />
</system.webServer>
Now all my custom JSON messages come through perfectly. Big thanks to Jason Finneyfrock for the tag team on this one.
In your web.config, do you have httpErrors defined to only be DetailedLocalOnly? I'm not sure whether or not the content would be removed in this situation.
http://www.iis.net/ConfigReference/system.webServer/httpErrors
I came across this, not sure if it will help:
context.HttpContext.Response.TrySkipIisCustomErrors = true;