28 October 2009

Securing WCF web services for Silverlight with Forms Authentication

One of the most straightforward ways of securing Silverlight-enabled WCF web services involves leveraging ASP.NET Forms Authentication to apply a role-based security model. The advantage of this approach is that it is easy to implement, is based on a familiar API and it allows you to secure web each individual web method declaratively.

Forms authentication is a cookie-based authentication system, so web services that have been secured in this way will require users to explicitly log in before consuming any secured web services. This means that as well as integrating services with forms-based authorization, you also have to expose a set of methods that will allow applications to authenticate users.

Setting up forms authentication for a Silverlight-compatible WCF web service project involves the following steps:

  • Setting up a forms authentication database
  • Enable ASP.NET compatibility in your web service project so forms authentication can be used
  • Adding declarative code to secure each individual web method to define which roles can access each method
  • Exposing a web service that is mapped onto the .NET Authentication Service to expose a set of methods that log users in and out.

Setting up the database

More detail on the basics of setting up forms authentication can be found here, but the main pre-configuration tasks involve setting up a database to hold the user and role information and wiring it up to your web service project:

  • Use the ASP.NET SQL Server Wizard to configure a SQL database with all the tables and procedures you need – this can be started by running aspnet_regsql through a Visual Studio command prompt.
  • Add a connection string entry called LocalSqlServer to your web.config file.
  • Use the ASP.NET configuration tool available on the Project menu in Visual Studio to set up some users and roles.

Enabling ASP.NET compatibility

You will also need to enable ASP.NET compatibility so your web services will be able to interact with forms authentication. This is done by adding the following line to the system.servicemodel section of your web.config file:

<serviceHostingEnvironment aspNetCompatibilityEnabled ="true" />

The class for your service should also be decorated with the AspNetCompatibilityRequirements attribute, which will require a reference to the System.ServiceModel.Activation namespace.

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SecuredWebServices
{
  ...
}

Securing individual web methods

Web methods can be secured declaratively using a PrincipalPermission attribute, which will require a reference to the System.Security.Permissions namespace. The example below secures the method for users who are authenticated members of the “Administrators” role:

[PrincipalPermission(SecurityAction.Demand, Role="Administrators")]
public string SecuredMethod(string input)
{
  //* Do some work here... }

Ensuring that the security principal is passed to the web method

Enabling ASP.NET compatability mode alone is not enough – you need to ensure that the security principal contained in the request’s HttpContext is actually passed to the executing thread, otherwise the web services will not be able to authenticate users.

This can be done by adding a default constructor to your web service classes that explicitly sets the security principal:

public SecuredWebServices()
{
    //* Ensure that the security principal is passed to the executing thread     Thread.CurrentPrincipal = HttpContext.Current.User;
}

Providing an authentication service

Securing the services is all well and good, but you also need to provide a means for a consuming application to log in and start consuming the web services. This can be done by creating a web service that maps directly onto the AuthenticationService in the System.Web.ApplicationServices namespace.

Firstly, add an empty text file to the project called AuthenticationService.svc. You don’t need all the automatic code generated by Visual Studio, hence the use of an empty text file. You should add the following service host reference to this file that maps the service onto the AuthenticationService:

<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.AuthenticationService" %>

As this is WCF you will have to add the following service reference to your web.config file::

<service name="System.Web.ApplicationServices.AuthenticationService"          behaviorConfiguration="AuthenticationServiceBehavior">
  <endpoint binding="basicHttpBinding"             bindingConfiguration="AuthenticationServiceBinding"             contract="System.Web.ApplicationServices.AuthenticationService"             bindingNamespace="http://asp.net/ApplicationServices/v200"></endpoint></service>

The service reference requires the following behaviour to be added:

<behavior name="AuthenticationServiceBehavior">
  <serviceMetadata httpGetEnabled="true" />
</behavior>

The following binding is also required. Note that in a live environment you would not use the security mode “None”.

<basicHttpBinding>
  <binding name="AuthenticationServiceBinding">
    <security mode="None"></security>
  </binding>
</basicHttpBinding>

Finally, you add in a scripting extension as a new section in your web.config file under

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" requireSSL="false"/>
    </webServices>
  </scripting>
</system.web.extensions>

Consuming the web services in Silverlight

The new authentication service can be used like any other Silverlight-enabled web services. If a service reference is created in a Silverlight project then a set of proxies will be automatically generated that provides methods such as Login(), Logout(), IsLoggedIn() and ValidateUser(). These methods can be used to authorise the user with a username and password (over an SSL connection, of course) and then cookie-based authentication will be applied to any methods that have had a PrincipalPermission attribute added to them.

Filed under Net Framework, UI Development.