Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross domain javascript callback is not supported in authenticated services. AJAX query to WCF service via SSL proxy

I have a WCF / SVC web service which is consumed by a JavaScript call via AJAX. The page is accessed via a SSL proxy (https://gate.company.com/MyPage) located in the DMZ which then forwards the request to the internal webserver (http://myLocalWebServer/MyPage).

After a lot of googeling and trying around, I was able to make it work, playing around with the parameters crossDomainScriptAccessEnabled and Access-Control-Allow-Origin. Although, it does only work if the authentication mode is set to false or if the user is not logged in yet. As soon as the call is made from within a page on which you need to be logged in (forms authentication), it does not work any more. The error message I get then is:

cross domain javascript callback is not supported in authenticated services

It does work again, however, once I logout and do the call from a non-protected page.

My service looks like this

namespace MyNameSpace
{
   [ServiceContract(Namespace = "MyNameSpace")]
   [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   public class Service
   {
      [OperationContract]
       public string[] GetDropDownData(string id)
      {
         List<string> resultList = new List<string>();
         ...
         return resultList.ToArray();
      }
   }
}

The service call and the callback method in JavaScript:

function fillDropdwon(dropId){
    jQuery.ajax({
        type: "POST",
        dataType: "jsonp",
        contentType: "application/json; charset=utf-8",
        cache: true,
        url: "Service.svc/GetDropDownData",
        data: '{"dropId":"' + dropId + '"}',
        jsonpCallback: "onDone",
        error: function (a,b,c) {
            alert("error");
        }
    });
}

// Callback-Methode after ServiceCall
function onDone(result) {
   var theDropDown = jQuery("#<%= cboSelection.ClientID %>");
    if (theDropDown.length > 0) {
        //Clear the old entries
        theDropDown.empty();

        //Add an empty entry
        if ("<%= cboSelection.ShowEmptyRow %>".toLowerCase() == "true") {
            theDropDown.append($('<option></option>'));
        }

        // Add the found items
        for (var i = 0; i < result.length; i++) {
            var text = result[i];
            theDropDown.append($('<option></option>').val(text).html(text));
        }
    }
}

The web.config part which concerns the service:

<system.serviceModel>
  <behaviors>
    <endpointBehaviors>
      <behavior name="MyNameSpace.ServiceAspNetAjaxBehavior">
        <enableWebScript />
      </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="true" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  <standardEndpoints>
    <webScriptEndpoint>
      <standardEndpoint crossDomainScriptAccessEnabled="true" name=""/>
    </webScriptEndpoint>
  </standardEndpoints>
  <services>
    <service name="MyNameSpace.Service">
      <!-- Service endpoint for HTTPS -->
      <endpoint address="" behaviorConfiguration="MyNameSpace.ServiceAspNetAjaxBehavior" binding="webHttpBinding" bindingConfiguration="jsonpBinding" contract="MyNameSpace.Service" /> -->
    </service>
  </services>
  <bindings>
    <webHttpBinding>
      <binding name="jsonpBinding" crossDomainScriptAccessEnabled="true">
        <security mode="None" />
      </binding>
      <binding name="jsonpSslBinding" crossDomainScriptAccessEnabled="true">
        <security mode="Transport" />
      </binding>
    </webHttpBinding>
  </bindings>
</system.serviceModel>

I first tried to use the ASP.NET AJAX Proxy to call the service, but this did not work because the call was made to the webserver directly, which is not SSL and the error I got was more or less: 'Page https://gate.company.com/MyPage tried to load not save content from page http://myLocalWebServer/MyPage...'. That is why I used the AJAX-call listed above.

function fillDropdwon(dropId){
   var service = new MyNameSpace.Service();
   service.GetDropDownData(dropId, onDone);
}

I also tried to add the following in the web.config

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <!-- Enable Cross Domain AJAX calls -->
      <remove name="Access-Control-Allow-Origin" />
      <add name="Access-Control-Allow-Origin" value="https://gate.company.com"/>
    </customHeaders>
  </httpProtocol>
</system.webServer>

I checked the headers sent to the server and saw that when I am NOT logged in, the header looks like this:

Request URL:`https://gate.company.com/MyPage/Servic.svc/GetDropDownData?callback=onDone`
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,fr-CH;q=0.2,fr;q=0.2
Connection:keep-alive
Content-Length:161
Content-Type:application/json; charset=UTF-8
Cookie:__utma=174172730.1157990369.1360852643.1381229705.1383150435.9; __utmc=174172730; __utmz=174172730.1369635484.4.3.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); promopost=oaezz3fzzj0o4l3fccxh0ss1;
ASP.NET_SessionID=
Host:`gate.company.com`
Origin:`https://gate.company.com`
Referer:`https://gate.company.com/MyPage/QuickCalculator.aspx?ObjectIdentity=47a93f52-6be6-4bd6-9600-e8eb9c8ff360`
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36
X-Requested-With:XMLHttpRequest
Query String Parametersview sourceview URL encoded
callback:onDone
Request Payloadview source
{dropId:123}
dropId: "123"
Response Headersview source
Cache-Control:private
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:1339
Content-Type:application/x-javascript
Date:Sun, 01 Dec 2013 15:14:25 GMT
Keep-Alive:timeout=15, max=97
Server:Microsoft-IIS/7.5
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-Powered-By

and the response looks like this.

onDone(["result1","result2"]);

When I call the service from within a protected page, I get this:

Request URL:`https://gate.company.com/MyPage/Servic.svc/GetDropDownData?callback=onDone`
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,fr-CH;q=0.2,fr;q=0.2
Connection:keep-alive
Content-Length:161
Content-Type:application/json; charset=UTF-8
Cookie:__utma=174172730.1157990369.1360852643.1381229705.1383150435.9; __utmc=174172730; __utmz=174172730.1369635484.4.3.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); promopost=oaezz3fzzj0o4l3fccxh0ss1;
**ASP.NET_SessionID=; .ASPXAUTH=AB5ADCE12C7847CA452DD54D903E6787C7D1F0009B9E3277D2EC50DE9C421D1331B87A6DCA2432993933794AB9BDE833E44EC58E217D5AA1D588132C6E1C67D4AD7692840359D9A719EC2A53826CF54FDC0943B4E0AB29093920143E1E987080AC7C35E63594FD678535972D06AEC0AAF74AF8BE8DFC3746B499CB032E7771F10B924110DB344824B3253F9BECB3CDD8**
Host:`gate.company.com`
Origin:`https://gate.company.com`
Referer:`https://gate.company.com/MyPage/QuickCalculator.aspx?ObjectIdentity=47a93f52-6be6-4bd6-9600-e8eb9c8ff360`
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36
X-Requested-With:XMLHttpRequest
Query String Parametersview sourceview URL encoded
callback:onDone
Request Payloadview source
{dropId:123}
dropId: "123"
Response Headersview source
Cache-Control:private
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:1339
Content-Type:application/x-javascript
Date:Sun, 01 Dec 2013 15:14:25 GMT
**jsonerror:true**
Keep-Alive:timeout=15, max=97
Server:Microsoft-IIS/7.5
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
**X-Powered-By:ASP.NET**

