WCF Service Authentication + Sitecore - asp.net

We are currently implementing WCF services in Sitecore to execute certain tasks. However we want to secure and authenticate these interactions to keep the Sitecore security model intact.
We use following configuration for the authentication (only relevant config and anonymised):
<service name="Services.MailService" behaviorConfiguration="serviceBehavior">
<endpoint address="" binding="wsHttpBinding" contract="Interfaces.IMailService"/>
</service>
<behavior name="serviceBehavior">
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Services.Authentication.CustomServiceAuthentication, MyLibrary" />
</serviceCredentials>
</behavior>
<wsHttpBinding>
<binding>
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
<transport clientCredentialType="None">
</transport>
</security>
</binding>
</wsHttpBinding>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/>
The custom validator inherits from UserNamePasswordValidator and logs the user in using the standard Sitecore.Security.Authentication.AuthenticationManager.Login() method. On this exact moment the user is indeed logged in and appears as Sitecore.Context.User. But when arriving in the WCF method itself this authentication is gone. (resulting in access exceptions from Sitecore as anonymous user does not have add item rights)
After a few tests and studying the interactions I noticed that the issue would be that WCF uses multiple messages and thus multiple HttpContext are being used. The cookies and login are not being retained between the requests. Looking deeper I noticed that the System.ServiceModel.ServiceSecurityContext.Current does retain the security login however it only shows up once entering the WCF method (ea it's not possible to use this in the Sitecore httpBeginRequest pipeline to identify and login the user at the UserResolver)
How can I ensure both asp.net and wcf are properly authenticated throughout the call?

In the end we ended up resolving this by including the following in the constructor of the service since our InstanceContextMode was set to PerCall:
// Handle login for Sitecore to sync with the WCF security context
if (ServiceSecurityContext.Current != null)
{
AuthenticationManager.Login(
string.Format("{0}\\{1}", "yoursitecoredomain", ServiceSecurityContext.Current.PrimaryIdentity.Name));
}

Related

How to pass ASP.NET authentication to a WCF Service

I have a WCF service with basic authentication, which requires a username and password. I am using this service within a thick client and the username and password are stored in the application so can be easily passed.
I now want to use this service with an ASP.NET application. I have security enabled, and it is working fine. I want to know the best way of sending these credentials to my web service. The user name I can get easily using this.User.Identity.Name, but the password is more difficult. Of course I could store it in an encrypted session variable, but is this the right solution? Snippet of code below with the currently hard coded password shown:-
MyServiceClient client = new MyServiceClient();
client.ClientCredentials.UserName.UserName = this.User.Identity.Name;
client.ClientCredentials.UserName.Password = "Password";
BTW: This is my first question after many years of finding answers here, so please go easy on me :-)
To enable the authentication service
If you do not already have an ASP.NET Web application, create one.
Add a service file (.svc) to the Web site that contains the following directive to reference the AuthenticationService class, as shown in the following example:
VB
<%# ServiceHost
Language="VB"
Service="System.Web.ApplicationServices.AuthenticationService"
Factory="System.Web.ApplicationServices.ApplicationServicesHostFactory" %>
C#
<%# ServiceHost
Language="C#"
Service="System.Web.ApplicationServices.AuthenticationService"
Factory="System.Web.ApplicationServices.ApplicationServicesHostFactory" %>
Make the following configuration settings in the Web.config file to configure the service and to require SSL:
Enable the authentication service in the authenticationService element.
Define the endpoint contract in the services element and the service behavior in the behaviors element. Include the bindingNamespace property in the endpoint contract as shown in the following example in order to prevent an exception in some proxy generation tools. For more information about WCF endpoints, see Windows Communication Foundation Endpoints.
Configure the serviceHostingEnvironment element for ASP.NET compatibility. For more information about hosting WCF services, see WCF Services and ASP.NET.
Create a binding in the bindings element that requires SSL. For more information about transport security in WCF, see Transport Security.
The following example shows the system.serviceModel element from a Web.config file that shows the configuration settings described in the previous list.
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled="true"
requireSSL = "true"/>
</webServices>
</scripting>
</system.web.extensions>
<system.serviceModel>
<services>
<service name="System.Web.ApplicationServices.AuthenticationService"
behaviorConfiguration="AuthenticationServiceTypeBehaviors">
<endpoint contract=
"System.Web.ApplicationServices.AuthenticationService"
binding="basicHttpBinding"
bindingConfiguration="userHttps"
bindingNamespace="http://asp.net/ApplicationServices/v200"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="userHttps">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="AuthenticationServiceTypeBehaviors">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
To configure forms authentication
In the Web.config file, configure the Web application to use forms authentication.
The following example shows the authentication element in a Web.config file that is configured to use forms authentication.
<authentication mode="Forms">
<forms cookieless="UseCookies" />
</authentication>
The authentication service requires cookies. Therefore, in the authentication element, set the cookieless attribute to "UseCookies". For more information, see ASP.NET Forms Authentication Overview.
Security
If you are passing sensitive user data such as authentication credentials, always access the authentication service over the secure sockets layer (SSL, by using HTTPS protocol). For information about how to set up SSL, see Configuring Secure Sockets Layer (IIS 6.0 Operations Guide).

