Zulfiqar's weblog

Middleware, security & random .Net

Archive for April, 2009

Forms Auth & Federated Security (part 2)

Posted by zamd on April 25, 2009

Here I talked about an approach that you can use to integrate your legacyJ  forms auth applications with STS and bring them into the world of federated security.    

One major hurdle in implementing this approach is to flow Forms Auth cookie to the STS so that it can authenticate the caller and can issue a token. With wsFederationHttpBinding, you don’t directly talk to the STS rather federation binding talks to the STS as part of your service call.  After wsFederationHttpBinding successfully acquired a token, only then your service is called and token is sent as part of the call. This is good because it hides all the token acquisition/forwarding complexity from you and offers you a simple programming model. 

Now in our case, we need to intercept the messages sent by FedBinding to the STS so that we can send our Forms Auth cookie along with the message. 

At this point a very brief diagrammatic overview of WCF message security framework will help: 

 

On the client side TokenProvider is responsible for providing tokens to message security layer. There is TokenProvider for each type of type (Usernname, IssuedToken etc). 

IssuedSecurityTokenProvider is used when a SAML token is required by the message security layer and this is the guy we need to intercept. 

Let’s start by creating a custom ClientCredentials: 

public class ClientCredentialsWrapper : ClientCredentials 

{ 

     public ClientCredentialsWrapper() 

    {} 

    public ClientCredentialsWrapper(ClientCredentials other):base(other) 

    {} 

    public override SecurityTokenManager CreateSecurityTokenManager() 

    { 

        return new ClientSecurityTokenManagerWrapper(this); 

    } 

    protected override ClientCredentials CloneCore() 

    { 

        return new ClientCredentialsWrapper(this); 

    } 

} 

Next our custom SecurityTokenManager: 

class ClientSecurityTokenManagerWrapper : ClientCredentialsSecurityTokenManager 

{ 

    public ClientSecurityTokenManagerWrapper(ClientCredentials parent) 

        : base(parent) 

    { } 

    public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement) 

    { 

        var provider = base.CreateSecurityTokenProvider(tokenRequirement); 

        var issuedProvider = provider as IssuedSecurityTokenProvider; 

        if (issuedProvider != null) 

            issuedProvider.IssuerChannelBehaviors.Add(new MessageInspectorInstallerBehavior()); 

        return provider; 

    } 

} 

Here I have added endpoint behaviour in the ChannelFactory used by IssuedSecurityTokenProvider to talk to STS. 

  

 class MessageInspectorInstallerBehavior : IEndpointBehavior 

{ 

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 

    { } 

  

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 

    { 

        clientRuntime.MessageInspectors.Add(new CookieFlowMessageInspector()); 

    } 

  

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 

    { } 

  

    public void Validate(ServiceEndpoint endpoint) 

    { } 

} 

Here I’m simply installing a MessageInspector which looks like this: 

class CookieFlowMessageInspector : IClientMessageInspector 

{ 

    public void AfterReceiveReply(ref Message reply, object correlationState) 

    { } 

  

    public object BeforeSendRequest(ref Message request, IClientChannel channel) 

    { 

        object prop = null; 

        HttpRequestMessageProperty rmp = null; 

        if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out prop)) 

            rmp = prop as HttpRequestMessageProperty; 

        else 

            rmp = new HttpRequestMessageProperty(); 

  

        rmp.Headers[HttpRequestHeader.Cookie] = FormAuthUtility.GetCookieHeaderFromRequest(); 

        request.Properties[HttpRequestMessageProperty.Name] = rmp; 

        return null; 

    } 

} 

Client code will stay the same and you just have to call just one extension method to hook everything together. 

 var proxy = new STSCookieServiceReference.EchoServiceClient(); 

var certPath = HttpContext.Current.Server.MapPath(“~/localhost.cer”); 

proxy.ClientCredentials.ServiceCertificate.DefaultCertificate = new X509Certificate2(certPath); 

  

proxy.EnableIssuedTokenProviderCookieFlow();  

proxy.Echo(“Welcome.”); 

And this is how extension method is implemented. 

public static void EnableIssuedTokenProviderCookieFlow<TChannel>(this ClientBase<TChannel> source) where TChannel : class 

{     

    var orignal = source.Endpoint.Behaviors.Remove<ClientCredentials>(); 

    source.Endpoint.Behaviors.Add(new ClientCredentialsWrapper(orignal)); 

} 

I have attached complete solution with this post. Feel free to download and have a look.  

  

  

Download: FormsAuthFedSecurity.zip
 

Posted in Federation/STS, WCF | 2 Comments »

Integrating Forms Authentication and Federated Security

Posted by zamd on April 15, 2009

Here I have talked about a technique to flow Forms Authentication cookie to a WCF service and using it as an authentication credential for the service.  For this to work – service must be running in ASP.net compatibility mode and Forms Authentication must be configured in web.config.

Here is diagrammatic view of a simple case.

In the above diagram, website authenticates users using Forms authentication, which creates a cookie after successful authentication. When ASP.net sites calls the backend service (in response to users action) – it simply passes the Form Auth cookie as part of the call. Now because our service is running in asp.net compatibility mode and Forms Authentication module is also configured in web.config, cookie will successfully be validated and we will see correct identity in the HttpContext.User

Looking good so far. Yes?

I think, this is acceptable when you only have one to two services – however when we talk about SOA – we generally mean larger number of services and this is where this approach becomes clunky primarily due to following two reasons:

·         Every service MUST run under asp.net compatibility mode, which is slower than the default native mode. So you incur some performance plenty.

·         For the cookie flow to work, both participants MUST use the same <machineKey>.  As your share single key among more and more participants – the overall security of solution is getting weaker as key will be duplicated across many places.

So there are some scalability & security issues in the above architecture.

 Let’s see how we can improve this architecture by introducing an STS into picture. The goal here is to extract the authentication mechanism (FormsAuth cookie based) out of the services and move it to STS. Service will simply require a token from STS – it is STS’s responsibility to authenticate user using some mechanism (Forms Auth being the step one).

Here you can see ONLY STS is running in asp.net compatibility mode and services are running under native mode which will result in better performance.

Also now we are only sharing keys (<machineKey>) between two participants (STS  & Website) regardless of the number of services. Services are simply configured to require a SAML token from the STS and are agnostic to the actual client authentication mechanism. We can easily change cookie based authentication with Kerberos without impacting services at all.

Now Step 1 isn’t really possible out of box – why?

Because it’s the wsFederationHttpBinding, which talks to STS rather your proxy (configured to use wsFederationHttpBinding). So setting cookie on the proxy will NOT send it to STS and the solution is to extend WCF security framework to enable cookie flow to the STS.

In next post, I will show how to implement Step 1 while still leveraging wsFederationHttpBinding. For now please see this post for background.

 

 

Posted in Federation/STS, WCF | 1 Comment »

MetadataExchangeClient message logging

Posted by zamd on April 13, 2009

Is to possible to add a custom behavior in the channel factory created by MetadataExchangeClient?

For example, is it possible to hook a message inspector to the MetadataExchangeClient  so that generated message can be inspected/logged?

 

It turns that default ctor of MetadataExchangeClient picks the endpoint information (including behavior configuration) from the config file. You can easily achieve this by creating a behavior extension and using it on MEX endpoint. Here are steps:

Step 1: create appropriate config entries

<system.serviceModel>

  <client>

    <endpoint address=http://localhost:9090/mex binding=mexHttpBinding contract=IMetadataExchange behaviorConfiguration=epBv/>

  </client>

  <behaviors>

    <endpointBehaviors>

      <behavior name=epBv>

        <customBehaviorToEnableMessageLogging/>

      </behavior>

    </endpointBehaviors>

  </behaviors>

</system.serviceModel>

Step2: Create a behavior extension 

public class customBehaviorToEnableMessageLogging : BehaviorExtensionElement

{

    public override Type BehaviorType

    {

        get { return typeof(InspectorInstallerBehavior); }

    }

 

    protected override object CreateBehavior()

    {

        return new InspectorInstallerBehavior();

    }

}

Step3: Create an endpoint behavior to install the message inspector

 

public class InspectorInstallerBehavior : IEndpointBehavior

{

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

    {

        clientRuntime.MessageInspectors.Add(new MyInspector());

    }

}

 

Posted in WCF | Leave a Comment »

Error handling with WebHttpBinding and Microsoft Ajax

Posted by zamd on April 2, 2009

Here I talked about a technique to convert SOAP faults into JSON objects for AJAX scenarios. The post was primarily around using WCF extensibility to send back exception details as JSON object with a particular HTTP status code. The code will only works with native XMLHttpRequest object or with a library which returns bare HTTP response (Ext JS, jQuery etc).

Microsoft AJAX library (generated JavaScript proxy) uses a well defined error format and will not understand the custom format I have used. So it will just ignore our custom error object and will return a generic error object to the onFailed callback.

For our server side extensibility feature to work with Microsoft AJAX library, we need to change our service’s error format to match to the format expected by Microsoft AJAX library which can easily be modelled using following DataContract.

[DataContractFormat]

public class MicrosoftAjaxError

{

    [DataMember]

    public string Message { get; set; }

    [DataMember]

    public string ExceptionType { get; set; }

    [DataMember]

    public string StackTrace { get; set; }

}

Now from our handler, we need to send back an instance of MicrosoftAjaxError class rather than our own error object. Here is the modified ProvideFault method.

public void ProvideFault(

    Exception error, MessageVersion version, ref Message fault)

{

    if (error is FaultException)

    {

        // extract the our FaultContract object from the exception object.

        var detail = error.GetType().GetProperty("Detail").GetGetMethod().Invoke(error, null);

 

        var msErrObject = new MicrosoftAjaxError { Message = "Fault exception…", ExceptionType = detail.GetType().ToString(), StackTrace = error.StackTrace };

       

        // create a fault message containing our FaultContract object

        fault = Message.CreateMessage(version, "", msErrObject, new DataContractJsonSerializer(msErrObject.GetType()));

        // tell WCF to use JSON encoding rather than default XML

        var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);

        fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);

 

        var rmp = new HttpResponseMessageProperty();

       

        rmp.Headers[HttpResponseHeader.ContentType] = "application/json";

        rmp.Headers["jsonerror"] = "true";

 

        rmp.StatusCode = System.Net.HttpStatusCode.BadRequest;

        // put appropraite description here..

        rmp.StatusDescription = "See fault object for more information.";

        fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);

    }

}

Posted in Ajax, WCF | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.