Zulfiqar's weblog

Architecture, security & random .Net

Pass-through SAML tokens & Secure Conversation (Part2)

Posted by zamd on June 7, 2009


Few people have contacted me regarding follow-up on my last post so this post continues where the last one left off.

To keep the API simple, I have created few extension methods. Let’s first review these.

public static class Extensions

{

    public static void EnableSamlTokenCaching(this ServiceHostBase source)

    {

        var orignal = source.Description.Behaviors.Remove<ServiceCredentials>();

        source.Description.Behaviors.Add(new ServiceCredentialsEx(orignal));

    }

 

    public static SamlSecurityToken GetIncommingSamlToken(this ServiceSecurityContext source)

    {

        var policy = source.AuthorizationPolicies.OfType<SamlTokenCachingPolicy>().FirstOrDefault();

        if (policy != null)

            return policy.IncommingSamlToken;

        return null;

    }

 

    public static void EnableSamlTokenFlow<TChannel>(this ClientBase<TChannel> source, SamlSecurityToken tokenToFlow) where TChannel : class

    {

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

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

    }

}

EnableSamlTokenCaching is what you have to call to enable SAML token caching functionality for a particular ServiceHost. Here is an example of a simple host (created using a custom factory (IIS/WAS hosted scenario)) being configured with this API

public class EchoServiceHostFactory : ServiceHostFactoryBase

{

    public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)

    {

        var sh = new ServiceHost(typeof(EchoService), baseAddresses);

        sh.EnableSamlTokenCaching();

        return sh;

    }

}

The highlighted call simply injects couple of customized components (interceptors) into WCF’s security framework. Specifically it is injecting a new customized SamlSecurityTokenAuthenticator, which wraps the original authenticator and simply delegates all calls to it. 

SamlSecurityTokenAuthenticator is called during secure conversation handshake. Here our custom authenticator (SamlSecurityTokenAuthenticatorEx) enables us to get hold of the incoming SAML token etc.

public class ServiceCredentialsEx : ServiceCredentials

{

    public ServiceCredentialsEx(ServiceCredentials other)

        : base(other){}

 

    public override SecurityTokenManager CreateSecurityTokenManager()

    {

        return new ServiceCredentialsSecurityTokenManagerEx(this);

    }

    protected override ServiceCredentials CloneCore()

    {

        return new ServiceCredentialsEx(this);

    }

}

 

public class ServiceCredentialsSecurityTokenManagerEx : ServiceCredentialsSecurityTokenManager

{

    public ServiceCredentialsSecurityTokenManagerEx(ServiceCredentials parent)

        : base(parent){}

 

    public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)

    {

        var authenticator = base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);

        if (authenticator is SamlSecurityTokenAuthenticator)

            return new SamlSecurityTokenAuthenticatorEx((SamlSecurityTokenAuthenticator)authenticator);

        return authenticator;

    }

}

Inside SamlSecurityTokenAuthenticatorEx, I’m simply creating an additional token caching policy and   adding it to the collection of policies created by default SAML token authenticator.

public class SamlSecurityTokenAuthenticatorEx : SamlSecurityTokenAuthenticator

{

    SamlSecurityTokenAuthenticator wrapped;

 

    public SamlSecurityTokenAuthenticatorEx(SamlSecurityTokenAuthenticator wrapped)

        : base(new List<SecurityTokenAuthenticator>())

    {

        this.wrapped = wrapped;

    }

    protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)

    {

        var orignalPolicies = wrapped.ValidateToken(token);

        List<IAuthorizationPolicy> finalPolicies = new List<IAuthorizationPolicy>(orignalPolicies);

 

        finalPolicies.Add(new SamlTokenCachingPolicy((SamlSecurityToken)token));

        return finalPolicies.AsReadOnly();

    }

}

My SamlTokenCachingPolicy is just a wrapped around the cached token.

public class SamlTokenCachingPolicy : IAuthorizationPolicy

{

    public const string PolicyID = “{32835E28-3ED4-42d4-A2EA-FA71E13AF51F}”;

    public SamlTokenCachingPolicy(SamlSecurityToken token)

    {

        this.IncommingSamlToken = token;

    }

    public bool Evaluate(EvaluationContext evaluationContext, ref object state)

    {

        return true;

    }

 

    public ClaimSet Issuer

    {

        get { throw new System.NotImplementedException(); }

    }

 

    public string Id

    {

        get { return PolicyID; }

    }

 

    public SamlSecurityToken IncommingSamlToken { get; private set; }

}

IAuthorizationPolicy model nicely aligns with WCF security framework (so I have used it here). If you are not comfortable with this approach, you could get the same results by choosing a different caching strategy.

Inside your service method you can get hold of the incoming SAML token using following piece of code.

[ServiceBehavior(AddressFilterMode = AddressFilterMode.Prefix)]

public class EchoService : IEchoService

{

    public string Echo(string input)

    {

        // Get token from incoming security context..

        var samlToken = OperationContext.Current.ServiceSecurityContext.GetIncommingSamlToken();

    }

}

GetIncommingSamlToken is again an extension which extracts the actual token from my custom AuthorizationPolicy(SamlTokenCachingPolicy).

    public static SamlSecurityToken GetIncommingSamlToken(this ServiceSecurityContext source)

    {

        var policy = source.AuthorizationPolicies.OfType<SamlTokenCachingPolicy>().FirstOrDefault();

        if (policy != null)

            return policy.IncommingSamlToken; 

        return null;

    }

Oh, this post is already way too long. Probably next time I will show you, how to pass this token to backend services (sharing the same certificate).

Advertisements

3 Responses to “Pass-through SAML tokens & Secure Conversation (Part2)”

  1. David Killian said

    This article aludes to a planned follow-on describing a solution to the backend services token access. I could find nothing later in the blog. Did you post it elsewhere?

    • zamd said

      Hi David,
      Never get a chance to do the final post on the series. Anyways, things have moved on since then and now a days you should use WIF for doing this kinds of token flow.

      Thanks,
      Zulfiqar

      • David Killian said

        Intestesting comment, because I’m using this technique to avoid routing problems while flowing SAML tokens. Working at the channel level in routing (or at least the encoder level) seems to have distinct advantages.

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: