Using Information Cards in ASMX Web Services

As I wrote here – an Information Card token is just a string. This means that (with the help of some extra plumbing) you can seamlessly integrate cards into “legacy” technologies. Here’s a sample walkthrough for ASMX web services.

To transmit the token to the service, I will use a SOAP header. So the first step is to define the header:

[XmlRoot(ElementName = “InformationCard”,
    Namespace = http://schemas.xmlsoap.org/ws/2005/05/identity”)]
public class InfoCardTokenHeader : SoapHeader
{
    public string Token;
}

For metadata support, we can now annotate a web method with this header information:

[WebService(Namespace = “urn:leastprivilege”)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AsmxService : WebService
{
    public InfoCardTokenHeader InfoCardToken;

    [WebMethod]
    [SoapHeader(“InfoCardToken”, Direction = SoapHeaderDirection.In)]
    public string Ping()
    {
      
    }
}

The client can now use the CardSpaceSelector API (or my wrapper) to get a token manually. Afterwards the token gets transmitted using the header:

static void Main(string[] args)
{
    AsmxService proxy = new AsmxService();

    InfoCardTokenHeader token = new InfoCardTokenHeader();
    token.Token = GetInfoCardToken(proxy.Url);
    proxy.InformationCard = token;

    Console.WriteLine(proxy.Ping());
}

static string GetInfoCardToken(string targetUri)
{
    IdentitySelector selector = new IdentitySelector();
    selector.TargetUri = new Uri(targetUri);
    selector.SetTargetCertificate(targetUri);

    selector.RequiredClaims.Add(ClaimTypes.GivenName);
    selector.RequiredClaims.Add(ClaimTypes.Surname);
    selector.RequiredClaims.Add(ClaimTypes.Email);

    GenericXmlSecurityToken token = selector.GetToken();
    return token.TokenXml.OuterXml;
}

On the server side you could now retrieve the token from the header and use your favourite token decryption class to extract the claims. If you want to put in a little bit more work, you can improve the integration of that information using a SoapExtension.

The extension will check the incoming headers, extract the token and set Thread.CurrentPrincipal and Context.User to an instance of IdentityPrincipal that wraps the token generated authorization context. A corresponding extension attribute connects this logic with the web method:

[WebMethod]
[InfoCardSoapExtension(TokenRequired = true)]   
[SoapHeader(“InfoCardToken”, Direction = SoapHeaderDirection.In)]
public string Ping()
{
    return IdentityPrincipal.Current.ClaimSets.FindClaim(
        ClaimTypes.GivenName).Get<string>();
}

This gives the web service method seamless access to incoming claims.

 

The code for the SOAP extension is quite simple (the configuration code is omitted):

public class InfoCardSoapExtension : SoapExtension
{
  public override void ProcessMessage(SoapMessage message)
  {
    if (message.Stage == SoapMessageStage.AfterDeserialize)
    {
      foreach (SoapHeader header in message.Headers)
      {
        InfoCardTokenHeader tokenHeader =
          header as InfoCardTokenHeader;

        if (tokenHeader != null)
        {
 
        IdentityPrincipal principal;

          try
          {
            var token = new Token(tokenHeader.Token, true);
            principal = new IdentityPrincipal(token.AuthorizationContext);
          }
          catch
          {
            throw new HttpException(500, “Token validation failed”);
          }

          HttpContext.Current.User = Thread.CurrentPrincipal = principal;
          return;
        }
      }

      if (_tokenRequired)
      {
        throw new HttpException(401, “Authentication required”);
      }
    }
  }
}

Disclaimer: I know that this code could be written far more generic. Consider this as a proof of concept.

 

The LeastPrivilege.IdentityModel download contains the complete sample. Have fun.

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

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