February 2011 - Posts

About this time last year, I wrote a couple of posts about how to use the “Interceptors” from the REST starker kit for implementing several authentication mechanisms like “SAML”, “Basic Authentication” or “OAuth” in the WCF Web programming model. The things have changed a lot since then, and Glenn finally put on our hands a new version of the Web programming model that deserves some attention and I believe will help us a lot to build more Http oriented services in the .NET stack. What you can get today from wcf.codeplex.com is a preview with some cool features like Http Processors (which I already discussed here), a new and improved version of the HttpClient library, Dependency injection and better TDD support among others.

However, the framework still does not support an standard way of doing client authentication on the services (This is something planned for the upcoming releases I believe). For that reason, moving the existing authentication interceptors to this new programming model was one of the things I did in the last few days.

In order to make authentication simple and easy to extend,  I first came up with a model based on what I called “Authentication Interceptors”. An authentication interceptor maps to an existing Http authentication mechanism and implements the following interface,

public interface IAuthenticationInterceptor
{
    string Scheme { get; }
    bool DoAuthentication(HttpRequestMessage request, HttpResponseMessage response, out IPrincipal principal);
}

An authentication interceptors basically needs to returns the http authentication schema that implements in the property “Scheme”, and implements the authentication mechanism in the method “DoAuthentication”. As you can see, this last method “DoAuthentication” only relies on the HttpRequestMessage and HttpResponseMessage classes, making the testing of this interceptor very simple (There is no need to do some black magic with the WCF context or messages).

After this, I implemented a couple of interceptors for supporting basic authentication and brokered authentication with SAML (using WIF) in my services. The following code illustrates how the basic authentication interceptors looks like.

public class BasicAuthenticationInterceptor : IAuthenticationInterceptor
{
    Func<UsernameAndPassword, bool> userValidation;
    string realm;
 
    public BasicAuthenticationInterceptor(Func<UsernameAndPassword, bool> userValidation, string realm)
    {
        if (userValidation == null)
            throw new ArgumentNullException("userValidation");
 
        if (string.IsNullOrEmpty(realm))
            throw new ArgumentNullException("realm");
 
        this.userValidation = userValidation;
        this.realm = realm;
    }
 
    public string Scheme
    {
        get { return "Basic"; }
    }
 
    public bool DoAuthentication(HttpRequestMessage request, HttpResponseMessage response, out IPrincipal principal)
    {
        string[] credentials = ExtractCredentials(request);
        if (credentials.Length == 0 || !AuthenticateUser(credentials[0], credentials[1]))
        {
            response.StatusCode = HttpStatusCode.Unauthorized;
            response.Content = new StringContent("Access denied");
            response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Basic", "realm=" + this.realm));
 
            principal = null;
 
            return false;
        }
        else
        {
            principal = new GenericPrincipal(new GenericIdentity(credentials[0]), new string[] {});
 
            return true;
        }
    }
 
    private string[] ExtractCredentials(HttpRequestMessage request)
    {
        if (request.Headers.Authorization != null && request.Headers.Authorization.Scheme.StartsWith("Basic"))
        {
            string encodedUserPass = request.Headers.Authorization.Parameter.Trim();
 
            Encoding encoding = Encoding.GetEncoding("iso-8859-1");
            string userPass = encoding.GetString(Convert.FromBase64String(encodedUserPass));
            int separator = userPass.IndexOf(':');
 
            string[] credentials = new string[2];
            credentials[0] = userPass.Substring(0, separator);
            credentials[1] = userPass.Substring(separator + 1);
 
            return credentials;
        }
 
        return new string[] { };
    }
 
    private bool AuthenticateUser(string username, string password)
    {
        var usernameAndPassword = new UsernameAndPassword
        {
            Username = username,
            Password = password
        };
 
        if (this.userValidation(usernameAndPassword))
        {
            return true;
        }
 
        return false;
    }
}

This interceptor receives in the constructor a callback in the form of a Func delegate for authenticating the user and the “realm”, which is required as part of the implementation. The rest is a general implementation of the basic authentication mechanism using standard http request and response messages.

I also implemented another interceptor for authenticating a SAML token with WIF.

public class SamlAuthenticationInterceptor : IAuthenticationInterceptor
{
    SecurityTokenHandlerCollection handlers = null;
 
    public SamlAuthenticationInterceptor(SecurityTokenHandlerCollection handlers)
    {
        if (handlers == null)
            throw new ArgumentNullException("handlers");
 
        this.handlers = handlers;
    }
 
    public string Scheme
    {
        get { return "saml"; }
    }
 
    public bool DoAuthentication(HttpRequestMessage request, HttpResponseMessage response, out IPrincipal principal)
    {
        SecurityToken token = ExtractCredentials(request);
 
        if (token != null)
        {
            ClaimsIdentityCollection claims = handlers.ValidateToken(token);
 
            principal = new ClaimsPrincipal(claims);
 
            return true;
        }
        else
        {
            response.StatusCode = HttpStatusCode.Unauthorized;
            response.Content = new StringContent("Access denied");
 
            principal = null;
 
            return false;
        }
    }
 
    private SecurityToken ExtractCredentials(HttpRequestMessage request)
    {
        if (request.Headers.Authorization != null && request.Headers.Authorization.Scheme == "saml")
        {
            XmlTextReader xmlReader = new XmlTextReader(new StringReader(request.Headers.Authorization.Parameter));
 
            var col = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
            SecurityToken token = col.ReadToken(xmlReader);
 
            return token;
        }
 
        return null;
    }
}
This implementation receives a “SecurityTokenHandlerCollection” instance as part of the constructor. This class is part of WIF, and basically represents a collection of token managers to know how to handle specific xml authentication tokens (SAML is one of them).

I also created a set of extension methods for injecting these interceptors as part of a service route when the service is initialized.

var basicAuthentication = new BasicAuthenticationInterceptor((u) => true, "ContactManager");
var samlAuthentication = new SamlAuthenticationInterceptor(serviceConfiguration.SecurityTokenHandlers);
 
// use MEF for providing instances
var catalog = new AssemblyCatalog(typeof(Global).Assembly);
var container = new CompositionContainer(catalog);
var configuration = new ContactManagerConfiguration(container);
 
RouteTable.Routes.AddServiceRoute<ContactResource>("contact", configuration, basicAuthentication, samlAuthentication);
RouteTable.Routes.AddServiceRoute<ContactsResource>("contacts", configuration, basicAuthentication, samlAuthentication);

In the code above, I am injecting the basic authentication and saml authentication interceptors in the “contact” and “contacts” resource implementations that come as samples in the code preview.

I will use another post to discuss more in detail how the brokered authentication with SAML model works with this new WCF Http bits.

The code is available to download in this location.

Posted by cibrax | 4 comment(s)
Filed under: , ,

We have introduced a new Visual Studio tool called “Service Explorer” as part of the new SO-Aware SDK version 1.3 to help developers to configure and export any regular WCF service into the SO-Aware service repository.

This new tool is a regular Visual Studio Tool Window that can be opened from “View –> Other Windows –> Services Explorer”.

ServicesExplorer_VS

Once you open the Services Explorer, you will able to see all the available WCF services in the Visual Studio Solution.

ServicesExplorer_VS2

In the image above, you can see that a “HelloWorld” service was found in the solution and listed under the Tool window on the left. There are two things you can do for a new service in tool, you can either export it to SO-Aware repository or associate it to an existing service version in the repository.

Exporting the service to SO-Aware means that you want to create a new service version in the repository and associate the WCF service WSDL to that version. Associating the service means that you want to use a version already created in SO-Aware with the only purpose of managing and centralizing the service configuration in SO-Aware.

The option for exporting a service will popup a dialog like the one bellow in which you can enter some basic information about the service version you want to create and the repository location.

ServicesExplorer_VS3

The option for associating a service will popup a dialog in which you can pick any existing service version repository and the application configuration file that you want to keep in sync for the service configuration. Two options are available for configuring a service, WCF Configuration or SO-Aware. The WCF Configuration option just tells the tool that the service will use the standard WCF configuration section “system.serviceModel” but that section must be updated and kept in sync with the configuration selected for the service in the repository. The SO-Aware configuration option will tell the tool that the service configuration will be resolved at runtime from the repository.

ServicesExplorer_VS4

For example, selecting SO-Aware will generate the following configuration in the selected application configuration file,

<configuration>
  <configSections>
    <section name="serviceRepository" type="Tellago.ServiceModel.Governance.ServiceConfiguration.ServiceRepositoryConfigurationSection, Tellago.ServiceModel.Governance.ServiceConfiguration" />
  </configSections>
  <serviceRepository url="http://localhost/soaware/servicerepository.svc">
    <services>
      <service name="ref:HelloWorldService(1.0)@dev" type="SOAwareSampleService.HelloWorldService" />
    </services>
  </serviceRepository>
</configuration>
As you can see the tool represents a great addition to the toolset that any developer can use to manage and centralize configuration for WCF services. In addition, it can be combined with other useful tools like WSCF.Blue (Web Service Contract First) for generating the service artifacts like schemas, service code or the service WSDL itself.
Posted by cibrax | 2 comment(s)
Filed under: , ,
More Posts