wcfiis-7restrestful-authenticationwcf-rest-contrib

Why is my WCF Rest Service on IIS7 Authenticating TWICE?


Ok, if someone could shed some light on this for me, I would greatly appreciate it. So here we go. I had a rest service running fine the other day but after I accidentally overwrote the web.config all hell broke loose. I've spent the past day and a half trying to sort things out but I can't seem to figure out what is missing or misplaced.

So, I've designed this service around WCF Rest Contrib (http://wcfrestcontrib.codeplex.com)'s authentication process. Now, I can get this working fine on my localhost w/ the current web.config (minus the endpoint entry) but once I upload it to discountasp and select "basic authorization" in the ISS7 Manager, it appears that I'm getting authenticated twice! Once using my discount asp.net user/pass and then the next time using the application user/pass. Unfortunately I only provide one set of credentials and don't want to hard code my discountasp account info into the app. Like I said before, this worked fine a few days ago. Anyway. here is my web.config as it is now:

<?xml version="1.0"?>
<configuration>
<connectionStrings>
    <add name="SQL2008_ConnectionString" connectionString="Data Source=sql2k8xx.discountasp.net;Initial Catalog=SQL2008_xx;Persist Security Info=True;User ID=SQL2008_xx_user;Password=myPass"
      providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
    <httpRuntime maxRequestLength="204800" executionTimeout="3600"/>
    <compilation debug="true">
      <assemblies>
        <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      </assemblies>
    </compilation>

    <httpModules>
      <add name="ServiceAnonymityModule" type="WcfRestContrib.Web.ServiceAnonymityModule, WcfRestContrib"/>
    </httpModules>
  </system.web>

  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
        <providerOption name="WarnAsError" value="false"/>
      </compiler>
    </compilers>
  </system.codedom>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules>
      <remove name="ServiceAnonymityModule"/>
      <add name="ServiceAnonymityModule" type="WcfRestContrib.Web.ServiceAnonymityModule, WcfRestContrib"/>
    </modules>
    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated"/>
    </handlers>
  </system.webServer>

  <system.diagnostics>
  <trace autoflush="true" />
  </system.diagnostics>

  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false">
      <baseAddressPrefixFilters>
        <add prefix="http://www.mydomain.com/myServiceBaseAddress"/>
      </baseAddressPrefixFilters>
    </serviceHostingEnvironment>
    <extensions>
      <behaviorExtensions>
        <add name="webAuthentication" type="WcfRestContrib.ServiceModel.Configuration.WebAuthentication.ConfigurationBehaviorElement, WcfRestContrib, Version=1.0.5.0, Culture=neutral, PublicKeyToken=89183999a8dc93b5"/>
        <add name="errorHandler" type="WcfRestContrib.ServiceModel.Configuration.ErrorHandler.BehaviorElement, WcfRestContrib, Version=1.0.5.0, Culture=neutral, PublicKeyToken=89183999a8dc93b5"/>
        <add name="webFormatter" type="WcfRestContrib.ServiceModel.Configuration.WebDispatchFormatter.ConfigurationBehaviorElement, WcfRestContrib, Version=1.0.5.0, Culture=neutral, PublicKeyToken=89183999a8dc93b5"/>
        <add name="webErrorHandler" type="WcfRestContrib.ServiceModel.Configuration.WebErrorHandler.ConfigurationBehaviorElement, WcfRestContrib, Version=1.0.5.0, Culture=neutral, PublicKeyToken=89183999a8dc93b5"/>
      </behaviorExtensions>
    </extensions>
    <bindings>
      <customBinding>
        <binding name="HttpStreamedRest">
          <httpTransport maxReceivedMessageSize="209715200" manualAddressing="true" />
        </binding>
        <binding name="HttpsStreamedRest">
          <httpsTransport maxReceivedMessageSize="209715200" manualAddressing="true" />
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="Rest">

          <webAuthentication
            requireSecureTransport="false"
            authenticationHandlerType="WcfRestContrib.ServiceModel.Dispatcher.WebBasicAuthenticationHandler, WcfRestContrib"
            usernamePasswordValidatorType="MyLibrary.Runtime.SecurityValidator, MyLibrary"
            source="MyRESTServiceRealm"/>
          <webFormatter>
            <formatters defaultMimeType="application/xml">
              <formatter mimeTypes="application/xml,text/xml" type="WcfRestContrib.ServiceModel.Dispatcher.Formatters.PoxDataContract, WcfRestContrib"/>
              <formatter mimeTypes="application/json" type="WcfRestContrib.ServiceModel.Dispatcher.Formatters.DataContractJson, WcfRestContrib"/>
              <formatter mimeTypes="application/x-www-form-urlencoded" type="WcfRestContrib.ServiceModel.Dispatcher.Formatters.FormUrlEncoded, WcfRestContrib"/>
            </formatters>
          </webFormatter>
          <errorHandler errorHandlerType="WcfRestContrib.ServiceModel.Web.WebErrorHandler, WcfRestContrib"/>
          <webErrorHandler
            returnRawException="true"
            logHandlerType="MyLibrary.Runtime.LogHandler, MyLibrary"
            unhandledErrorMessage="An error has occured processing your request. Please contact technical support for further assistance."/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>

So, whenever I upload this and change the ISS setting to Basic Authentication, it looks like it is trying to use the default handler for authentication as if I try to enter my web app user/pass, I get an error screen which has the following detailed information about the moduel/handler

Detailed Error Information

Module: IIS Web Core
Notification: AuthenticateRequest
Handler: svc-ISAPI-2.0
Error Code: 0x80070005

Requested URL: http://www.mydomain.com:80/MyService.../MyService.svc
Physical Path: E:\web\xxxxxx\htdocs\MyServiceBaseAddress\MyService.svc
Logon Method: Not yet determined
Logon User: Not yet determined

Now for the fun stuff... i tried providing my discountasp.net account username/password for kicks and sure enough it responded properly for any [OperationContract] which doesn't have [OperationAuthentication] defined (which is only one or two of the operations I have).

I thought this was strange, so I looked at fiddler and saw something interesting.

Whenever I try request a procedure with [OperationAuthentication] defined and provide my discountasp.net username/pass I get two different "WWW-Authenticate" headers back in Fiddler:

WWW-Authenticate: Basic realm="MyRESTServiceRealm"

WWW-Authenticate: Basic realm="www.mydomain.com"

On the other hand, if I try to access the same procedures with only my application's user/pass, I only get the site's header:

WWW-Authenticate: Basic realm="www.mydomain.com"

My hypothesis is that for some reason I'm having to pass through the default "Basic Authorization" layer set by IIS before I can get to the application's "Custom Basic Authorization" layer.

After verifying this by created an identical user/pass for my service that I use for my discountasp.net account, I was able to successfully pass both layers of authentication without any issues... so I think I can conclude that this is indeed the issue. Now how do I disable the default one? Do I need to do this in the IIS Manager, or in the web.config?

Anyway, I have absolutely no idea how this is possible or what I need to do to resolve the issue, but I know that something is seriously out of whack.

Any suggestions would be greatly appreciated! Thanks.


Solution

  • You said:

    once I upload it to discountasp and select "basic authorization" in the ISS7 Manager, it appears that I'm getting authenticated twice!

    Why are you turning on basic authorization in IIS if your application handles authorization itself? Seems like you just want to enable Anonymous authentication at the IIS level and then let the app do it's own thing.