and the response looks like this.

onDone({"ExceptionDetail":{"HelpLink":null,"InnerException":null,"Message":"Cross domain javascript callback is not supported in authenticated services.","StackTrace":"   bei System.ServiceModel.Dispatcher.JavascriptCallbackMessageInspector.AfterReceiveRequest(Message& request, IClientChannel channel, InstanceContext instanceContext)\u000d\u000a   bei System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.AfterReceiveRequestCore(MessageRpc& rpc)\u000d\u000a   bei System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)\u000d\u000a   bei System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)","Type":"System.NotSupportedException"},"ExceptionType":"System.NotSupportedException","Message":"Cross domain javascript callback is not supported in authenticated services.","StackTrace":"   bei System.ServiceModel.Dispatcher.JavascriptCallbackMessageInspector.AfterReceiveRequest(Message& request, IClientChannel channel, InstanceContext instanceContext)\u000d\u000a   bei System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.AfterReceiveRequestCore(MessageRpc& rpc)\u000d\u000a   bei System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)\u000d\u000a   bei System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)"},500);

The main difference is that there is a SessionID and the jsonerror:true for the version "logged in".

It there a way to fix this problem?

Is it not possible to "disable" the authentication for the AJAX request by changing the header before the call or something like that. Or is there any error in my code, web.config?

