Monday, 1 September 2008

Extending LINQ to Entities

The other day I was trying to perform the equivalent of a SQL 'Where In' Clause using the Entity Framework.

As of today, the Entity Framework does not support such functionality natively. However, it is possible to replicate this functionality in a straight forward way.

Once again, I'm going to use extension methods and lambda expressions to get where I need.

To understand the way I've achieved this, you need to understand that (what I call) LINQ methods such as Where, OrderBy, Take etc manipulate the IQueryable<T> interface (where T is an EntityObject within your ObjectContext). So in order to extend the functionality of the Entity Framework, we're going to need to extend any class that implements the IQueryable<T> interface.

The extension method looks like this:

public static IQueryable<T> In<T, TValue>(this IQueryable<T> source, Expression<Func<T, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}

if (null == values)
{
throw new ArgumentNullException("values");
}

if (!values.Any())
{
throw new ArgumentOutOfRangeException("values");
}

ParameterExpression p = valueSelector.Parameters.Single();

var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));

var body = equals.Aggregate((accumulate, equal) => Expression.Or(accumulate, equal));

var containsExpression = Expression.Lambda<Func<T, bool>>(body, p);

string command = "Where";

Type type = typeof(T);

var resultExpression = Expression.Call(
typeof(Queryable),
command,
new Type[] { type },
source.Expression,
Expression.Quote(containsExpression));

return source.Provider.CreateQuery<T>(resultExpression);
}


Firstly, we're providing two arguments - the first is a parameter to tell the 'In' method how to select values within the clause. The second is a list of values mimicking the In clause itself.

We expand the Expression tree to write a standard 'Where' clause, with each predicate explictly stated, instead of

Where [Name] In { A, B, C }

we write

Where [Name] = 'A' OR [Name] = 'B' OR [Name] = 'C'

Finally, we execute the expression on the source object. The return type of IQueryable<T> enables us to 'chain' this method with any of the existing methods.

To call this method, do something like this:

List<string> names = new List<string>();
names.Add("Fluffy");
names.Add("Sweetness");
var results = objectcontext.Person.In(p => p.Name, names).ToList();


This selects all users whose [Name] is either 'Fluffy' or 'Sweetness' from the table 'Person' within my object context. You'll notice that the code has the same feel as it might with any of the native LINQ methods.

Happy Coding!

Graeme

Friday, 29 August 2008

Method decoration using Lambda expressions

My latest foray into the world of .NET 3.5 got me wondering about how I might be able to 'decorate' methods from other assemblies, using a standard pattern, with the minimum of fuss.

To illustrate, in my particular example I wanted a 'RunAs' functionality for any existing method; so the workflow goes as follows:

1 - Cache any existing user details
2 - Logon a new user
3 - Execute some code
4 - Return the previous context

I didn't want other developers writing their own code to do this, because the potential for mistakes and misunderstanding is very high.

In the above example steps 1, 2 and 4 aren't important, I could have equally written

1 - Do something
2 - Execute some code
3 - Do something else

I found a neat way of doing this, involving both Lambda expressions and Extension methods (although I'm sure there's plenty of other ways to achieve the same thing).

Firstly, we'll create an extension method (mine is of System.Object so that ALL objects can do this, but you may want to restrict yours).

/// <summary>
/// Enables ANY Function to be executed as another user (prompts logon first)
/// </summary>
/// <typeparam name="R">Return type</typeparam>
/// <param name="source">Type to extend</param>
/// <param name="f">Function to be executed</param>
/// <returns>The response from the wrapped method</returns>
/// <remarks>Call this method like so:
/// <c>var result = this.RunAs(() => this.DoSomething());</c>
/// </remarks>
public static R RunAs<R>(this object source, Func<R> f)
{
DO SOMETHING PRIOR...
R result = f();
... DO SOMETHING POST
return result;
}

To call this method, we may write something like:

var result = this.RunAs(() => this.DoSomething());

Notice that the lambda expression is clever enough to figure out the return type for us. Pretty cool. Another remarkable fact is that the signature of the method you're calling doesn't even have to match the Func<R> delegate type in the extension method...

var result this.RunAs(() => this.DoSomethingElse( 1, 2, 3));

... would be equally valid! This is because the lambda expression actually evaluates to a Func<R> delegate, i.e. a function that takes no arguments (hence the () construct) and that returns something of type R (the return type of 'DoSomething').

An overload of the RunAs method is needed to execute an Action delegate (i.e. one with no return type)

