UserName SupportingToken in WCF

There has been some mentioning of supporting tokens recently (here and here). Supporting tokens allow adding additional tokens to a SOAP security header – at least one token (usually the primary token) must be capable of providing the necessary key material to secure the message – the other tokens are just additional information. This is quite interesting when you have typical multi-hop architectures.

The following scenario is quite common. How can this be accomplished using WCF?

  • Three participants: a client (using a browser), a web application and a back-end WCF service.
  • The web application uses forms authentication (or some other scheme where you end up with a username) to authenticate clients.
  • The web application uses a client certificate to talk to the back-end WCF service.
  • The WCF service needs the identity of the (browser) client for authorization.

Now there are several hand-rolled ways to accomplish this. You could add the client name as an operation parameter – or better as a header – and could parse this information in the WCF service. But if you already bought into the WCF security model  and especially claims and claims based authorization – the supporting token approach integrates more nicely.

WCF will create claim sets for all incoming tokens. In our case the back-end WCF service would end up with two claim sets – one for the web app client certificate and one for the browser client. Perfect.

How does this work?

First you have to configure a binding for supporting tokens. You can either create a new binding – or modify an existing one. This works by creating a token parameter description (in our case a UserNameTokenParameter) and setting the inclusion mode (derived key can be disabled here).

There are some decisions to make when adding a support token:

Optional or required
Supporting tokens can be either optional or required. You express this by using the xxxSupportingTokenParameters or the OptionalxxxSupportingTokenParameters collection on the SecurityBindingElement.

Scope
You can either add the token for some specific operations or the complete endpoint. Again there is a collection for Endpoint and Operation supporting tokens.

Token Usage
You can specify whether this token is used to generate key material (Endorsing and SignedEndorsing) or just as extra information (Signed or SignedEncrypted).

Modifying an existing binding involves calling the GetBindingElements method, finding the right binding element to modify and afterwards creating a new binding using the modifications. The code looks like this:

static Binding AddUserNameSupportingTokenToBinding(Binding binding)
{
    BindingElementCollection elements = binding.CreateBindingElements();

    SecurityBindingElement security =
      elements.Find<SecurityBindingElement>();
    if (security != null)
    {
        UserNameSecurityTokenParameters tokenParameters = new
          UserNameSecurityTokenParameters();
        tokenParameters.InclusionMode =
          SecurityTokenInclusionMode.AlwaysToRecipient;
        tokenParameters.RequireDerivedKeys = false;
        security.EndpointSupportingTokenParameters.SignedEncrypted.Add(
          tokenParameters);

        return new CustomBinding(elements.ToArray());
    }

    throw new ArgumentException(
      “Binding contains no SecurityBindingElement”);
}

Of course you have to modify the binding both on the client and the service (see the sample download). You then set both credentials on the client side – the client certificate and the username part of the UserName credential.

Since this scenario is using a trusted subsystem approach, the back-end service trusts the web app that the client has already been properly authenticated. This means that you don’t have to send the client’s password to the WCF service – just leave it empty. This also means that you don’t have to “remember” the client’s password in the web app – which is desired.

To allow empty passwords on the service side you have to additionally provide a UserNamePasswordValidator which is happy with empty passwords. (again – the code is provided in the sample download).

When you got all this up and running – you have several places in your WCF service that are impacted by the additional token. First you get a new claim set – you will see an X509CertificateClaimSet for the direct caller’s client certificate and a UserNameClaimSet that contains the name of the original client. You can inspect the claim sets like this:

private void ShowAllClaims()
{
    foreach (ClaimSet set in ServiceSecurityContext.Current.AuthorizationContext.ClaimSets)
    {
        Heading(“IssuerClaims (“ + set.Issuer.GetType().Name + “)”);
        foreach (Claim claim in set.Issuer)
        {
            Console.WriteLine(“{0}n{1}n{2}n”,
               claim.ClaimType,
               claim.Resource,
               claim.Right);
        }

        Heading(“IssuedClaims (“ + set.GetType().Name + “)”);
        foreach (Claim claim in set)
        {
            Console.WriteLine(“{0}n{1}n{2}n”,
               claim.ClaimType,
               claim.Resource,
               claim.Right);
        }
    }
}

You can also gain direct access to supporting tokens via the OperationContext:

if (OperationContext.Current.HasSupportingTokens)
{
    Heading(“Supporting tokens:”);
    foreach (SupportingTokenSpecification spec in OperationContext.Current.SupportingTokens)
    {
        Console.WriteLine(spec.SecurityToken.GetType().Name);
    }
}

Cool. Mission accomplished, right? Well – there is one gotcha.

 

The ServiceSecurityContext.PrimaryIdentity typically returns an IIdentity for the caller of the service. I was surprised to see that when you are adding a supporting token to a message the primary identity suddenly changes to Anonymous. Inspecting the code with Reflector shows that the logic of PrimaryIdentity is like this:

  • if there is a single identity, return this identity
  • if there is no identity or more than one, return anonymous.

Maybe I am misunderstanding the concept of primary identity – but I thought the primary identity should still point to my direct caller whereas the supporting token is just some secondary identity information. So the current behavior feels wrong to me.

If you are relying on PrimaryIdentity somewhere in your code – be aware that adding a supporting token will break that code.

 

Workarounds
Primarily, I guess you have to be aware of that behavior. If you need the direct caller on PrimaryIdentity you could write an IAuthorizationPolicy that sets a single identity on the Properties dictionary at the end of the evalution cycle. Haven’t tested that – but should work.

I chose to write two extension methods for ServiceSecurityContext that give me more options when working with identities. If you are on 3.5 you can add these to your toolbox.

The first one returns all available identites:

public static IList<IIdentity> GetIdentities(this ServiceSecurityContext context)
{
    return GetIdentities(context.AuthorizationContext);
}

private static IList<IIdentity> GetIdentities(AuthorizationContext authorizationContext)
{
    object list;
    if ((authorizationContext != null) && authorizationContext.Properties.TryGetValue(“Identities”, out list))
    {
        return (list as IList<IIdentity>);
    }

    return new List<IIdentity>();
}

 

The second one returns the first identity (or anonymous if no identity exists):

public static IIdentity GetFirstIdentity(this ServiceSecurityContext context)
{
    IList<IIdentity> identities = GetIdentities(context.AuthorizationContext);
    if (identities.Count > 0)
    {
        return identities[0];
    }
    else
    {
        return new GenericIdentity(string.Empty);
    }
}

 

SupportingUserNameToken.zip (42.56 KB)

HTH

This entry was posted in ASP.NET, WCF. Bookmark the permalink.

5 Responses to UserName SupportingToken in WCF

  1. smitha says:

    Hi, the link for supportingusernametoken.zip is broken. I need this functionality

  2. smitha says:

    Thank you .. so much.. I have a soap xml request which need username token with nonce, 2 binary securitytokens(supporting tokens).. and body of the soap encrypted, ciphered. I am doing this is using C#, wcf. Hopefully this usernametoken sample can provide me some insight..Please let me know if you have some info to accomplish this.

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 )

Facebook photo

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

Connecting to %s