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. ASP.NET | CardSpace | IdentityModel
3/30/2008 8:49:07 AM UTC
|