Windows Live Development Part 1 - Authenticating users via Windows Live ID

June 20, 2008 00:01 by MartinHN

With so many online services out there, you nearly claim a new online identity somewhere every day. You tend to use the same username and password couple for all of them, but sometimes your preferred username is taken, or your password does not meet the sites' password policy. I reckon I have well over 100 online identities out there - I rely on the same 3-4 combinations of username and password, and it's really messy. In this blog post I'm going to cover how to authenticate users of an ASP.NET application against Windows Live, using the SDK for Windows Live ID. I'm going use the built-in ASP.NET Membership mechanism and go from there.

I could have gone the OpenID way, but Scott Hanselman already did an example on OpenID, so I thought I'd try out Windows Live. Jeff Atwood asked the question, if we really need another username and password in the pursuit of the best authentication model for stackoverflow.com.

Conceptual idea of using Windows Live ID

tmpF3A8Windows Live ID, is the technology you should be quite familiar with when authenticating on Microsoft web sites, and Messenger. If you want to know more about what Windows Live ID is, take a look at this video from Channel9.

When using Windows Live ID, you redirect the user to the Windows Live login-page, with a return URL specified. When the user authenticates, Windows Live will redirect the user back to your site, and delivering a user id and a token. Here's an overview of how it looks:

image

Getting your application ID to use Windows Live ID

To get started using Windows Live ID, first of all you need an application ID. Basically you need to sign up for one, and register your application. Go to https://msm.live.com/app/default.aspx and click 'Register an Application'. (You can find Microsoft's guide to this here: http://msdn.microsoft.com/en-us/library/cc287659.aspx)

tmpF3F5

Leave Domain Name blank. Save your self-chosen Secret Key for later use, and submit the form. When you see the confirmation page, your application ID is shown. Copy that, as we're going to use it later.

In the return URL field, type the URL of your application followed by a name of a page to handle the Windows Live ID communication, like http://localhost/demoapp/webauth-handler.aspx.

Download and install Windows Live ID Web Authentication SDK

Microsoft has made it quite easy for ASP.NET developers to get started, by providing an SDK. Download it here: http://www.microsoft.com/downloads/details.aspx?FamilyId=24195B4E-6335-4844-A71D-7D395D20E67B&displaylang=en and install the SDK. By default it is installed here: C:\Program Files\Windows Live ID\WebAuth

From the SDK we need a class called WindowsLiveLogin, located in the App_Code folder of the sample. Keep this file in mind for a few seconds.

Start coding - create a new website

Open Visual Studio 2005/2008 and create a new ASP.NET website the way you want it. First we will add the WindowsLiveLogin.cs file to our App_Code folder. (C:\Program Files\Windows Live ID\WebAuth\Sample\App_Code\WindowsLiveLogin.cs).

Leave the default.aspx page, and put the following HTML in the markup:

<html>
<head>
  <title>Windows Live&trade; ID</title>
</head>
<body>
  <iframe id="WebAuthControl" name="WebAuthControl" src="http://login.live.com/controls/WebAuth.htm?appid=<%=AppId%>&style=font-size%3A+10pt%3B+font-family%3A+verdana%3B+background%3A+white%3B"
    width="80px" height="20px" marginwidth="0" marginheight="0" align="middle" frameborder="0"
    scrolling="no"></iframe>
  <%
   1:  if (UserId == null)
   2:      { 
%> <p> This application does not know who you are! Click the <b>Sign in</b> link above.</p> <%
   1:  }
   2:      else
   3:      { 
%> <p> Now this application knows that you are the user with ID = "<b><%
   1: =UserId
%></b>".</p> <%
   1:  } 
%> </body> </html>

And this piece of code in its code-behind class.

using System;
using System.Web;
using System.IO;
using WindowsLive;

public partial class DefaultPage : System.Web.UI.Page
{
  const string LoginCookie = "webauthtoken";

  // Initialize the WindowsLiveLogin class.
  private static WindowsLiveLogin wll = new WindowsLiveLogin(true);
  
  protected static string AppId = wll.AppId;
  protected string UserId;

  protected void Page_Load(object sender, EventArgs e)
  {
    HttpRequest req = HttpContext.Current.Request;
    HttpApplicationState app = HttpContext.Current.Application;

    HttpCookie loginCookie = req.Cookies[LoginCookie];

    if (loginCookie != null)
    {
      string token = loginCookie.Value;

      WindowsLiveLogin.User user = wll.ProcessToken(token);

      if (user != null)
      {
        UserId = user.Id;
      }
    }
  }
}

Default.aspx will work as a login page. Users click the Sign-in button, and is redirected to Windows Live. When they authenticate, they're redirected back to our handler page, that we will now create...

Create a new aspx page, and name it webauth-handler.aspx. Recall that this was the page we provided as the return URL when we registered the application at Windows Live.

This page will serve as the handler page for the user, when Windows Live redirects back to your site. Windows Live will post authentication specific values for you to use. Because this page is never seen by a user (we will send the user back where they began), we can simply remove all the markup.

Put the following code in your code-behind class for webauth-handler.aspx.

using System;
using System.Web;
using System.IO;
using WindowsLive;

public partial class HandlerPage : System.Web.UI.Page
{
  // Relative path to the login- and logoutpage.
  const string LoginPage = "default.aspx";
  const string LogoutPage = "default.aspx";
  const string LoginCookie = "webauthtoken";
  static DateTime ExpireCookie = DateTime.Now.AddYears(-10);
  static DateTime PersistCookie = DateTime.Now.AddYears(10);

  // Initialize the WindowsLiveLogin class.
  static WindowsLiveLogin wll = new WindowsLiveLogin(true);

  protected void Page_Load(object sender, EventArgs e)
  {
    HttpRequest request = HttpContext.Current.Request;
    HttpResponse response = HttpContext.Current.Response;
    HttpApplicationState application = HttpContext.Current.Application;

    string action = request["action"];

    if (action == "logout")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);
      loginCookie.Expires = ExpireCookie;
      response.Cookies.Add(loginCookie);
      response.Redirect(LogoutPage);
      response.End();
    }
    else if (action == "clearcookie")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);
      loginCookie.Expires = ExpireCookie;
      response.Cookies.Add(loginCookie);

      string type;
      byte[] content;
      wll.GetClearCookieResponse(out type, out content);
      response.ContentType = type;
      response.OutputStream.Write(content, 0, content.Length);

      response.End();
    }
    else if (action == "login")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);

      WindowsLiveLogin.User user = wll.ProcessLogin(request.Form);

      if (user != null)
      {
        loginCookie.Value = user.Token;

        if (user.UsePersistentCookie)
        {
          loginCookie.Expires = PersistCookie;
        }
      }
      else
      {
        loginCookie.Expires = ExpireCookie;
      }

      response.Cookies.Add(loginCookie);
      response.Redirect(LoginPage);
      response.End();
    }
    else
    {
      response.Redirect(LoginPage);
      response.End();
    }
  }
}

The first login test

Now we've got everything setup as we should, and it should be possible to login using Windows Live. View the default.aspx page in your favorite browser, and let's try.

First you will see out login page:
tmp9D6

Click the Sign in link, which will take you to Windows Live:
wlive_signin

Sign in with your Windows Live ID, and you will return to our login page - which will now welcome you and show your user ID:
wlive_signin 

Now your user has authenticated against Windows Live, using the Windows Live ID. Now it's up to you to handle the user's Windows Live ID, which is what I'm going to blog about in part 2 of the Windows Live Development series here...


kick it on DotNetKicks.com

Currently rated 4.3 by 3 people

  • Currently 4.333333/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Designing for Internationalization

June 10, 2008 21:17 by MartinHN

Web sites and Web Applications today, are very often exposed beyond the borders of your home country, and therefore users speak different languages, and has a different currency, date- and time formats etc. ASP.NET provides you with an entire namespace for handling things like this. That is the System.Globalization namespace, where you will find a lot of classes for handling your every day globalization tasks. I'm not going to cover anything in this namespace now, if you want to get your hands dirty take a look at this video: http://asp.net/learn/videos/video-40.aspx where you will see how to use local and global resources for your application.

Using a global resource file for a place to store display text on buttons, labels, validation controls etc. is fine. But if you have an e-commerce site, selling products in multiple regions with different languages, you need an extra level. That level is a way to globalize e.g. the name and description of your product. When a user changes language, the name and description of your "display product details page" should change accordingly.

For me, the ideal solution should not result in extra database columns like name_us, name_da, name_es. This would be a very static solution, as you would have to change your database whenever a new language is added to your application. Nor should it require extra tables, so you need to join like hell, when you need to select a product.

I've decided to store e.g. the Name values as XML in the database, and parse that XML into a Dictionary<string, string> property on my Product object, with the key of the Dictionary being the language code (en-US for US English). To me this seems to work just fine. My database design is not getting more complex, and I can get and set values quite easily.

The XML string that goes into my ProductName column in the database table, looks like this:

<cultures><culture code="en-us">Logitech SmartCam 124</culture></cultures>

On my Product object, the Name property is a Dictionary:

private Dictionary<string, string> _Name;

public Dictionary<string, string> Name
{
  get { return _Name; }
  set
  {
    if (_Name != value) MarkDirty("Name");
    _Name = value;
  }
}

When I need to get the US English value of the Product.Name property, I call:

p.Name["en-us"]

To get a Dictionary from my XML, I use this helper method:

public static Dictionary<string, string> GetDictionary(string xml)
{
  if (String.IsNullOrEmpty(xml))
    return new Dictionary<string, string>(owner);
    
  XmlDocument doc = new XmlDocument();
  doc.LoadXml(xml);
    
  Dictionary<string, string> dic = new Dictionary<string, string>();

  foreach (XmlNode node in doc.DocumentElement)
  {
    dic.Add(node.Attributes["code"].Value, node.InnerText);
  }

  return dic;
}

And when I want to update my database, after I change the Name of the Product, I convert to Dictionary to an XML string using this helper method:

public static string GetXmlDocument(Dictionary<string, string> dic)
{
  XmlDocument doc = new XmlDocument();
  XmlNode docElement = doc.CreateNode(XmlNodeType.Element, "cultures", "");

  foreach (string key in dic.Keys)
  {
    XmlNode node = doc.CreateNode(XmlNodeType.Element, "culture", "");
    XmlAttribute att = doc.CreateAttribute("code");
    att.Value = key;
    node.Attributes.Append(att);

    node.InnerText = dic[key];

    docElement.AppendChild(node);
  }

  doc.AppendChild(docElement);

  return doc.OuterXml;
}

If you need more information on this huge topic, take a look at the ASP.NET Wiki: http://wiki.asp.net/page.aspx/55/internationalization/


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Powered by BlogEngine.NET 1.4.0.0
Theme by Mads Kristensen

About the author

Martin Høst Normark

Senior Frontend Developer at TraceWorks.

View Martin Høst Normark's profile on LinkedIn

Recent comments

Comment RSS

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

© Copyright 2008