views:

2774

answers:

3

Hello,

I am working on a Silverlight v3 web app and I would like to secure access to the WCF service I am using to fetch my data. I currently have the WCF working just fine, but it doesn't require any user credentials.

I'm not very experienced with this aspect of WCF, so my first idea was to add username and password parameters to each of my service's operations. The problem I have with this is that this would require a lot of redundant code, and the fact that the username and password would be transferred over the wire in plain text.

What I would like is a way to specify the credentials upfront on the client side right after I create my service proxy (I am using the proxy autogenerated from "Add Service Reference").

Upon googling for a solution to this, I could only find solutions that similar to my first idea (using username/password parameters). Could someone please point me in the right direction?

Thanks!

A: 

Hi, you can pass in some sort of authentication object and encrypt it at the message level with WCF. C# aspects (http://www.postsharp.org/) can then be used to avoid redundant logic. Its a very clean way of handling it.

Steve
+2  A: 

Don't roll your own and add explicit parameters - that is indeed way too much work!

Check out the WCF security features - plenty of them available! You can e.g. secure the message and include credentials inside the message - all out of the box, no extra coding on your side required!

Check out this excellent article on WCF security by Michele Leroux Bustamante: http://www.devx.com/codemag/Article/33342

In your case, I'd suggest message security with user name credentials - you need to configure this on both ends:

Server-side:

<bindings>
  <basicHttpBinding>
    <binding name="SecuredBasicHttp" >
      <security mode="Message">
        <message clientCredentialType="UserName"/>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>
<services>
  <service name="YourService">
    <endpoint address="http://localhost:8000/MyService"
              binding="basicHttpBinding"
              bindingConfiguration="SecuredBasicHttp"
              contract="IYourService" />
  </service>
</services>

And you need to apply the same settings on the client side:

<bindings>
  <basicHttpBinding>
    <binding name="SecuredBasicHttp" >
      <security mode="Message">
        <message clientCredentialType="UserName"/>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>
<client>
    <endpoint address="http://localhost:8000/MyService"
              binding="basicHttpBinding"
              bindingConfiguration="SecuredBasicHttp"
              contract="IYourService" />
</client>

Now your server and client agree on the security - on the client, you'd then specify the user name and password to use like this:

YourServiceClient client = new YourServiceClient();

client.ClientCredentials.UserName.UserName = "your user name";
client.ClientCredentials.UserName.Password = "top$secret";

On the server side, you'll need to set up how these user credentials are being validated - typically either against a Windows domain (Active Directory), or against the ASP.NET membership provider model. In any case, if the user credentials cannot be verified against that store you define, the call will be rejected.

Hope this helps a bit - security is a big topic in WCF and has lots and lots of options - it can be a bit daunting, but in the end, usually it does make sense! :-)

Marc

marc_s
I'm not sure what scenario in which you were able to do this, but in my experience, this is not allowed in WCF. You can't use message-mode username-credential security in basicHttpBinding, it's disallowed by the framework because the credentials would be passed in plain text. You'll get this InvalidOperationException: "BasicHttp binding requires that BasicHttpBinding.Security.Message.ClientCredentialType be equivalent to the BasicHttpMessageCredentialType.Certificate credential type for secure messages. Select Transport or TransportWithMessageCredential security for UserName credentials."
Grank
+4  A: 

Where are these usernames and passwords coming from? If your web site already implements Forms authentication then you can bypass setting credentials yourself and use the forms authentication cookie. If your users are logged in then the cookie will travel with the web service call. In order to read it on the other side you need to make a couple of changes.

First you need to enable ASP.NET compatibility mode for WCF in the system.ServiceModel section:

<system.serviceModel>  
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> 
</system.serviceModel>

Once that is done then for each service method you want to understand the ASP.NET cookie add the [AspNetCompatibilityRequirements] attribute to your service class

[ServiceContract]
[AspNetCompatibilityRequirements(
    RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ExampleService
{
}

Now within each method you can access the HttpContext.Current.User.Identity object to discover the user's identity.

If you only want certain methods to be called by authenticated users then you can use a PrincipalPermission thus

[OperationContract]
[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
public string Echo()

As a bonus if you're using ASP.NET's role provider then those will also be populated and you can then use a PrincipalPermission on methods to limit them to members of a particular role:

[OperationContract]
[PrincipalPermission(SecurityAction.Demand, Role="Administators")]
public string NukeTheSiteFromOrbit()

And this works in Silverlight2 as well obviously.

blowdart
Looks good; do you happen to know if this (or something similar) is usable if using raw httpwebrequest? I have a custom RPC stack that I'd like to secure in the same way (I can of course ask as a new queston if it is a non-trivial answer)
Marc Gravell
It should do yes; see http://www.silverlightshow.net/items/Cookies-in-Silverlight-Web-Requests.aspx
blowdart
Ta; I'll look at that ;-p
Marc Gravell
It's sorta late, but... thanks blowdart!
Charles