I have been experimenting with Information Cards with the intent of plugging them into the Community Server blog commenting system. If you have not immersed yourself in the world of information cards here is a quick rundown:
- Why information cards?
- Identity on the Internet today is what you say about you. In other words, I can say that I am Chuck Norris and no one would be the wiser.
- Identity in the information card world adds an additional aspect - what others say about you. No government, bank, or other widely trusted source will backup my claim that I am Chuck Norris.
- You can rely on someone else saying something about you, which is known as a Managed Cards, or you can also create a Personal Card which is really as good as what we have today as it is based on what you say about you. The difference is that there is a bit a token at play which can enhance password security (something you know + something you have).
- Information cards put you in control of how you want to be represented. You can choose to use the one that tells people that you are a Boston Red Socks fan, or use one that says you like the New York Yankees.
- Privacy is preserved because the entity backing your claim of identity up never knows where you use that identity. In other words, you can use your identity on those "late night sites" and not have to worry about the government knowing where you have used the digital identity they issued you.
- What do I need in order to use information cards?
- Windows CardSpace is the identity selector for Windows XP/Vista with the .NET Framework 3.0 and Internet Explorer 7.0
- There are other selectors shipping today including ones for Firefox (also available in a Windows-specific version that leverages the CardSpace UI), Safari, and more.
- Why have not I seen them widely used?
- I think the industry is still getting their stuff together on this. It is early days but it is looking promising.
- Who will play the role of identity provider?
- My guess is banks, government, credit card companies, and large web portals (a la MSN, Google, Yahoo, etc...).
- Where can I get a managed card today?
The first thing we will need is an SSL certificate. You might wonder why and I point you to the world of phishing. It is easy to get a random domain and make it look real-ish (e.g. www1-ebay.com). SSL certificates add another layer of validation. Now you can go get a cheap certificate and still look real-ish, but it adds another level of trace-ability. in addition there is a movement to try and get the big players to use Extended Validation certificates. They cost a whole lot more because there is a real person doing some checking to ensure you are real. For most of us regular folk we will be happy with the $30 SSL certificate from the likes of Trustico and others. For the purposes of development, however, we can use a self-signed certificate.
With SSL certificate in hand we need to make a few decisions about the criteria of the identity which we are requesting from the user. There are five major decisions to make:
- Should I accept a Personal Card, Managed Card, or both?
- Personal cards are self-signed, and thus should be used for casual login scenarios where you do not need someone to vouch for the user. Before you say "heck no!" remember that most sites today will allow you to create a Chuck Norris account (unless, of course, someone else beat you to it) without any proof that you really are Chuck Norris. How are personal cards any different? Well, they do offer one extra advantage - they include a token that is stored on the user's computer that prevents me from creating a card on another computer using the same information. If you want the user to provide only personal cards then set the issuer to http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self
- Managed cards are verified by someone else. You have the added benefit of saying I will believe the user if they can find someone else trustworthy to corroborate their identity. Right now there are not many people offering this, but we could see our first big production usage this summer in the form of Windows Live ID according to Kim Cameron. Following that at some point the Internet at large will start to cut over to Internet2 where we will see Information Cards take on more of a role as well.
- What token format should I accept?
- Well, this is where Microsoft and others lack guidance. The interesting bit is that you can use any token format as long as you know there is an identity provider out there that will issue tokens in that format. The identity selector (e.g. Windows CardSpace), does not care what is in the package, just as long as the parties at both ends agree. All of the examples seem to reference the SAML assertion token type "urn:oasis:names:tc:SAML:1.0:assertion", so I am sticking with that until someone develops a list of what is out there or we see one become dominant.
- If you are using personal cards then the token type should be SAML 1.1 identified by "urn:oasis:names:tc:SAML:1.0:assertion" or "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"
- Which claims will I need?
- A claim is the piece of information that makes up the identity. The required claims list outlines what you need the identity provider to verify for the user. There is a base set of claims that need to be supported which are outlined at http://schemas.xmlsoap.org/ws/2005/05/identity/claims, and beyond that you are free to create additional claims. The key, once again, is that both the identity provider and relying party agree on the claims. Examples of claims include first name, last name, email address, phone number, and web page.
- What claims would be nice to know?
- Following on from required claims you can also request optional information. It follows all of the points noted above and appears in a separate field to tell the Identity Selector that it does not need to provide these if it is unable to do so.
- Do I have a privacy policy?
- To get back to the human element there is provision for the cards to point to a privacy policy. This will show up in the information selector as a link for the user to read. I have yet to see my mom read a privacy policy but the lawyers sleep better at night knowing that it is there.
With the major decisions in place we now need to tell the browser that we want to take an information card. This is done through one of two ways, the OBJECT tag, or an XHTML binary behavior. I am not sure where binary behaviors came from as the OBJECT tag does not cause XHTML validation to kick and scream. A quick search with my favorite search engine does not reveal anything either. For the sake of this post we will use the OBJECT tag. I can put this tag in one of two places - the HEAD tag content or the FORM tag content. In the HEAD tag I can optionally call upon it in my form using some ECMAScript, where as when it is part of the FORM tag will cause the information selector to trigger when the user clicks a submit button. Below I have opted to use ECMAScript:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"
ValidateRequest="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Windows Cardspace Relying Party Demo</title>
<object id="InformationCard" name="InformationCard" type="application/x-informationCard">
<param name="tokenType" value="urn:oasis:names:tc:SAML:1.0:assertion" />
<param name="issuer" value="http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self" />
<param name="requiredClaims" value="
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" />
<param name="optionalClaims" value="
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage" />
</object>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:HiddenField ID="EncryptedToken" runat="server" Value="empty" />
<asp:CustomValidator ID="EncryptedTokenRequired" runat="server"
ErrorMessage="There is no data in the token field to decode."
OnServerValidate="EncryptedTokenRequired_ServerValidate" Display="dynamic" />
<asp:LinkButton ID="Submit" runat="server" Text="Submit your Information Card"
OnClick="Submit_Click" />
</div>
<asp:Literal ID="InformationCardValues" runat="server" />
<script type="text/ecmascript">
//<![CDATA[
function SelectInformationCard(tokenControlId)
{
var informationCard = document.getElementById("InformationCard");
var tokenControl = document.getElementById(tokenControlId);
tokenControl.value = informationCard.value;
if((tokenControl.value == 'empty') || (tokenControl.value == 'undefined'))
{
return false;
}
else
{
return true;
}
}
//]]>
</script>
</form>
</body>
</html>
In the back-end code I need to decode the tag, read in the claims, and validate them. You do remember that you are not supposed to trust input from anyone, right? :) There is one piece of code that is only included in the attachment which decodes the XML blob. It is pretty rigid stuff meant for demo purposes. In a production application you would need to make some decisions around what formats to accept, and how to protect the SSL private key.
#region References
using Microsoft.IdentityModel.Tokens;
using System;
using System.Data;
using System.Configuration;
using System.Globalization;
using System.IdentityModel.Claims;
using System.IO;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
#endregion
public partial class _Default : System.Web.UI.Page
{
#region Properties
/// <summary>
/// Determines whether the request is secure.
/// </summary>
/// <returns>
/// <c>true</c> if the request is secure; otherwise, <c>false</c>.
/// </returns>
private bool IsSecureRequest
{
get
{
return ((String.Compare(Request.ServerVariables["HTTPS"], "on", true, CultureInfo.InvariantCulture) == 0) |
(Request.IsSecureConnection));
}
}
#endregion
#region Methods
/// <summary>
/// Handles the Load event of the Page control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsSecureRequest)
{
UriBuilder redirectUrl = new UriBuilder(Uri.UriSchemeHttps, Request.Url.Host);
redirectUrl.Path = Request.Url.AbsolutePath;
redirectUrl.Query = Request.Url.Query;
Response.Redirect(redirectUrl.ToString(), true);
}
Submit.OnClientClick = "return SelectInformationCard('" + EncryptedToken.ClientID + "') };";
}
/// <summary>
/// Handles the ServerValidate event of the EncryptedTokenRequired control.
/// </summary>
/// <param name="source">The source of the event.</param>
/// <param name="args">The <see cref="System.Web.UI.WebControls.ServerValidateEventArgs"/> instance containing the event data.</param>
protected void EncryptedTokenRequired_ServerValidate(object source, ServerValidateEventArgs args)
{
// We could also try to decipher the token here to ensure that it is properly formatted.
args.IsValid = (string.IsNullOrEmpty(args.Value));
}
/// <summary>
/// Handles the Click event of the Submit control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected void Submit_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
InformationCardToken token = new InformationCardToken(EncryptedToken.Value);
using (StringWriter stringWriter = new StringWriter())
{
XhtmlTextWriter writer = new XhtmlTextWriter(stringWriter);
writer.WriteFullBeginTag("dl");
foreach (Claim claim in token.IdentityClaims)
{
writer.WriteFullBeginTag("dt");
writer.WriteEncodedText(claim.ClaimType);
writer.WriteEndTag("dt");
writer.WriteFullBeginTag("dd");
writer.WriteEncodedText(InformationCardToken.GetResourceValue(claim));
writer.WriteEndTag("dd");
}
writer.WriteEndTag("dl");
InformationCardValues.Text = stringWriter.ToString();
}
}
}
#endregion
}
Compile, run, and there you have it - you are up and running with a few lines of code. Now before you run off and implement this in production there is a matter of securing the decryption process since it uses your SSL private key. Dominick Baier has put together a nifty WCF service that will decode the token out-of-process which reduces the attack surface for the SSL private key. I highly recommend it because it is a much safer way to decode the token. In addition a few other folks have put together ASP.NET server controls to help you get up and running even faster:
It seems like there are many identity frameworks spawning. Hopefully the consortium of projects will tackle the interoperability issue before it gets silly. The last thing we need is another AJAX framework world where I lost count after 20 frameworks. If you are interested in this technology I suggest you check out a few other quintessential resources: