This topic comes up quite often recently – so I hope the title is search engine friendly.
Disclaimer: At the time of this writing, the current version of Silverlight is v3 and WIF is in beta 2. Hopefully this will be a non-issue soon.
I talk a lot about claims, tokens and WIF/ADFS 2 to customers. All is good and fine and they like it. Another technology that comes up very often is Silverlight – and especially the “story” of Silverlight and WCF/WIF. Now this is not an easy question to answer.
When thinking about Silverlight and back-end security in general – there are two fundamental scenarios – short of really good names – I call them “passive” and “active”.
Passive
With passive I mean, that the browser has already established a security context with the back-end system the Silverlight .XAP uses (and typically is also hosted at). An example would be that the user first authenticates with the web application and then starts the Silverlight app from there. In this case all the authentication related work was done by browser beforehand and all back-end requests simply re-transmit authentication headers, cookies and the like.
This is not different with WIF secured ASP.NET applications. Eugenio has a good example of this here. This approach works fine while running in the browser and using “application-local” resources only.
Active
This is the scenario I really want to talk about. In this case the .XAP comes from some server down to the client (in or out-of-browser) and wants to communicate with some back-end service on a different machine. This back-end service requires an issued token from a STS.
Those of you with WCF background will say: “that’s what the federation bindings in WCF are for”. But there is no federation binding in Silverlight.
If you need to enable such a scenario, you have to use several workarounds.
Requesting a Token
Since Silverlight has no support for WS-Trust, we must find another way to request a token from a STS. Thanks to WIF this is not hard to do when you control the token service. You could add a simple REST or SOAP head that returns tokens. I wrote about this approach here.
Some remarks here:
- Since you cannot do the proof of possession crypto in Silverlight, you need to request bearer tokens from the STS.
- The STS roundtrip is a cross-domain request. This means that the STS needs a client access policy (see here).
- You have to somehow authenticate with the STS. Silverlight has no support for Kerberos or client certificates. Basically you are stuck with some kind of userid/secret credential. I wrote about that here.
Sending the token to the Service
This is the slightly more complicated part. Since Silverlight has no support for issued tokens, you have to manually embed the token in the outgoing SOAP security header. This is not as scary as it sounds.
There is the so called Basic Security Profile in WS-Security which allows sending a simple security header with a timestamp and a token over SSL. This can be encapsulated in a MessageHeader derived class.
public class SecurityMessageHeader : MessageHeader
{
string _token;
string _nsUtility = “…”;
public SecurityMessageHeader(string token)
{
_token = token;
}
protected override void OnWriteHeaderContents(
XmlDictionaryWriter writer, MessageVersion messageVersion)
{
DateTime now = DateTime.UtcNow;
string created = XmlConvert.ToString(
now, “yyyy-MM-ddTHH:mm:ss.fffZ”);
string expires = XmlConvert.ToString(
now.AddMinutes(5), “yyyy-MM-ddTHH:mm:ss.fffZ”);
writer.WriteStartElement(“Timestamp”, _nsUtility);
writer.WriteAttributeString(“Id”, _nsUtility, “_0”);
writer.WriteElementString(“Created”, _nsUtility, created);
writer.WriteElementString(“Expires”, _nsUtility, expires);
writer.WriteEndElement();
writer.WriteNode(XmlReader.Create(
new StringReader(_token)), false);
}
public override string Name
{
get { return “Security”; }
}
public override string Namespace
{
get { return “…”; }
}
}
Afterwards you can attach the header to outgoing calls:
var factory = new ChannelFactory<ServiceContract>(“client”);
var proxy = factory.CreateChannel();
using (var scope = new OperationContextScope(proxy as IContextChannel))
{
OperationContext.Current.OutgoingMessageHeaders.Add(
new SecurityMessageHeader(token));
proxy.BeginOperation(result =>
{
…
}, null);
}
The binding on the client side is a binding with no client credential but SSL (the binary encoder is not required and used only for performance):
<customBinding>
<binding name=“BearerTokensOverTransport“>
<binaryMessageEncoding />
<httpsTransport />
</binding>
</customBinding>
On the service side, you need a binding that accepts bearer tokens over transport security, like this:
<customBinding>
<binding name=“simple“>
<security authenticationMode=“IssuedTokenOverTransport“
messageSecurityVersion=“WSSecurity11
WSTrust13
WSSecureConversation13
WSSecurityPolicy12
BasicSecurityProfile10“>
<issuedTokenParameters keyType=“BearerKey“ />
</security>
<binaryMessageEncoding />
<httpsTransport />
</binding>
</customBinding>
The rest works as normal. You have to enable WIF in the service and you get your IClaimsPrincipal.
Some remarks:
- Again this service needs a client access policy for Silverlight cross domain calls.
- Keep in mind that these are bearer tokens. When you have a scenario that requires proof of possession, this will not work
- The SOAP response from the service will also contain a security header. Since the client is not configured for security this header may be unexpected. Silverlight does not seem to care, but other web service stacks might. In WCF you can work around that problem by adding the ValidateMustUnderstand behavior to the client stack.
The complete code (STS extensions, Silverlight client and back-end service) is included in the next drop of the StarterSTS (version 0.95). I will upload that soon.
HTH
Hi, is this still how you would do active auth using silverlight 5 adsf2, or is there a better method today?
Don’t know. I wouldn’t use silverlight5… Sorry.