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.