Integrate Windows Live ID Authentication with ASP.NET Membership, Profiles and Roles


By Peter Bromberg
Printer Friendly Version
  

Microsoft has rolled out its Live ID Web Authentication SDK and from some of the forum posts I've seen, developers are already misunderstanding what they really have to work with. You see comments like "hey, but it won't give me their email", or "Useless!", etc. Wrong! Here we'll show how to integrate Live ID with ASP.NET Membership, Roles and Profile.



Windows Live ID is the authentication used by Microsoft’s web sites and services - for example, Hotmail and Live.com. Windows Live ID is the evolution of the original Passport Service -- the name change ostensibly to focus on Window Live services and its ability to federate in a multi-property, multi-service identity world. If you don't believe this is big, let me give you one number: currently, more than 380 million users have credentials that work with Windows Live ID.  If you can make it easy for visitors to sign into your web site via Live ID, this is going to be very familiar to them -- which can increase traffic.

When you put the sample IFRAME - based Live ID sign-in "control" on a page, and a Live ID user "signs in", the AppId information you've stored when you registered your property (site) with Live ID tells it to redirect to the page you've designated. At this point you can capture the unique UserId string for that user, which will always be the same for your application id. Now this requires a bit of thinking "outside the box" for most developers:

Normally,  with ASP.NET, you would have a new user create a membership - with their username, email, password, etc. Your membership provider would store this information and this would enable the user to come back and log back in at your site. You would probably email them a link to "activate" so that you don't get "spambers". You would authenticate them via the username and password they provide, using either basic Forms Authentication or the Membership Provider.

With Live ID, the process needs to be reversed just a bit. When a new user signs into into your site with their Live ID they are already authenticated  -- you just don't have their user information yet. So, what you do is look up their UserId (their Live ID identifier described above) and if they aren't in your system, you take them immediately to your sign-up form where they can now enter their name, email, preferences, etc. For standard ASP.NET Membership provider authentication, the password you would use would be the same as the username, and that is the unique user ID string returned from Live ID .

What I have done here is to alter a previous implementation of some simple Membership, Profile and Role provider classes that I originally adapted from Michal Altair Valášek's "Altairis" Simple providers which can be found at codeplex.com. I've already done  my own implementation of the Table and Stored Procedure Providers here, but I think Valášek's implementation lends itself more easily to modfication for this type of demo. I've changed the namespace since I don't want anyone getting a hold of it to think it's his original and not understand why it won't work for "regular" Membership.  To this, I have started with the WebAuth Live ID sample website from the SDK, so that it can be used "out of the box" by simply adding in my code and providers.

NOTE:To use this the way it was designed from the SDK, you must use IIS (not the development webserver from Visual Studio), and the web must be in a folder named "webauth" located just under your wwwroot IIS default web. The Application name must be "WebAuth" and the pages must be in the /sample subfolder. If you do not do this, then the default parameters for the SDK sample app won't redirect to the correct location on your localhost, and neither the original Live ID SDK sample nor my "hacked up" Live ID demo Membership sample will work. When you apply for your own real site AppID and parameters, you can deploy the web anywhere you want - as long as you enter the correct redirect location on the Live Id site when you go to register your application.

We start out just like the SDK example, with a default.aspx page on which we've placed the IFRAME Live ID Sign-in control:

<iframe 
id="WebAuthControl" 
name="WebAuthControl"
src="http://login.live.com/controls/WebAuth.htm?appid=<%=AppId%>&Context=<%=AppContext%>&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>

This is the codebehind for the Default.aspx page from which you can Sign In to your Live ID account:

public partial class DefaultPage : System.Web.UI.Page
{
    const string LoginCookie = "webauthtoken";
    // Initialize the WindowsLiveLogin module.
    static WindowsLiveLogin wll = new WindowsLiveLogin(true);
    protected static string AppId = wll.AppId;
    protected string UserId;
    protected string AppContext = "Register.aspx";
    
    protected void Page_Load(object sender, EventArgs e)
    {
        /* If the user token has been cached in a site cookie, attempt
           to process it and extract the user ID. */
        HttpRequest req = HttpContext.Current.Request;
        HttpCookie loginCookie = req.Cookies[LoginCookie];
        if(loginCookie != null){
            string token = loginCookie.Value;
            if ((token != null) && (token.Length != 0))
            {
                WindowsLiveLogin.User user = wll.ProcessToken(token);
                if (user != null) 
                {
                    UserId = user.Id;
if (Membership.ValidateUser(user.Id, user.Id))
                    {
                        MembershipUser memuser = Membership.GetUser(user.Id);
                        this.lblWelcome.Text = "Welcome back " + memuser.Email;
                    }
                    else
                    {
                        Response.Redirect(AppContext);
                    }
} } } } }

It is easy to see that the cookie is grabbed and processed by the code in the WindowsLiveLogin utility class, converted to  a UserId -- which is unique for a given user at your application -- and then, if the user does not validate (because we don't have them in our Users table yet), we redirect to the AppContext, which in this case, since we now already have an Authenticated User, is our "Register.aspx" page where we'll ask them to fill in the blanks for us. That's where the Membership, Roles and Profile Providers kick in.

On our Register.aspx page, we present a WebForm that asks for the username, password, email, and whether they want to receive our newsletter. When they submit this form, we have the following codebehind to process the page:

public partial class Register : System.Web.UI.Page
    {
        const string LoginCookie = "webauthtoken";
        protected string UserId;
        static WindowsLiveLogin wll = new WindowsLiveLogin(true);
        protected void Page_Load(object sender, EventArgs e)
        {
            /* If the user token has been cached in a site cookie, attempt
         to process it and extract the user ID. */
            HttpRequest req = HttpContext.Current.Request;
            HttpCookie loginCookie = req.Cookies[LoginCookie];

            if (loginCookie != null)
            {
                string token = loginCookie.Value;
                if ((token != null) && (token.Length != 0))
                {
                    WindowsLiveLogin.User user = wll.ProcessToken(token);
                    if (user != null)
                    {
                        UserId = user.Id;
                        MembershipUser user1 = Membership.GetUser(UserId);
                        if(user1!=null)
                            Response.Redirect("Default2.aspx");
                        Label1.Text = "Welcome, UserID " + UserId + ". Please complete your Registration.";
                    }
                }
            }


        }

        protected void btnRegister_Click(object sender, EventArgs e)
        {
          MembershipUser user=  Membership.CreateUser(UserId, UserId, txtEmail.Text);
            if(user!=null)
            {
//don't really need next line, just habit... FormsAuthentication.Authenticate(UserId, UserId); WebProfile Profile = new WebProfile(); Profile.Initialize(UserId,true); Profile.FirstName = this.txtFirstName.Text; Profile.LastName = this.txtLastName.Text; Profile.Newsletter = this.chkNewsLetter.Checked; Profile.Email = this.txtEmail.Text; Profile.Save(); GenericIdentity userIdentity = new GenericIdentity(UserId); GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity, new string[] { "User" }); Context.User = userPrincipal; if (!Roles.IsUserInRole(User.Identity.Name, "User")) { PAB.Web.Providers.SimpleSqlRoleProvider prov = new SimpleSqlRoleProvider(); NameValueCollection config = new NameValueCollection(); config["connectionStringName"] = "LiveId"; System.Configuration.ConnectionStringSettings ConnectionStringSettings = System.Configuration.ConfigurationManager.ConnectionStrings[config["connectionStringName"]]; prov.Initialize("",config); prov.AddUsersToRoles(new string[] {User.Identity.Name}, new string[] {"User"}); } Response.Redirect("Default2.aspx"); } else { } } }
In the btnRegister_Click handler, we create a new MembershipUser from the Live ID UserID (not their "username" or "password"), and their email. We authenticate them with FormsAuth. Then we create a new Profile and set its FirstName, LastName and boolean Newsletter properties, and we Save it. Next we create a new Identity from the UserId, stuff it into a new GenericPrincipal, and attach this to the Page's User property.

Finally, we check the Role, and if not present, we create an instance of the custom Role Provider and add the new User to the "User" role. The last step, now that we are done collecting and storing our Membership, Profile and Role "Stuff" for our new user, is to redirect them to the Default2.aspx "welcome new user" page, where I actually use the Profile class once again as a proof of concept to display their first and last name:

protected void Page_Load(object sender, EventArgs e)
    {
        /* If the user token has been cached in a site cookie, attempt
           to process it and extract the user ID. */
        HttpRequest req = HttpContext.Current.Request;
        HttpCookie loginCookie = req.Cookies[LoginCookie];

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

            if ((token != null) && (token.Length != 0))
            {
                WindowsLiveLogin.User user = wll.ProcessToken(token);

                if (user != null) 
                {
                    UserId = user.Id;
                WebProfile profile=  new  WebProfile(System.Web.Profile.ProfileBase.Create(user.Id));
                    Label1.Text = "Welcome, " + profile.FirstName + " " + profile.LastName + "!";
                }
            }
        }
  • To install this, you need to deploy it in wwwroot\webauth as described above.
  • Next, you need to create a new SQL Server database, "LIVEID".
  • Next, you run the SQL script "CreateTablesForLIveIdDb.sql" from the SQLScript folder.
  • Finally, alter the connection string in the web.config file to match your environment.

There it is - Windows Live ID authentication, fully integrated with custom Membership, Profile and Role providers.  All Membership and Profile fields are stored in non-opaque SQL Server table columns and can be custom-defined via provider entries in the web.config. That makes searching, sorting or reporting on your member info a breeze.

Kindly note that this demo app doesn't have form field validation or exception handling in order to keep things as readable as possible. You of course, as a developer, should always take the time to put these features in, and so I leave them as an exercise for the reader.

Download the Visual Studio Web Application Project /Solution



Biography
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking ,financial and telephony for 20 years. Pete focuses exclusively on the .NET Platform, and his samples at GotDotNet.com have been downloaded over 56,000 times. Peter enjoys producing 3D raytraced digital photo collage with Maya, the beach, and fine wines. You can view Peter's UnBlogIttyUrl, and BlogMetafinder sites.
Please post questions at forums, not via email!

button

 
Article Discussion: Integrate Windows Live ID Authentication with ASP.NET Membership, Profiles and Roles
Peter Bromberg posted at 08-Sep-07 03:55
Original Article

 
control the language and the css style of the sign in / out iframe
Or Shnaider replied to Peter Bromberg at 27-Dec-07 03:46

Hi,

Is it possible to control the language and the css style of the sign in / out iframe?

Thanks,

Or Shnaider