Confused about WCF and ASP.NET MembershipProvider Forms Authentication

Reading, watching videos, googling around, I am very confused about how to connect WCF with an ASP.NET app that uses forms authentication with a MembershipProvider. I've seen a suggestion where you have use a specialized service host, explained here (http://msdn.microsoft.com/en-us/library/bb398990.aspx][1]):
<%# ServiceHost Language="C#"
Service="System.Web.ApplicationServices.AuthenticationService" %>
I've also seen an implementation that does it in a ServiceFactory:
ServiceHost serviceHost = new ServiceHost (typeof(MyServices), baseAddresses)
{
Credentials =
{
UserNameAuthentication =
{MembershipProvider = Membership.Provider}
},
Authorization =
{
PrincipalPermissionMode = PrincipalPermissionMode.UseAspNetRoles
}
};
serviceHost.Credentials.ServiceCertificate.SetCertificate(HttpContext.Current.Request.ServerVariables["HTTP_HOST"]);
Both methods are confusing to me. For the first one, where do I specify my specific service contract and what if I have several services? The second method is clearer. But in both cases what happens if I try to access a forms authentication protected directory, for instance, mysite/admin/myservice.svc? Does the security mechanism kick in on both the Service and directory access level? What if you wanted to use two different membership providers, one for the file access and another for the actual WCF service? This wouldn't be an unusual scenario.
Any help would be great, feeling dazed and confused.
The beauty of WCF is that this can be done in the web.config or via code-behind (whichever is your preference). The authentication for WCF is handled in the behavior. I've found it much easier to use the web.config for my bindings. Here is a quick example of how the server configuration would look.
<system.serviceModel>
<bindings>
<wsHttpBinding> <!-- required since BasicHttpBinding has no security model -->
<binding name="FormsAuthProvider">
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredentials="true"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="FormsAuthProvider">
<serviceCredentials>
<usernameAuthetication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="formsProvider"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.web>
<membership>
<providers>
<add name="formsProvider" type="..."/>
</providers>
</membership>
</system.web>
This example configuration works on message security - not transport security (security mode). If you had directory security on the service itself it would be required to have been authenticated prior to consuming the service endpoint.
MSDN doesn't have this option listed as a common security scenario for some reason.

Does WCF (dotnet 4) require webHttpBinding to be able to easily return JSON?