I appreciate any hint, since I am trying around for a long time.

like image 958
Adrian Avatar asked Nov 14 '13 13:11

Adrian


1 Answers

I finally found the solution to my problem. I wrote down the steps I took to get to the solution and hope that this will help out anyone with a similar problem.

I first used the ASP.NET AJAX Proxy and made the call like this.

var service = new SDAG.Post.PPT.Website.Service();
service.GetDropDownData(dropId, onDone);

However, this did not work in my environment configuration with the SSL-Proxy which forwards to the web server internally via port 80 (listed above). I got the error message:

The page at <code>'https://gate.company.com/MyPage/Page.aspx?ObjectIdentity=f5c0c016-4828-4935-a7a9-73f3ba47a1ed'</code> was loaded over HTTPS, but displayed insecure content from <code>'http://myLocalWebServer.company.com/MyPage/Service.svc/GetDropDownData'</code>: this content should also be loaded over HTTPS.

ScriptResource.axd?d=8mniuUQAKIvBIxCF_O9BRQpND31cf-SHqs1HBOCcP0DdxGNo4-nOZcF0WZIDoCtTdw5mZIOSt0veif…:2

OPTIONS <code>http://myLocalWebServer.company.com/MyPage/Service.svc/GetDropDownData</code> 405 (Method Not Allowed) ScriptResource.axd?d=8mniuUQAKIvBIxCF_O9BRQpND31cf-SHqs1HBOCcP0DdxGNo4-nOZcF0WZIDoCtTdw5mZIOSt0veif…:2

OPTIONS <code>http://myLocalWebServer.company.com/MyPage/Service.svc/GetDropDownData</code> No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin <code>'https://gate.company.com'</code> is therefore not allowed access. ScriptResource.axd?d=8mniuUQAKIvBIxCF_O9BRQpND31cf-SHqs1HBOCcP0DdxGNo4-nOZcF0WZIDoCtTdw5mZIOSt0veif…:2

XMLHttpRequest cannot load <code>http://myLocalWebServer.company.com/MyPage/Service.svc/GetDropDownData</code>. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin <code>'https://gate.company.com'</code> is therefore not allowed access. Page.aspx?ObjectIdentity=f5c0c016-4828-4935-a7a9-73f3ba47a1ed:1

Refused to get unsafe header "jsonerror" 

I found out that by using the crossDomainScriptAccessEnabled in the webservice binding configuration in the web.config and using a Jsonp Ajax call via jQuery, it did work (see above for the code) BUT: It only worked in the non-authorized area of the page. As soon as the user authenticated on the login page, the call did not work anymore. It told me Cross domain javascript callback is not supported in authenticated services.

Finally, I found the solution. The error message came from the crossDomainScriptAccessEnabled configured in the web.config. When I did remove it, the jsonp call did not work anymore. So what I did is removing the crossDomainScriptAccessEnabled and replace the jsonp-call by a regular json-call.

jQuery.ajax({
        type: "POST",
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        cache: true,
        url: "Service.svc/GetDropDownData",
        data: '{"dropId":"' + dropId + '"}',
        error: function (xhr, textStatus, errorThrown) {
            // Ignore in my case...
        },
        success: function (data, textStatus, xhr) {
            fillSubList(data.d);
        }
    });

function fillSubList(result) {
    var theDropDown = jQuery("#<%= cboSelektion.ClientID %>");
    if (theDropDown.length > 0) {
        //Clear the old entries
        theDropDown.empty();

        //Add the empty one
        if ("<%= cboSelektion.ShowEmptyRow %>".toLowerCase() == "true") {
            theDropDown.append($('<option></option>'));
        }

        // Add the found items
        for (var i = 0; i < result.length; i++) {
            var text = result[i];
            theDropDown.append($('<option></option>').val(text).html(text));
        }
    }
}
like image 198
Adrian Avatar answered Oct 14 '22 12:10

Adrian