With the release of Windows 8 and the new app distribution channel through the Windows Store, developers now are embracing a completely new and different programming model.  One of the often under-looked features in this new ecosystem is the ability for applications and websites to leverage users’ Microsoft accounts to provide personalized content without ever prompting the user to sign in.  For client only apps, using SSO along with other Windows services such as Profile allows app developers to easily access user’s information and provide customized experiences without requiring additional user gesture.  Apps can also leverage the 7GB free cloud storage that every Windows user has to store information in SkyDrive that is accessible from any platform.  For websites or apps that have a server component, implementing SSO becomes even more compelling because now you can deliver personalized content to your users without having your own identity system.  Even if you already have an identity system on your website, you can still benefit from SSO because with the support from the OS, the sign in and sign up experiences become much more seamless.

The Live SDK is a set of client APIs that make integrating with MSA and Windows services simple and straightforward.  The V5.3 version of the Live SDK was recently released which includes updated Windows 8 SDKs and brand new .Net SDKs for classic desktop apps and Asp.Net apps.  In this post, I will talk about how to use these SDKs to implement Single-sign-on (SSO) using Microsoft accounts (MSA) for Windows Store apps and Asp.Net websites. (I choose to talk about Asp.Net websites purely because we have an existing SDK for it. The concept though applies to websites implemented using other technologies as well.) If you want to take an in-depth look at the benefits and the technology used in SSO on Windows, check out the //BUILD2012 talk on this topic. For those who just want to get start on code, it is my hope that content in this post would help you get right on track.

Pre-requisite

Implementing SSO for Windows Store apps

Implementing SSO for a Windows Store app is very straightforward.  Follow the following steps and you’ll be done in 5 minutes. 

1. Create a new Windows Store C# app called HelloWorldWin8 in Visual Studio 2012.

image

2. Bring up the Add Reference dialog, fine the Windows/Extensions tab and add Live SDK to the project.

image

3. Add an image control and a textbox control to the main page.  The image control will be used to display the user’s profile picture and the textbblock will be used to show a greeting message.  The complete XAML is shown below:

<Page
    x:Class="HelloWorldWin8.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:HelloWorldWin8"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Image x:Name="imgMe" HorizontalAlignment="Left" Height="150"  Width="150" 
Margin="230,330,0,0" VerticalAlignment="Top" />
        <TextBlock x:Name="tbGreeting" HorizontalAlignment="Left" Margin="450,350,0,0" 
VerticalAlignment="Top" Height="50" Width="823" FontSize="32"/>
        <TextBlock x:Name="tbError" HorizontalAlignment="Left" Margin="230,560,0,0" TextWrapping="Wrap" 
VerticalAlignment="Top" Height="175" Width="1043"/>
    </Grid>
</Page>
4. Add the following code to mainpage.xaml.cs.
using System;
using System.Collections.Generic;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
 
using Microsoft.Live;
 
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
 
namespace HelloWorldWin8
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private static string[] scopes = new string[] { "wl.signin wl.basic" };
        private LiveAuthClient authClient;
 
        public MainPage()
        {
            this.InitializeComponent();
            this.authClient = new LiveAuthClient();
        }
 
        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            this.SignInUser();
        }
 
        private async void SignInUser()
        {
            try
            {
                var loginResult = await this.authClient.InitializeAsync(new List<string>(scopes));
                if (loginResult.Status == LiveConnectSessionStatus.Connected)
                {
                    this.GetMe();
                }
                else
                {
                    loginResult = await this.authClient.LoginAsync(new List<string>(scopes));
                    if (loginResult.Status == LiveConnectSessionStatus.Connected)
                    {
                        this.GetMe();
                    }
                }
            }
            catch (LiveAuthException e)
            {
                this.tbError.Text = e.ToString();
            }
        }
 
        private async void GetMe()
        {
            try
            {
                var connectClient = new LiveConnectClient(this.authClient.Session);
                this.imgMe.Source = new BitmapImage()
                {
                    UriSource = new Uri("https://apis.live.net/v5.0/me/picture?access_token=" + this.authClient.Session.AccessToken)
                };
 
                var getResult = await connectClient.GetAsync("me");
                dynamic jsonResult = getResult.Result;
                this.tbGreeting.Text = "Hello " + jsonResult.first_name + " " + jsonResult.last_name;
            }
            catch (LiveConnectException e)
            {
                this.tbError.Text = e.ToString();
            }
        }
    }
}

5. Provision your app to use Live Connect services.  Make sure you have a valid Windows Store developer license.  Go to Store –> Create App Packages from the Visual Studio menu (in the Ultimate version, this menu item is under Project –> Store –> Create App Packages), sign in and follow the steps to create a new Windows Store app.

6. Run the application.  Upon app start, you will be prompted to give the app permission to access your data.  Click Yes.  Your profile picture should then appear along with a message that greets you by your name.

