Confused about WCF and ASP.NET MembershipProvider Forms Authentication - asp.net

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.

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).

WCF Service Authentication + Sitecore

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));
}

HowTo: Pass Windows user credentials of asp.net intranet user to WCF service

I would like to pass the Windows credentials of the user using my ASP.NET MVC application to a WCF service. I want to achieve this via configuration only so that this happens transparently in code.
(This question was originally asked in a too specific manner, as can be seen in the revisions. I only re-asked this in a better way to answer my own in hopes it might help someone. It turned out to be a pretty simple task).
In the web.config (client and server), in the <system.serviceModel> section add/modify a binding to look something like this:
<basicHttpBinding>
<binding name="MyBasicBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
And, add this to client side web.config <system.web> section:
<identity impersonate="true" />
<authentication mode="Windows" />
The two changes will make the end-user the current user of the web request which will then be sent in the WCF message.
The user can then be retrieved on the server side like this:
ServiceSecurityContext.Current.WindowsIdentity

Setting up a WCF host within a website project

This is going to be a weird question, but:
I'm currently running a wcf service on my website (made in Visual Studio 2012 - .scv file). I have a console client which I try to test the connection with in addition to the built in wcf test client. The test client runs fine and I'm able to call upon the functions, but when I stop running the site/test client (which should mean host by extension, right?), my console client can still run and talk to the service just fine.
I'm also able to visit the WCF service page while it's not running in the browser. When I created the WCF in console, I was not able to do any of these things while the host wasn't actually running. Because of this I'm wondering if there's something wrong with the code that I'm just not seeing.
I could see this developing into an issue when I try to get the website online on a server (since my testing client won't be located on the same machine as the service. I'm assuming this is happening because I have access to the files even when it's not running).
What should I do/what is wrong?
Thank you for your time.
relevant web.config code:
<system.serviceModel>
<services>
<!-- ICanHasGamez=solution that holds the webpage&wcfservice -->
<service name="ICanHasGamez.APIHost" behaviorConfiguration="behavior">
<endpoint address="" binding="basicHttpBinding" contract="ICanHasGamez.IAPIHost">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name ="behavior">
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
app.config code from the Console Client:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IAPIHost" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:2105/APIHost.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IAPIHost" contract="ServiceReference1.IAPIHost"
name="BasicHttpBinding_IAPIHost" />
</client>
</system.serviceModel>
</configuration>
In your website project's properties under the web tab, are you hosting it in IIS? If so, the WCF service will keep running, even if you're not using the built in WCF test client. IIS runs on its own, outside of the visual studio environment. You can turn off your service by stopping it in IIS manager, if you'd like, but otherwise it's usable outside of VS.
In contrast, running the WCF service in a console (known as self-hosted) will close down when it's host application, i.e your console application, closes.
In short, if you are using local IIS for your web project, then this isn't anything you've done wrong and is expected behaviour. And regarding running your service on a different machine to your test client, that's not a problem. If you're running it in IIS, then your hosting computer just needs IIS turned on and that project running in IIS there. Voila! If you're running the service as self-hosted, i.e in a console, just leave the console open. Check out this StackOverflow question for some of the pros and cons of self-hosted wcf vs IIS (the accepted answer is a bit local to that particular OP, but the answers below are more informative).
I hope this helps and I hope I'm not barking up the wrong tree here.
I don't see anything wrong with the scenario you described - its expected behavior.
When you host the WCF service in IIS, as long as IIS is up and running (and there's no other problems), your service can receive requests. If the service host has been disposed (due to inactivity timeout or other reasons), if a new request is received IIS will spin up a new instance.
On the other hand, if you're self-hosting the WCF service in, for example, a console app, then the only time that service is able to respond to requests is when the self-hosting application is running.
So to answer your question, you're not doing anything wrong, and you don't need to do anything different. You should be able to simply deploy the WCF Service to the remote server, and then access it with your client.

implementing Ws-security within WCF proxy

I have imported an axis based wsdl into a VS 2008 project as a service reference.
I need to be able to pass security details such as username/password and nonce values to call the axis based service.
I have looked into doing it for wse, which i understand the world hates (no issues there)
I have very little experience of WCF, but have worked how to physically call the endpoint now, thanks to SO, but have no idea how to set up the SoapHeaders as the schema below shows:
<S:Envelope
xmlns:S="http://www.w3.org/2001/12/soap-envelope"
xmlns:ws="http://schemas.xmlsoap.org/ws/2002/04/secext">
<S:Header>
<ws:Security>
<ws:UsernameToken>
<ws:Username>aarons</ws:Username>
<ws:Password>snoraa</ws:Password>
</ws:UsernameToken>
</wsse:Security>
•••
</S:Header>
•••
</S:Envelope>
Any help much appreciated
Thanks, Mark
In order to call these kind of services, you will typically use either basicHttpBinding (that's SOAP 1.1 without WS-* implementations) or then wsHttpBinding (SOAP 1.2, with WS-* implementations).
The main issue will be getting all the security parameters right. I have a similar web service (Java-based) that I need to call - here's my settings and code:
app./web.config
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="SoapWithAuth" useDefaultWebProxy="false">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" proxyCredentialType="None" realm="" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint name="SoapWithAuth"
address="http://yourserver:port/YourService"
binding="basicHttpBinding"
bindingConfiguration="SoapWithAuth"
contract="IYourService" />
</client>
</system.serviceModel>
and then in your client's code when calling the service, you need this snippet of code:
IYourServiceClient client = new IYourServiceClient();
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "top-secret";
Does that help at all?
The WCF client proxy doesn't support the password digest option. The only way to do this is to build the UsernameToken yourself and then inject it into the SOAP headers before the message is sent.
I had a similar problem which is described here, which should be enough to help you solve your same issue.
I ended up using the old WSE3.0 library for the UsernameToken, rather than coding the hashing algorithm myself and then using a custom behavior to alter the SOAP headers.

Resources