3

I have a asp.net website that is hosting a WCF service. This service is then accessed from a desktop app. In my service the HttpContext is always null during the execution of the Validate method in my implementation of the UserNamePasswordValidator class. I'm using Username as the client credential type. I need access to the http context in order to get the Url the service was accessed from in order to validate the username and password correctly as the site can be accessed using different Urls and each one has a different user store.

The following attribute on the class that contains the method that will be called after the validator class (and the validator class as well)

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]

I have a service is configured as follows:

<system.serviceModel>
  <bindings>
    <wsHttpBinding>
      <binding name="wsHttpSecurityOptions">
        <security mode="Message">
          <message clientCredentialType="UserName" establishSecurityContext="true" negotiateServiceCredential="true"/>
          <transport clientCredentialType="Certificate" proxyCredentialType="None"/>
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>
  <behaviors>
    <serviceBehaviors>
    <behavior name="SecurityServiceBehavior">
      <serviceMetadata httpGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="true"/>
      <serviceCredentials>
        <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCFServer.MyAuthenticator" includeWindowsGroups="false"/>
        <serviceCertificate findValue="myurl.com" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
      </serviceCredentials>
    </behavior>
    </serviceBehaviors>
  </behaviors>
  <services>
    <service behaviorConfiguration="SecurityServiceBehavior" name="Test.WCF.Actions">
      <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpSecurityOptions" contract="WCFServer.IActions"/>
    </service>
  </services>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>

I've seen the HttpContext is not initialised on first call bug but this happens to me for every call I make to the service, even when I call the same method on the same connection more than once

Edit: clarified question to answer marc_s's comment and Aliostad's question

Edit: Added following links that suggest the http context should not be null

Can anyone lend me a hand with this please? I'd rather not have to put the site's Url in the appSettings config section for all my sites.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Keith K
  • 2,893
  • 4
  • 33
  • 43
  • What do you need the HttpContext for, in your Validate method?? – marc_s Nov 01 '10 at 16:28
  • Is it null just inside UserNamePasswordValidator.Validate, or inside your [OperationContract] method as well? I am not sure that accessing HttpContext from the validator is a supported scenario... – Eugene Osovetsky Nov 05 '10 at 23:26
  • Its null in the UserNamePasswordValidator.Validate method only. I had this working in an earlier development version when the transport security was defaulting to Windows (it was definitely Username message security). ServiceOperation.Current is null as its supposed to be at this stage but I can't find anything that confirms the HttpContext will be null as well. – Keith K Nov 06 '10 at 00:22

4 Answers4

2

The problem is that you want to access HttpContext from Validate method. As I understand internal WCF implementation Validate method runs in different thread. By design this thread doesn't have access to any context available to main thread processing the request. In Validate method you can't access any WCF based context (OperationContext, ServiceSecurityContext, etc.) so I think it will be the same with HttpContext.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Thanks for your answer but it doesn't solve the problem I (still) have and my marked solution (as pointed out) does have a security issue with it. – Keith K Apr 09 '13 at 10:20
  • 1
    No it doesn't solve the problem. The point of the answer is that WCF's password validation is not supposed to be used in your scenario at all. Instead of trying to hack WCF's implementation you should simply create your own completely independent one. – Ladislav Mrnka Apr 09 '13 at 11:22
  • @LadislavMrnka but is there any way to pass authentication data to service method? E.g. roles, which I add in authentication method. – Johnny_D Aug 18 '14 at 11:20
1

UserNamePasswordValidator's validate method is executed before asp.net pipeline is initialized. So the HttpContext is null. Try using OperationContext instead.

shaans
  • 534
  • 2
  • 5
  • 2
    OperationContext is definitly only created after the validate method executes. Hopefully I'll get some time to dig out the code that had HttpContext working in the validate method to provide an example. – Keith K Nov 12 '10 at 10:49
0

I am not clear on what you are trying to do.

aspNetCompatibilityEnabled only makes sense - as far as I know - when you are using new WCF REST API which does not require a binding configuration. Binding in WCF REST is managed by ASP.NET MVC routing.

If you use configuration API to set up a classic binding, then you are not using the new feature hence "no aspNetCompatibilityEnabled for you"!

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • I am trying to access the HttpContext from my WCF service as shown in http://msdn.microsoft.com/en-us/library/aa702682.aspx in the "Hosting WCF Services in ASP.NET Compatibility Mode" section. aspNetCompatibilityEnabled makes perfect sense when you have a WCF endpoint embedded in asp.net website (WebForms and MVC) – Keith K Nov 01 '10 at 21:14
0

So finally I thought of a workaround. I pass the url that the service is running in to the UserNamePasswordValidator.Validate though the username parameter. I use the format $username$|$siteurl$. Then at the server I separate the two. One thing to note is the

ServiceSecurityContext.Current.PrimaryIdentity.Name
property will then contain $username$|$siteurl$ for the rest of the request so you have to split it into its component everytime you want to access it.

Just to clarify why I need to do this. Our system can run multiple sites with different urls on the same home directory, each with separate authentication that is tied to the url. So without the url I can't authenticate the request. I had been using an appSetting key to provide the url but that meant each site had to have its own home directory.

Keith K
  • 2,893
  • 4
  • 33
  • 43
  • If I understand this correctly you are using client-supplied information to determine which user store to use for authentication. This is very very bad from a security perspective, as I can easily access service A with credentials from service B – csauve Apr 05 '13 at 16:41
  • @csauve - Yes, this isn't ideal, it is a compromise. If you built your own client for the service you could select a different user store to try your credentials on. I did this as I could see no other way (apart from rebuilding the user authentication system) to make this work. To mitigate the chance of someone building their own client for the service I have set the httpGetEnabled to be false for the secure services. I'm open to better answers/workarounds if you have one. – Keith K Apr 09 '13 at 10:17