Zulfiqar's weblog

Architecture, security & random .Net

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
 

Advertisements

3 Responses to “Forms Auth & Federated Security (part 2)”

  1. When I use the issuedToken CardSpace shows up with the error: The incoming policy could not be validated. For more information, please see the event log.

    How can Cardspace be triggerd in this case?

    • zamd said

      Hi,
      When there isn’t enough information on how to acquire a token (e.g. STS binding or URL is missing), the IssueTokenProvider launches the CS selector and asks it for token issuance. Please make sure required information is passed to IssuedSecurityTokenProvider….

  2. […] http://zamd.net/2009/04/25/forms-auth-federated-security-part-2/ […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: