Welcome to NetFxFactory Sign in | Join | Help

Papers

Let your community site leverage Windows Live

1. Why

Most web sites require managing users from personal details to authentication. Event though, ASPNet provides some tools ease this process with a Sql Server backend database. This can be tedious to manage and implies a responsibility to keep personal data safe furthermore people put some time and effort into filling in their Windows Live Id profile information. Of course some web sites make their business on having a huge user database. However community web sites such as netfxfactory does not gain any added value from managing its own user database. Various blogs and papers on the web explain how to leverage Windows live authentication mechanism to validate a local user (see here here and here). They mostly let someone associate its web site account with his Windows Live Id however it’s name, dob, email etc… are still managed localy by the web site (this is the functionalities offered by the Memebership provider offered with the Windows Live SDK). Only the authentication mechanism is leveraged here.

The goal of this article is to go one step further and externalize as much as possible the user management. Managing locally user’s data requires an extra effort from the user to keep his information up to date. We will demonstrate a solution using Windows Live based on a custom aspnet membership provider used to pull out information from Windows Live such as User info but also his contacts info.

This is a first step towards S+S as promoted by Microsoft.

2. Solution

a. Register live application

In order to be able to take advantage of Windows Live API one needs to register his application. This registration process requires several pieces of information such as a return Url that will be called after a successful sign in along with a set of parameters allowing to complete the authentication process on our site. The form bellow shows the information to supply:

WindowsLiveApplicationID

Note: In a dev environment, you can add a static DNS resolution on your dev machine in order to resolve the domain name to 127.0.0.1 Edit your host file (c:\windows\system32\drivers\etc\host)
Note: It seems that Windows Live does not let you register domain name that does not contains “dots” in it.

Upon completion of this form, you are being attributed an ApplicationID that your application have to provide along with the secret key.

b. ASP.Net related

Acknowledgement

The membership provider mechanism offered by ASPNet offers a large functional perimeter allowing managing for instance password expiration, reset etc… Because we intend to rely as much as possible on Windows  Live there is no need to implement such functionality on our web site.

Custom Windows Live Membership provider

Based on the acknowledgement detailed above, the main method of AspNet Membership provider that we are interested in is ValidateUser but instead of validating a username password as we usually do, we will only have to ensure that the current user has signed in properly on Windows Live (therefore owns a valid authentication and delegation token).

Because Windows Live authentication only returns the id of the logged in user and extra step is required to retrieve personal information about the user. This step is done in the overridden GetUser() method. A REST request is sent to retrieve information details about the user such as his/her name, email(s) and so on. this can then easily accessed throughout the site using Membership.GetUser() method.

Custom Windows Live Membership user

In order to facilitate the use of MembershipUser, we provide a specialized one that exposes the user’s Windows live information. Here the choice has been made to not include the complete address book but just the user’s details. A quick look at the Windows Live Api Rest Provider described bellow would do the trick.

Custom Windows Live Contact Api Rest Provider

This class is responsible for exposing methods that wraps calls made to the Windows Live services exposed as REST. Its main task is to build the requests to send from Windows Live Services (url constructs, token and authentication assignment as well as deserialization)

c. Windows live

Delegated authentication

Windows Live delegated authentication is mechanism allowing taking advantage of Windows Live ID authentication to prove to Windows Live services (Contact, Photos, Application Storage and so on) that a trusted relationship exists between the user and a your web site. it suits perfectly the bill of our requirements, i.e. handle locally user’s data as less as possible as stated here: “Sign-in and consent management functions are performed by the Windows Live ID service, so you don't have to worry about implementing these details. Another advantage to Delegated Authentication is that Windows Live ID profile data is not shared with your Web site. As a result, you can focus more on the functionality of your application, and less on managing user data”.

The corner stone of delegation authentication is that users own their own data. They can decide which sites get access to that data and what those sites can do on the user's behalf with that data.

Consent management

Windows Live let users decide what information they want to share with a web site leveraging Windows Live authentication. Anyone with a Windows Live account can manage his consents at the following address: https://consent.live.com. This page sums up all the sites you have given access to as well as the access mode (read, update, write …). You can revoke access of any of these sites at any time before your consent expires.

Here is what the consent management page looks like:

WindowsLive_Consent

The Windows Live Contact consent can even be done on a per contact basis. Unfortunately, there is no way to select/unselect  a group of contact indeed it can become tedious to select/unselect individual contacts when you address book contains 30+ contacts.

WindowsLive_AllowConsent

d. Implementation

1. Windows Live protocol

Most of the protocol related code is taken from the Windows Live API samples. I won’t  drill into too much details here, have a look at the classes LiveLogin, LiveConsentToken and LiveAuthenticatedUser in the sample code provided with this paper.

2. Windows Live delegated authentication process

wlid del auth

The delegated authentication process is composed of the following steps (these include the local authentication steps as well):

  1. A user requests the web site’s home page (default.aspx). Because he is not yet authenticated and forms authentication has been setup on the site he is redirected to the login page (login.aspx),
  2. The login page offers, through an iFrame pointing to Windows Live login page, a sign in link,
  3. Once the user clicks on it, he is rediricted to the classic Window Live login page,
  4. Windows Live then calls the web site that initiated the authentication (wlid.axd) with a signin action,
  5. The handler checks if the request already contains a valid Windows Live consent token. If the consent token is valid then go to step 7. If no consent token is available then a new one is requested through the Windows Live consent form,
  6. Once the user has granted the required delegation rights to the site, Windows live then calls back the handler  (wlid.axd) with a delegated authentication(delauth) action.
  7. The user is then validated against the membership provider, signed in on the web site and finally redirected to the home page.

Steps 7 and  8 on the schema above  represents the web site requesting user’s details such as full name, emails, location etc… as the only information provided to the site by the windows live login is an ID that uniquely identifies the user as well as a token.

Windows Live callback handler

This callback handler is used to handle Windows Live callback upon successful authentication and consent agreement. The  address that exposes this HttpHandler is the one specified in the Windows Live Application registration form known as ReturnUrl. It is possible to add extra parameters (referred as a context) to the initial request made for authentication in order to supply additional information to the callback. For instance, one can supply a url that must be called at the end of authentication and consent agreement phase.

The callback handler needs to be able to address 4 types of action on the callback made by Windows Live:

  • signin,
  • signout,
  • clear cookie,
  • delegated authentication

This handler is an important piece of the process as it handles requests made by  Windows Live and act on the local web site accordingly. Its action is mostly based on redirections and local authentication.

Windows Live Asp net membership provider

This custom membership provider is quite different from the one that are usually presented. Because our goal is to externalize as much as possible the user management there are quite a few methods that usually requires a specific implementation but that are not relevant in our case. For exemple, ChangePassword, ChangePasswordQuestionAndAnswer, GetPassword and so on.

Here are the methods and properties that needs to be implemented in our WindowsLiveMembershipProvider:

Methods:

ValidateUser,
GetUser

Properties:

ApplicationName,
EnablePasswordReset,
EnablePasswordRetrieval,
RequiresQuestionAndAnswer,
RequiresUniqueEmail

The most interesting parts of this membership provider are obviously the ValidateUser and GetUser methods. The first one ensures that the current user own a valid Windows Live token and the last returns a custom membership user that contains all the details about the user such as his name, his email address(es), location etc…

Here is the implementation of the ValidateUser method:

public override bool ValidateUser(string username, string password)
{
    System.Diagnostics.Debug.WriteLine("ValidateUser");

    HttpCookie __consentCookie = HttpContext.Current.Request.Cookies[WindowsLiveConstants.COOKIE_CONSENT];

    if (__consentCookie != null)
    {
        LiveConsentToken __token = LiveMembershipProvider.LiveLogin.ProcessConsentToken(__consentCookie.Value);
        return __token.IsTokenValid;
    }
    return false;
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

and the GetUser method:

public override MembershipUser GetUser(string username, bool userIsOnline)
{
    MembershipUser __user = null;
    long __lid;
    string __delegationToken;
    if (IsConsentTokenValid(out __lid, out __delegationToken))
    {
        //get the user details from Windows live contact api
//to improve responsiveness user details can be tested on the current user to see if they have already been retrieved
LiveContactsRestProvider __liveContactsRestProvider = new LiveContactsRestProvider(__lid, __delegationToken); Owner __owner = __liveContactsRestProvider.GetOwnerDetails(); __user = LiveMembershipUser.CreateLiveMembershipUser(__owner, this.Name); } return __user; }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

As described earlier, because the default membership user only contains a user id, it is required to create a specialized membership user.

Here is the details of its implementation:

    public sealed class LiveMembershipUser
        :MembershipUser
    {
        private Owner _owner;

        private LiveMembershipUser(Owner owner, string providerName, string name, object providerUserKey, string email, string passwordQuestion, string comment, bool isApproved, bool isLockedOout, DateTime creationDate, DateTime lastLoginDate, DateTime lastActivityDate, DateTime lastPasswordChangedDate, DateTime lastLockoutDate)
            :base(providerName, name, providerUserKey, email, passwordQuestion, comment, isApproved, isLockedOout, creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockoutDate)
        {
            _owner = owner;
        }

        public Owner Owner
        {
            get { return _owner; }
            set { _owner = value; }
        }

        public static LiveMembershipUser CreateLiveMembershipUser(Owner owner, string providerName)
        { 
            string __userName = String.Format("{0} {1}", owner.Profiles.Personal.FirstName, owner.Profiles.Personal.LastName);

            //Look up the default address if none pickup the first email available
            List __emails = owner.Emails.Where(email => email.IsDefault == true).ToList();
            string __email = (__emails.Count == 1) ? __emails[0].Address : owner.Emails[0].Address;

            LiveMembershipUser __user = new LiveMembershipUser(
                                                owner,                  //owner detais
                                                providerName,           //membership provider name
                                                __userName,             //user's name
                                                owner.WindowsLiveID,    //providerUserkey
                                                __email,                //email
                                                string.Empty,           //password question
                                                string.Empty,           //comment
                                                true,                   //is approved
                                                false,                  //isLockedOut
                                                DateTime.MinValue,      //creation date
                                                DateTime.Now,           //last login date
                                                owner.LastChanged,      //last activity date
                                                DateTime.Now,           //last password change date
                                                DateTime.MinValue);     //last lock out date
            
            return __user;
        }
    }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

3. Windows Live Contact data (lack of) API

Microsoft does not provide (yet) a .Net object model representing the Windows Live Contact data, the only thing close is the schema of the data returned by the REST service. So I had to build up my own. Here is the class diagram representing the Windows Live Contact object model as I represented it:

LiveContacts

4. Windows Live Contact Rest provider

This last piece is nothing more than a helper allowing to query the Windows Live Contact services exposed as REST. It is in charge of building the request url, provide the authentication details , call the service and deserialize the xml returned into .Net objects described in the previous step.

The authentication is provided by a specific header added to the request:

__request.Headers.Add(WindowsLiveConstants.DELEGATION_HEADER_KEY, 
String.Format(WindowsLiveConstants.DELEGATION_HEADER_VALUE_FORMAT, _delegationToken));
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

The header must conform to the following pattern:

public const string DELEGATION_HEADER_VALUE_FORMAT = "DelegatedToken dt=\"{0}]\"";
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

5. Use a user’s contact list

Once the user is logged in on the site, we can now use Windows Live Contacts to retrieve his friends. In the sample provided with this article, the about.aspx page lists the user’s friends that have published on NetFxFactory.

The sample uses rss feeds to get the posts from NetFxFactory and populate this sample site.

Now, we can look up the user’s contacts in order to retrieve those that published something on NetfxFactory*. Then we easily provide a list of friends as well as a link to a page listing all the posts made by a particular contact.

*Acknowledgement: Because NetFxFactory is powered by Community Server an helper has been done in order to map IDs from CS to Windows Live unique IDs.

f. Sign in on Windows Live with Cardspace

Windows Live allows you to associate a Carspace card to your Windows Live account in order to sign in via Cardspace instead of the usual email/password pair. This saves you from handling Cardspace on your site (specially the required SSL certificate used to authenticate the web site requesting the Card), simply delegate the work to Windows Live that will handle request and validation of the card.

To associate a card to your Windows Live account, navigate here and follow the registration process. Once completed, you get the following options when signing in Windows Live:

WindowsLive_Cardspace

3. Samples

An implementation of NetFxFactory meeting Windows Live is available at this address: http://live.netfxfactory.org. This sample is an ASPNET application that uses Windows Live delegated authentication, as demonstrated in this paper, to logon the site. Once logged in, the user is able to browse the site and display rss feeds from www.netfxfactory.org. The “My Friends” section displays all the user’s Windows Live Contacts in two groups: the user’s friends who posted an article to NetFxFactory and the friends who did not. In the group of contacts that wrote an article on NetFxfactory, there is a link to a dedicated page that displays all the articles posted by a particular contact. The source code is available here (note: you need to register an application as demonstrated above and update the web.config and login.aspx files accordingly. Check out readme.txt within the Zip archive).

4. Going further

As we have seen in this paper, it is pretty straight forward to take advantage of Windows Live ID to handle all most of the user management related to a community site. We voluntarily set aside the problem of storing custom data (for instance user’s role, preferences etc…) that can easily be externalized as well using Windows Live Application Base Storage (check http://dev.live.com/livedata/sdk/ for details and samples).

a. Claims and Federation

Taking advantage of Windows Live is great in a sense but wouldn’t  be nice to be able to raise the abstraction one level up and use claim based authentication on your web site? Using claims and Federation one would be able to target multiple identity providers such Windows Live, Facebook, Yahoo and so on, abstract their respective implementation details and only manage claim evidence on client side to deal with authentication, user management and so on. This will be the subject of a paper to come.

b. Geneva (formerly known as Zermatt)

Microsoft Code Name Geneva is Microsoft’s answer to federation and claim based identity. There is a thorough paper presenting Geneva and its implementation written by Keith Brown from Plurasight LLC available here. Microsoft Sharepoint is meant to go down the way of claims based authentication. Even though there are no clues as of today that Zermatt will be used as the underlying technology to implement such authentication mechanism, there are good chances that both can interoperate.

Geneva is one of the pillars beyond Veracruz project.

This is a hot subject as proven by the Microsoft 2008 PDC agenda and the Issue 16 of the Architecture Journal.

c. Veracruz

Veracruz project will rely on such a mechanism and leverage Facebook capabilities (authentication, user/profile/contact management) in conjonction with Windows Sharepoint Services in order to make identity federation at heart of the system.

Published Monday, November 03, 2008 12:00 AM by Tony

Comments

No Comments
Anonymous comments are disabled

This Blog

Syndication

Powered by Community Server (Personal Edition), by Telligent Systems