7. Now you’re set up to use Microsoft accounts in your app, you can then add SkyDrive support by using our SkyDrive API.  To learn more about how to work with SkyDrive, check out our //BUILD2012 talk “The developer’s guide to the SkyDrive API” by Deepesh Mohnani.

Integrate Microsoft account and Windows services in your website

You can also integrate with Microsoft account in your website to use MSA as your identity system or to interact with user data stored in SkyDrive and other Windows services.  With the introduction of the new Asp.Net SDK, we’ve made this a much easier task than it used to be.

To show you how to user the Asp.Net SDK to authenticate the user using MSA, I’m going to use the project I created for the BUILD talk as my code sample here.  It would be easier to walk through the code if you open the project in Visual Studio.  So here’s how you would set it up:

  • First download the project files and open the solution file YummySample.sln.  Notice that there are two projects in the solution, one YummyWin8App and YummyWebsite.  These two projects are designed to work with each other to demonstrate the interactions between your app and your companion website.
  • Open the Win8 app project and follow the steps above to register this app with the Windows Store.
  • While you’re still in the Windows Store developer portal, select your application, click on “Advanced features” –> “Push notifications and Live Connect services info” –> “Representing your app to Live Connect users”. Enter “http://www.yummywebsite.com" as the redirect domain for your app.  As an optional step, upload a logo.

image

  • Click the “Authenticating your service” link on the left nav and note down the two values on this page: Package Security Identifier (PSID) and Client secret.

image

  • Go back to Visual Studio, open YummyWebsite –> Controllers –> HomeController.cs file and copy the values of your app’s client id (the PSID) and client secret to the corresponding variables at the top of the file.  Do the same for the AccountControll.cs file. 
  • Next, you need to configure you website on your local IIS server.  Go to IIS Manager, add a new website call “YummyWebsite” and configure it to point to where your copied the project files to on your local file system.  Set the host name to be “http://www.yummywebsite.com”.

image

  • Add a host entry in your hosts file that points to your local IIS.

127.0.0.1    www.yummywebsite.com

  • Now you should be set up to run the sample.  Make sure you’re logged in to the OS with either a Microsoft account or a MSA connected domain/local account.  In IE10, browse to http://www.yummywebsite.com and see the web page loads with pictures of yummy food.  Notice there is no personal information displayed on the page.
  • Click the Register link at the upper right corner.  This takes you to a page where the website asks you to register for an account.  For the simplicity of the sample, the registration logic is not implemented.  The purpose of this page is to serve as a proof-of-concept that you can streamline sign-up with information about the user and make the process much less intimidating.
  • Once you click the Register button, it takes you back to the home page where you now will see your name and profile picture show up at the upper right corner.  You have now signed in to the website using your MSA.

Now let’s take a look at the code for the home controller:

public async Task<ActionResult> Index()
        {
            HomePageModel model = new HomePageModel();
            model.Pictures = this.GetPictures();
            model.Favorites = new List<string>();
            model.ProfileImageUrl = "Images/user.png";
 
            #region LiveSDK Impl
 
            HttpCookie stateCookie = this.Request.Cookies["state"];
            if (stateCookie == null || string.Compare(stateCookie.Value, "registered", true) != 0)
            {
                return View(model);
            }
 
            LiveLoginResult loginResult = await this.authClient.InitializeWebSessionAsync(this.HttpContext);
            if (loginResult != null && loginResult.Status == LiveConnectSessionStatus.Connected)
            {
                model.LoginUrl = "";
                model.ProfileImageUrl = "https://apis.live.net/v5.0/me/picture?access_token=" + loginResult.Session.AccessToken;
 
                var connectClient = new LiveConnectClient(loginResult.Session);
                var getResult = await connectClient.GetAsync("me");
                var jsonResult = getResult.Result as dynamic;
                model.UserName = jsonResult.first_name + " " + jsonResult.last_name;
 
                model.Favorites = this.GetFavorites(loginResult.Session.AuthenticationToken);
            }
            else if (loginResult != null &&
                loginResult.Status == LiveConnectSessionStatus.Unknown || loginResult.Status == LiveConnectSessionStatus.Expired)
            {
                Dictionary<string, string> options = new Dictionary<string, string>();
                options.Add("display", "none");
                string reAuthUrl = this.authClient.GetLoginUrl(new List<string>(scopes), options);
                return new RedirectResult(reAuthUrl);
            }
            else
            {
                model.LoginUrl = this.authClient.GetLoginUrl(new List<string>(scopes));
                model.ProfileImageUrl = "Images/user.png";
            }
 
            #endregion
 
            return View(model);
        }

The line of code that I want to highlight is this:

LiveLoginResult loginResult = await this.authClient.InitializeWebSessionAsync(this.HttpContext);

The LiveAuthClient.InitializeWebSessionAsync function is the main function that handles the web authentication flow for OAuth 2.0. If the user has 1) signed in to MSA either through the OS for IE or through the web for any browsers that support cookies; 2) granted permission for the app to access his information, the user will be signed in to your website and you can go ahead with retrieving user information from Windows services.

If the user’s status is not Connected, it means either the user is not signed or the user has not given the app permission to access his data.  In these cases, the app needs to call LiveAuthClient.GetLoginUrl to get the url for the sign-in/consent page and redirect the browser to that page so the user can finish the sign-in flow. 

Notice that at the top of the HomeController.cs file, we specify the value for redirectUrl as

private static string redirectUrl = "http://www.yummywebsite.com/home/login";

How is this used?  The OAuth web flow is a two step authentication process.  When the user grants consent to the web app, the authentication server sends a authorization code to the application web server (http://www.yummywebsite.com) and the application web server needs to exchange this code for the access token that it needs for retrieving data from the resource provider (https://apis.live.net).  The redirect url tells the authentication server where to send the authorization code to.  You can use the default path to handle redirect, but we recommend that you use a separate path or a different controller for the sole purpose of handling OAuth redirect.   It makes to code easier to read and maintain.  In my example, I’m using the path /home/login to handle the redirect.

The AccountController class handles the /account/register path.  The logic here is similar to that of the home controller.  Notice the code below:

Dictionary<string, string> options = new Dictionary<string, string>();
options.Add("state", "register");
string reAuthUrl = authClient.GetLoginUrl(new List<string>(scopes), options);
return new RedirectResult(reAuthUrl);

Here we’re getting the login url by calling LiveAuthClient.GetLoginUrl.  A ‘state’ parameter is added to the login url to track the current navigation state and the user is taking to the MSA login or consent page when the browser gets redirected to the login url.

Tracking user on your website and providing personalized content

So far, I have not talked about how to link the Win8 app and the website together and have SSO work across the two.  I will devote this section for that purpose.  First let’s talk about SSO across the app and the website.  The promise here is if you are signed in to the app on a connected windows device, you’re also signed in to the website and vice versa.  To see how it works, now run the YummyWin8 app again.  You will notice that after the app initializes, your name and profile pictures shows up at the upper right corner.  This is because you’ve already given the app permission to sign in when you went through the registration process on the website.  The app now can automatically sign you in when it starts.

Now you have SSO between the app and the website, we can then talk about tracking users and providing personalized content.  The key that provides the link between the app and the website in terms of user tracking is a ticket that’s known as the “Authentication Token”.   So far, when we talk about authentication and authorization through OAuth, we talk about the “access token”. An access token is a ticket that the authentication server gives to the application for accessing data from a resource provider. In most cases, the resource provider is owned by the same entity that owns the authentication server. For example, the SkyDrive API service is a resource provider that accepts Microsoft account’s OAuth access token. The authentication token on the other hand, is a ticket that is understood by the application website, i.e. your website. This ticket contains a unique user id that your app can use to identify the user. The ticket is encrypted using the application secret that only your app knows about, therefore only understood by your app. Once you are able to uniquely identify your user, you have the foundation for providing personalized content. You can use this unique user id as key into your database, or any other way you choose to store user information.

The authentication token is exposed as the AuthenticationToken property on the LiveConnectSession object.  Note that this property is only set if the application has a redirect domain registered.  Apps that don’t have a server-side component have no use of this token. 

To decrypt the authentication token, you need your app’s client secret.  The authentication token is encrypted with your client secret using the standard JWT format.  Because only your web server has access to the client secret, you must send the authentication token to your website for decryption.  We ask that under no circumstances, client secret should be store on user machines.  The Asp.Net SDK provides a utility method to decrypt the token and extract the user id.  You can call LiveAuthClient.GetUserId and pass in the authentication token.  The return value is the unique user id you can use to identify the current user.

The “Add to favorites” functionality in the sample app leverages the authentication token to store user favorites on the server.  You can easily expand this to include any personalized content you want to build for your app.  To see how the favorites functionality work in this sample, follow these steps:

  1. Start the website by going to http://www.yummywebsite.com
  2. Run the YummyWin8 app.
  3. Click on a couple pictures and see them being selected.
  4. Click the “Add to favorites” header.
  5. Notices the favorites being added to the favorites column.
  6. Go back to the website and refresh the page.  Notice the favorites also show up on the website.

How does this work?  It works with the Win8 app sending the authentication token along with the list of user favorites to the web server.  The website then decrypts the token, gets the unique user id and stores the favorites list for this user.  The website can also display the favorites by getting its own authentication token, decrypts it and then retrieve the favorites list for the current user.

That’s it.  You have set up the foundation to power your app and your website with Microsoft accounts and Windows services.  Now it’s up to you to create the most awesome app for the new Windows ecosystem and good luck!