public static void RunAs(this object source, Action a)
{
DO SOMETHING PRIOR...
a();
... DO SOMETHING POST
}


Does anybody else think this is totally cool?

Lambda expressions really are a very powerful tool for your programming locker.

Cheers!

Graeme

Monday, 18 August 2008

Custom WCF Channel in Silverlight

Here's some code I use to create a custom channel in Silverlight. I use this to mimic the IMessageInspector behaviour available in the standard WCF stack (but alas, not in Silverlight).

Firstly you'll need a Custom BindingElement:

/// <summary>
/// Custom binding element - builds the inner channel
/// </summary>
public class ClientChannelBindingElement : BindingElement
{
#region Constructors
/// <summary>
/// Default constructor
/// </summary>
public ClientChannelBindingElement()
{
}
/// <summary>
/// Protected constructor
/// </summary>
/// <param name="other">Another binding element</param>
protected ClientChannelBindingElement(ClientChannelBindingElement other)
: base(other)
{
}
#endregion
#region Public methods
/// <summary>
/// Build a channel factory
/// </summary>
/// <typeparam name="TChannel">Channel of interest</typeparam>
/// <param name="context">Binding context</param>
/// <returns>A new channel factory</returns>
public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
ClientChannelFactory<TChannel> factory = new ClientChannelFactory<TChannel>();
factory.InnerChannelFactory = context.BuildInnerChannelFactory<TChannel>();
return factory;
}
/// <summary>
/// Copy this object
/// </summary>
/// <returns>A copy of this</returns>
public override BindingElement Clone()
{
return new ClientChannelBindingElement(this);
}
/// <summary>
/// Gets an element property
/// </summary>
/// <typeparam name="T">Channel of interest</typeparam>
/// <param name="context">Binding context</param>
/// <returns>Property value</returns>
public override T GetProperty<T>(BindingContext context)
{
return context.GetInnerProperty<T>();
}
#endregion
}


Secondly, you'll need to reference this from a binding. I've made this binding look the same as a BasicHttpBinding, so that it may be used from Silverlight:

/// <summary>
/// Custom Silverlight binding (mimics BasicHttpBinding)
/// </summary>
public class ClientChannelBinding : CustomBinding
{
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public ClientChannelBinding()
{
BasicHttpBinding basic = new BasicHttpBinding();
this.Elements.Add(new ClientChannelBindingElement());
this.Elements.Add(new TextMessageEncodingBindingElement(basic.MessageVersion, basic.TextEncoding));
this.Elements.Add(new HttpTransportBindingElement());
}
#endregion
#region Public methods
/// <summary>
/// Builds the channel factory
/// </summary>
/// <typeparam name="TChannel">Channel of interest</typeparam>
/// <param name="parameters">Binding parameters</param>
/// <returns>An encapsulated channel factory</returns>
public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingParameterCollection parameters)
{
ClientChannelFactory<TChannel> factory = new ClientChannelFactory<TChannel>();
BasicHttpBinding binding = new BasicHttpBinding();
factory.InnerChannelFactory = binding.BuildChannelFactory<TChannel>();
return factory;
}
#endregion
#region Internal methods
/// <summary>
/// Async Opened event handler
/// </summary>
/// <typeparam name="TChannel">Channel of interest</typeparam>
/// <param name="result">Async result</param>
internal void OnOpened<TChannel>(IAsyncResult result)
{
if (result.CompletedSynchronously)
{
return;
}
else
{
CompleteOpen<TChannel>(result);
}
}
/// <summary>
/// Async callback handler
/// </summary>
/// <typeparam name="TChannel">Channel of interest</typeparam>
/// <param name="result">Async result</param>
internal void CompleteOpen<TChannel>(IAsyncResult result)
{
IChannelFactory<TChannel> factory = (IChannelFactory<TChannel>)result.AsyncState;
factory.EndOpen(result);
}
#endregion
}


Next, you'll need a channel ...

/// <summary>
/// Silverlight client channel class
/// </summary>
public class ClientChannel : IRequestChannel
{
#region Fields
/// <summary>
/// Internal channel
/// </summary>
private IRequestChannel innerChannel;
#endregion
#region Constructors
/// <summary>
/// Channel Constructor
/// </summary>
/// <param name="innerChannel">Inner channel</param>
public ClientChannel(IRequestChannel innerChannel)
{
this.innerChannel = innerChannel;
}
#endregion
#region Events
/// <summary>
/// Close event handler
/// </summary>
public event EventHandler Closed;
/// <summary>
/// Closing event handler
/// </summary>
public event EventHandler Closing;
/// <summary>
/// Faulted event handler
/// </summary>
public event EventHandler Faulted;
/// <summary>
/// Open event handler
/// </summary>
public event EventHandler Opened;
/// <summary>
/// Opening event handler
/// </summary>
public event EventHandler Opening;
#endregion
#region Properties
/// <summary>
/// Gets the remote address of the channel
/// </summary>
public EndpointAddress RemoteAddress
{
get
{
return this.innerChannel.RemoteAddress;
}
}
/// <summary>
/// Gets the coomunication state object
/// </summary>
public CommunicationState State
{
get
{
return this.innerChannel.State;
}
}
/// <summary>
/// Gets the URI (from inner channel)
/// </summary>
public Uri Via
{
get
{
return this.innerChannel.Via;
}
}
#endregion
#region Methods
/// <summary>
/// Async begin request
/// </summary>
/// <param name="message">Message to send</param>
/// <param name="timeout">Timeout period</param>
/// <param name="callback">Callback method</param>
/// <param name="state">State information</param>
/// <returns>Asynchronous result</returns>
public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
{

// In Silverlight this is the place to add the header to the outgoing request
... ADD THE HEADER HERE ...
this.Decorate(message);
IAsyncResult result = this.innerChannel.BeginRequest(message, timeout, callback, state);
return result;
}
/// <summary>
/// Async begin request
/// </summary>
/// <param name="message">Message to send</param>
/// <param name="callback">Callback method</param>
/// <param name="state">State information</param>
/// <returns>Asynchronous result</returns>
public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
{

... ADD THE HEADER HERE ...
this.Decorate(message);
IAsyncResult result = this.innerChannel.BeginRequest(message, callback, state);
return result;
}
/// <summary>
/// End the request - parse the reply for the security header
/// </summary>
/// <param name="result">Async results object</param>
/// <returns><c>Message</c> reply</returns>
public Message EndRequest(IAsyncResult result)
{

// In Silverlight this is the place to read the header from the incoming reply
Message reply = this.innerChannel.EndRequest(result);

... READ THE HEADER HERE ...
return reply;
}
/// <summary>
/// Synchronous request
/// </summary>
/// <param name="message">Outgoing message</param>
/// <param name="timeout">Timeout period</param>
/// <returns>Modified message</returns>
public Message Request(Message message, TimeSpan timeout)
{
this.Decorate(message);
return this.innerChannel.Request(message, timeout);
}
/// <summary>
/// Synchronous request
/// </summary>
/// <param name="message">Outgoing message</param>
/// <returns>Modified message</returns>
public Message Request(Message message)
{
this.Decorate(message);
return this.innerChannel.Request(message);
}
/// <summary>
/// Gets a channel property
/// </summary>
/// <typeparam name="T">Type of property</typeparam>
/// <returns>Property result</returns>
public T GetProperty<T>() where T : class
{
return this.innerChannel.GetProperty<T>();
}
/// <summary>
/// Abort this channel
/// </summary>
public void Abort()
{
this.innerChannel.Abort();
}
/// <summary>
/// Asynchronous close
/// </summary>
/// <param name="timeout">Timeout period</param>
/// <param name="callback">Callback method</param>
/// <param name="state">State information</param>
/// <returns>Asynchronous result</returns>
public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
{
return this.innerChannel.BeginClose(timeout, callback, state);
}
/// <summary>
/// Asynchronous close
/// </summary>
/// <param name="callback">Callback method</param>
/// <param name="state">State information</param>
/// <returns>Asynchronous result</returns>
public IAsyncResult BeginClose(AsyncCallback callback, object state)
{
return this.innerChannel.BeginClose(callback, state);
}
/// <summary>
/// Asynchronous open
/// </summary>
/// <param name="timeout">Timeout period</param>
/// <param name="callback">Callback method</param>
/// <param name="state">State information</param>
/// <returns>Asynchronous result</returns>
public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
return this.innerChannel.BeginOpen(timeout, callback, state);
}
/// <summary>
/// Asynchronous open
/// </summary>
/// <param name="callback">Callback method</param>
/// <param name="state">State information</param>
/// <returns>Asynchronous result</returns>
public IAsyncResult BeginOpen(AsyncCallback callback, object state)
{
return this.innerChannel.BeginOpen(callback, state);
}
/// <summary>
/// Synchronous close
/// </summary>
/// <param name="timeout">Timeout period</param>
public void Close(TimeSpan timeout)
{
this.innerChannel.Close(timeout);
}
/// <summary>
/// Synchronous close
/// </summary>
public void Close()
{
this.innerChannel.Close();
}
/// <summary>
/// Asynchronous callback - close
/// </summary>
/// <param name="result">Async result</param>
public void EndClose(IAsyncResult result)
{
this.innerChannel.EndClose(result);
}
/// <summary>
/// Asynchronous callback - open
/// </summary>
/// <param name="result">Async result</param>
public void EndOpen(IAsyncResult result)
{
this.innerChannel.EndOpen(result);
}
/// <summary>
/// Synchronous open
/// </summary>
/// <param name="timeout">Timeout period</param>
public void Open(TimeSpan timeout)
{
this.innerChannel.Open(timeout);
}
/// <summary>
/// Synchronous open
/// </summary>
public void Open()
{
this.innerChannel.Open();
}
#endregion
#region Protected Methods
/// <summary>
/// Decorate the message with the new headers
/// </summary>
/// <param name="message">Message to decorate</param>
protected virtual void Decorate(Message message)
{
message.Headers.Add(new SecurityHeader());
}
#endregion
}


[Note: the SecurityHeader class is derived from the MessageHeader class]

... and an associated channel factory:


/// <summary>
/// Overriden custom channel factory
/// </summary>
/// <typeparam name="TChannel">Type of channel to create</typeparam>
public class ClientChannelFactory<TChannel> : ChannelFactoryBase<TChannel>
{
#region Fields
/// <summary>
/// Inner channel factory
/// </summary>
private IChannelFactory<TChannel> innerChannelFactory;
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public ClientChannelFactory()
{
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the inner channel factory
/// </summary>
public IChannelFactory<TChannel> InnerChannelFactory
{
get
{
return this.innerChannelFactory;
}
set
{
this.innerChannelFactory = value;
}
}
#endregion
#region Protected methods
/// <summary>
/// Open event handler
/// </summary>
/// <param name="timeout">Timeout period</param>
protected override void OnOpen(TimeSpan timeout)
{
this.innerChannelFactory.Open(timeout);
}
/// <summary>
/// Create channel evebt handler
/// </summary>
/// <param name="to">Destination of channel</param>
/// <param name="via">Via this URI</param>
/// <returns>Newly created channel</returns>
protected override TChannel OnCreateChannel(EndpointAddress to, Uri via)
{
TChannel innerchannel = this.innerChannelFactory.CreateChannel(to, via);
if (innerchannel is IRequestChannel)
{
ClientChannel clientChannel = new ClientChannel((IRequestChannel)innerchannel);
return (TChannel)(object)clientChannel;
}
return innerchannel;
}
/// <summary>
/// End open async callback handler
/// </summary>
/// <param name="result">Async result</param>
protected override void OnEndOpen(IAsyncResult result)
{
this.innerChannelFactory.EndOpen(result);
}
/// <summary>
/// Begin open async handler
/// </summary>
/// <param name="span">Timeout span</param>
/// <param name="callback">Callback method</param>
/// <param name="asyncState">Async state object</param>
/// <returns>Async result</returns>
protected override IAsyncResult OnBeginOpen(TimeSpan span, AsyncCallback callback, object asyncState)
{
return this.innerChannelFactory.BeginOpen(callback, asyncState);
}
#endregion
}


Finally, you'll need to create the channel from your code (the client class is autogenerated when you import the service):


ClientChannelBinding binding = new ClientChannelBinding();
EndpointAddress endpoint = new EndpointAddress("http://localhost:3532/Authentication.svc"); ////TODO get this from config
this.client = new Authentication.AuthenticationClient(binding, endpoint);

Happy coding!

Graeme

Wednesday, 25 June 2008

TFS Automated Build - Mailing the developer

Not strictly identity related, but anything to make my life easier is all good:

A little while ago I asked a question on the TFS forum. It was a simple request to email the person who requested the build when the build was finished, including the status of the build (success/failure).
The answer I received was to implement a web service, with a known interface, and subscribe this service to receive events from the build process. The web service receives a chunk of xml, which I'd need to parse to retrieve the user name. In turn I'd need to look up the email of this user (somehow!). All a bit of pain, frankly. Suffice to say, we never seemed to have the time to do this.

Recently I have been investigating gated builds, and in particular an open source product called OpenGauntlet. I noticed that these guys are emailing developers on start and on finish of their gated builds - brilliant. A quick look through the source code showed me how:

The TFS API is rich in functionality, but the important interface in this case is
IGroupSecurityService. Here's how it can be used to retrieve a user's email address:

// First we need to get our TFS and then find it's build store
TeamFoundationServer server = new TeamFoundationServer( serverUrl, CredentialCache.DefaultCredentials );


// Now get the correct interface
IGroupSecurityService gss = ( IGroupSecurityService ) server.GetService( typeof( IGroupSecurityService ) );


// Retrieve the identity
Identity objIdentity = gss.ReadIdentity( SearchFactor.AccountName, userName, QueryMembership.None );


// Get the email address
string email = objIdentity.MailAddres;

The userName is retrieved like so:

BuildStore store = ( BuildStore ) server.GetService( typeof( BuildStore ) );
// Get the build from our uri
BuildData bd = store.GetBuildDetails( buildUri );

// user name
string userName = buildData.RequestedBy;

Knowing this, it is a simple matter of writing a task to email the developer, passing serverUri, buildUri etc as optional parameters.

Simple.

Friday, 28 March 2008

Federation over a Duplex channel

Me again.

This time I wanted to do something a little beyond the norm, at least as far as the available examples are concerned. I would really like to implement a publish/subscriber service, where an application subscribes to various system wide events. Seems like a job for a Duplex channel - the client subscribes to service, and also provides an endpoint as a means to receive events.

The big catch here is that I'd love this service to execute under the same security context as the rest of my architecture, i.e. I'd like the service to be federated with my STS.

After trawling the net, I found several useful snippets, but nothing that solved the whole problem. Specifically, I wanted to achieve all this via config, if possible.

So here's how I did it...


The client binding looks like

<customBinding>
<binding name="DuplexBinding">
<security authenticationMode="SecureConversation">
<secureConversationBootstrap authenticationMode="IssuedToken">
<issuedTokenParameters tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<issuer address="http://localhost:8080/STS/Slave" binding="wsFederationHttpBinding" bindingConfiguration="UserNameFederationBinding">
<identity>
<dns value="My STS" />
</identity>
</issuer>
<issuerMetadata address="http://localhost:8080/STS/Slave/mex" />
</issuedTokenParameters>
</secureConversationBootstrap>
</security>
<compositeDuplex clientBaseAddress="http://localhost:8080/Client"/>
<oneWay/>
<textMessageEncoding />
<httpTransport />
</binding>
</customBinding>

Here, the original authentication mechanism was via a Username token. Notice the order of the elements within the tag - this foxed me for a good while before it clicked! Also note that the authentication mode is set to 'SecureConversation', with the bootstrap mode set to 'IssuedToken'. Initially I thought this seemed counter-intuitive, but when you consider the order in which WCF (or more correctly, WS-Security) performs its negotiation, this makes more sense.

In the service, the binding looks like

<customBinding>
<binding name="DuplexBinding">
<security authenticationMode="SecureConversation">
<secureConversationBootstrap authenticationMode="IssuedToken">
<issuedTokenParameters tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<issuer address="http://localhost:8080/STS/Slave" binding="wsHttpBinding" />
<issuerMetadata address="http://localhost:8080/STS/Slave/mex" />
</issuedTokenParameters>
</secureConversationBootstrap>
</security>
<compositeDuplex/>
<oneWay/>
<textMessageEncoding />
<httpTransport />
</binding>
</customBinding>

The service works in exactly the same way a standard Duplex service would, i.e. it simply retains a reference to the callback channel, like so

IRegisterCallback callback = OperationContext.Current.GetCallbackChannel();

in order to use this when necessary.

I hope this helps anybody else wishing to do the same, or similar, with their architecture.

Cheers

Graeme

Thursday, 21 February 2008

Token expiration dates

Here's another (brief) problem regarding the setting of validity dates within a token, or more specifically an RSTR (Issue), as implemented in WCF.

I was attempting to set validity date information within an RSTR. Firstly, I updated the dates within the SAML assertion itself, like so



DateTime validFrom = DateTime.UtcNow;

DateTime validTo = validFrom + TimeSpan.FromDays(1);
SamlAssertion assertion = new SamlAssertion();
assertion.Conditions = new SamlConditions(validFrom, validTo);

Avid readers (!) will note that the WS-Trust schema defines the following elements for specifying the valid date range of the accompanying token:

wst:RequestSecurityTokenResponse/wst:Lifetime


wst:RequestSecurityTokenResponse/wst:Lifetime/wsu:Created


wst:RequestSecurityTokenResponse/wst:Lifetime/wsu:Expires





where wst namespace is http://schemas.xmlsoap.org/ws/2005/02/trust

and wsu namespace is
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd


It is these dates that WCF will use to determine the lifetime of a token. The trick, when setting these dates in your RSTR, is to supply them in the correct format. If you don't you'll receive a very unhelpful error message within your client, the gist of which will be 'I don't understand your date formats, but I'm not going to tell you why'. After alot of messing, and finally resorting to code decomposition, I determined that the correct format is:

time.ToUniversalTime().ToString("o");
The RSTR is modified like so (within the OnWriteBodyContents method of the RequestSecurityTokenResponse class as per the WCF samples), for completeness:



// This part tells WCF what the lifetime of the token is, without having to parse the token itself
writer.WriteStartElement("Lifetime", TrustNS);
writer.WriteElementString("Created", UtilityNS, FormatDate(validFrom));
writer.WriteElementString("Expires", UtilityNS, FormatDate(validTo));
writer.WriteEndElement();



I guess that, with hindsight, it is reasonably obvious that the time should be UTC, but as they say, hindsight is a wonderful thing.


Hopefully, this will save somebody at least a small amount of time in future!


Fluffy

SAML token serialization

So, as promised, my first post is regarding simple serialization/de-serialization of a SAML token.





The driver for this exercise was to implement the 'Renew' verb of the WS-Trust specification, using the framework that WCF provides. To my endless frustration, the development team over at Microsoft stopped short of implementing the entire WS-Trust spec for anything but SCTs! Instead we're left with just 'Issue'.





My STS is signing tokens using self-issued X509 certificates (one for the STS and one for each client). Here's the (shortened) code for the serialization of the token:




///
/// Write the given token into an XmlWriter object
///

///
///
public virtual void WriteToken(SamlSecurityToken token, XmlWriter writer)
{
System.ServiceModel.Security.WSSecurityTokenSerializer serializer = new System.ServiceModel.Security.WSSecurityTokenSerializer();
serializer.WriteToken(writer, token);
}


Now, when I receive the token back from the client for renewal I need to de-serialize it in order to update its validity time span. Here's the code for this:



///
/// Read the token from an XmlDictionaryReader reader
///

///
///
public virtual SamlSecurityToken ReadToken(XmlDictionaryReader reader)
{
System.ServiceModel.Security.WSSecurityTokenSerializer serializer = new System.ServiceModel.Security.WSSecurityTokenSerializer();
System.Collections.Generic.List tokens = new System.Collections.Generic.List();
tokens.Add(SigningToken);
tokens.Add(UnencryptingToken);
System.IdentityModel.Selectors.SecurityTokenResolver resolver =
System.IdentityModel.Selectors.SecurityTokenResolver.CreateDefaultSecurityTokenResolver(tokens.AsReadOnly(), false);
SamlAssertion assertion = new SamlAssertion();
assertion.ReadXml(reader, new SamlSerializer(), serializer, resolver);
return new SamlSecurityToken(assertion);
}



Note that here the signing token is the certificate used by the STS to sign the assertion, and the unencrypting token is the private key equivalent of the certificate used to encrypt the proof key in the RSTR.

The point here is that in order to de-serialize the token, you're going to need the private key of the certificate used to encrypt the original. I guess this isn't a problem when you're in control of client certification, but I think you can see why this might cause issues.

Welcome!

Hi guys!

I've recently been doing a lot of work around Identity in the .NET framework, especially as it applies in 3.0 and 3.5. I've found that much of this stuff isn't covered in any great detail in the documentation. To get a problem solved I've simply had to 'suck it and see', if you'll pardon the expression. This has been frustrating, to say the least! I finally decided to blog about some of my exploits with SAML, WCF, WS-Trust, etc, in order that I might be able to help out a few people who come across the same issues!

I'll begin posting in the very near future - I'll start with a simple SAML serialization/de-serialization problem I encountered a few months ago. I also want to share my experiences of WS-Trust and how it applies (or doesn't!) to WCF, plus one or two salutory tales of developing an STS (Security Token Service) from scratch.

Cheers

Fluffy