My service will be consumed by another application on the same box (Adobe AIR as it happens), and I can't run a server. I'm currently running a WCF service as a Windows service on the box to achieve this. No server means no REST (please do correct me if I'm wrong with any of this), but I still want my service to be able to return JSON.
I've been doing research on this, and finding lots of people using REST services with webHttpBinding and then setting a JSON behaviour in the config, however for reasons above I believe I can't use REST.
So with that as a background, my question is: Can a WCF service running as either basicHttpBinding, or WSHttpBinding (would like to avoid due to the overheads) return JSON without me having to hand roll it?
If so, would someone be good enough to explain how?
Here are the current details of my app.config for the service
<configuration>
<system.serviceModel>
<services>
<service name="WcfProjectLibrary.ProjectService">
<endpoint address="" binding="wsHttpBinding" contract="WcfProjectLibrary.IProjectService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8732/Design_Time_Addresses/WcfProjectLibrary/ProjectService/" />
</baseAddresses>
</host>
</service>
</services>
<!--<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>-->
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="True"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Thanks
JSON can be returned only with webHttpBinding or custom binding using same binding elements as webHttpBinding and you can still host REST (with webHttpBinding) service in windows service. WebHttpBinding together with WebHttpBehavior are responsible for correct processing of non-SOAP messages.
I have no idea what you mean by no server. Process exposing the service is "a server" in terms of communication. You just need full .NET framework 4 (client profile is not enough) and http.sys installed (you will need it with any HTTP based binding).

WCF Endpoints & Binding Configuration Issues

I am running into a very strange issue here folks. For simplicity I created a project for the sole purpose of testing the issue outside the framework of a larger application and still encountered what is either a bug in WCF within Visual Studio 2010 or something related to my WCF newbie skill set : )
Here is the issue:
I have a WCF endpoint I created running inside of an MVC3 project called "SimpleMethod". The method runs inside of a .svc file on the root of the application and it returns a bool.
Using the "WCF Service Configuration Editor" I have added the endpoint to my Web.Config along with a called "LargeImageBinding".
Here is the service:
[OperationContract]
public bool SimpleMethod()
{
return true;
}
And the Web.Config generated by the Config Tool:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="LargeImageBinding" closeTimeout="00:10:00" />
</wsHttpBinding>
</bindings>
<services>
<service name="WCFEndpoints.ServiceTestOne">
<endpoint address="/ServiceTestOne.svc" binding="wsHttpBinding"
bindingConfiguration="LargeImageBinding" contract="WCFEndpoints.IServiceTestOne" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
The service renders fine and you can see the endpoint when you navigate to: http://localhost:57364/ServiceTestOne.svc - Now the issue occurs when I create a separate project to consume the service. I add a service reference to a running instance of the above project, point it to: http://localhost:57364/ServiceTestOne.svc
Here is the weird part. The service automatically generates just fine but In the Web.Config the endpoint that is generated looks like this:
<client>
<endpoint address="http://localhost:57364/ServiceTestOne.svc/ServiceTestOne.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServiceTestOne"
contract="ServiceTestOne.IServiceTestOne" name="WSHttpBinding_IServiceTestOne">
As you can see it lists the "ServiceTestOne.svc" portion of the address twice!
When I make a call to the the service I get the following error:
The remote server returned an error: (404) Not Found.
I tried removing the extra "/ServiceTestOne.svc" at the end of the endpoint address in the above config, and I get the same exact error.
Now what DOES work is if I go back to the WCF application and remove the custom endpoint and binding references in the Web.Config (everything in the "services" and "bindings" tags) then go back to the consumer application, update the reference to the service and make the call to SimpleMethod()....BOOM works like a charm and I get back a bool set to true.
The thing is, I need to make custom binding configurations in order to allow for access to the service outside of the defaults, and from what I can tell, any attempt to create custom bindings makes the endpoints seem to run fine, but fail when an actual method call is made.
Can anyone see any flaw in how I am putting this together? Thank you for your time - I have been running in circles with this for about a week!
When hosting a WCF service in IIS, the base address of the service is formed using the following format: {protocol}://{host}:{port}/{applicationName}/{svcFileName}. This is the address you can browse to get the WCF help page and/or the metadata (on a default configuration).
To form the actual address of the endpoint (the one your client needs to use), the following format is used: {serviceBaseAddress}/{endpointAddress}
Comparing the format above and the sample configuration you provided explains why you're getting ServiceTestOne.svc twice in your client address.
If you want the address of your service to be http://localhost:57364/ServiceTestOne.svc, I'd recommend leaving the address attribute empty in your endpoint configuration:
<endpoint address="" ... />

Making a WCF Web Service work with GET requests

Background
I have created ASMX web services in the past and have been able to access the service from the web browser and Ajax GET requests using the address convention: MyService.asmx/MyMethod?Param=xxx
I just got started using WCF and created a new web service in my ASP.NET project. It creates a file with the .svc extension such as MyService.svc.
Current Situation
I am able to consume the service using the WcfTestClient that comes with VS2008. I am also able to create my own WCF Client by either adding a service reference in another project or using the svcutil.exe commandline to generate the proxy and config file.
The Problem
When I try to use the service from a browser using MyService.svc/MyMethod?MyParam=xxx, I get a blank page without any errors.
What I have tried
I have already added a basicHttpBinding to the web.config and made it HttpGetEnabled in the behavior configuration. I also added the [WebGet(UriTemplate = "MyMethod?MyParam={MyParam}")] attribute to my operation contract.
I have already followed the information in this other stack overflow question:
REST / SOAP EndPoints for a WCF Service
However, I either get a blank page or an HTTP 404 Error after following those steps. There's nothing special about the code. I am just taking in a string as a parameter and returning "Hello xxx". This is a basic "Hello WCF World" proof-of-concept type thing.
UPDATE - Here's the relevant code
[ServiceContract]
public interface IMyService
{
[WebGet(UriTemplate = "MyMethod/MyParam={MyParam}")]
[OperationContract]
string MyMethod(string MyParam);
}
Web.Config - system.serviceModel Section
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="MyService">
<endpoint address=""
binding="wsHttpBinding" contract="IMyService" />
<endpoint address="MyService.svc"
binding="basicHttpBinding" contract="IMyService" />
<endpoint address="mex"
binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
Looking at your web.config serviceModel section, I can see that you need to add a webHttpBinding and associate an endPointBehavior that includes webHttpGet.
Your operation contract is correct. Here's how your system.serviceModel config section should look in order for you to be able to consume the service from a GET HTTP request.
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="MyService">
<endpoint address="ws" binding="wsHttpBinding" contract="IMyService"/>
<endpoint address="" behaviorConfiguration="WebBehavior"
binding="webHttpBinding"
contract="IMyService">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
Be sure to assign a different address to your wsHttpBinding endpoint, otherwise you will get an error saying that you have two endpoints listening on the same URI.
Another option is to leave the address blank in the wsHttpBinding, but assign a different address to the webHttpBinding service. However, that will change your GET address as well.
For example, if you assign the address as "asmx", you would call your service with the address "MyService.svc/asmx/MyMethod?MyParam=xxxx".
The normal WCF requests are always SOAP requests - you won't be able to get this going with just your browser, you'll need the WCF Testclient for that.
There is an add-on for WCF called the WCF REST Starter Kit (which will also be included in WCF 4.0 with .NET 4.0), which allows you to use GET/POST/PUT/DELETE HTTP commands to query WCF services and such. You need to write your services specifically for REST, though - you can't have SOAP and REST on the same service call.
Marc
As marc_s says, the REST Starter Kit can help, but you should also be aware that .NET 3.5 has support for REST services directly in it. It's not quite as complete as what you can do with the starter kit, but it is useful.
The way it works is that you put a [WebGet] attribute on your operations to indicate where in the URL the various parameters should come from:
[WebGet(UriTemplate = "helloworld/{name}")]
string Helloworld(string name);
See this portal for tons of information.
Note, you can have the same service exposed as both SOAP and REST if you specify multiple endpoints/bindings in the configuration.

Resources