Was this page helpful?
Your feedback about this content is important. Let us know what you think.
Additional feedback?
1500 characters remaining
Building a Callback Page

Building an OAuth WRAP Callback Page

Ff752581.note(en-us,MSDN.10).gifNote:
Current information about Live Connect is now available in the Windows Live Developer Center. The information in the following sections is provided for legacy purposes only.

An OAuth WRAP callback page is required for each application that is built by using Windows Live Messenger Connect. The callback page provides the functionality to process the verification code that is returned by the Windows Live consent service, to acquire an access token, and to store the access token and other information as cookies on the client system. This topic demonstrates how to construct a callback page. A complete callback page is shown at the end.

Ff752581.note(en-us,MSDN.10).gifNote:
If you are building your application by using ASP.NET, you do not have to create a callback page. Instead, you can use the Microsoft.Live.AuthHandler.dll library that is included in the Windows Live SDK to process the verification code and access token that are returned by the Windows Live consent service. For more information, see Getting Started Sample for ASP.NET.

The following is an example of the structure of a basic callback page.

Ff752581.note(en-us,MSDN.10).gifNote:
This example uses C#; however, you can write your callback page in the programming language of your choice.
<%@ Page Language="C#" ClassName="Callback" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Web.Services" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
       // Code to process consent information goes here.
    }
</script>
<html>
<body onload="onLoad()">
    <script type="text/javascript">
        var windowClose = window.close;
        window.close = function() {
            window.open("", "_self");
            windowClose();
        }
        function onLoad() {
            window.close();
        }
    </script>
</body>
</html>

This page contains the following key elements:

  • Page_Load event handler. This handler contains the code that detects a verification code, acquires an access token, and writes the cookies that are controlled by the Messenger Connect UI controls and that the JavaScript API requires.
  • Code to close the pop-up window. Typically, when a user clicks a sign-in link, the consent UI is displayed in a pop-up window. After the user provides consent, the Windows Live consent service returns the user to the callback page in this window. The callback page processes the consent information (for example, the verification code), calls the JavaScript function to close the window, and returns the user to the original webpage.

The callback page detects whether a verification code is present in the query string of the URL. The verification code indicates that a user provided consent to your application.

// Added to the Page_Load event handler.
if (Request.QueryString["wrap_verification_code"] != null)
{

}

If the callback page detects a verification code, it acquires an access token, as shown in the following code. Microsoft Ajax Library for Messenger Connect

// Added to the verification code if statement.
WebRequest tokenRequest = WebRequest.Create("http://consent.live.com/connect.aspx");
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.Method = "POST";
using (StreamWriter writer = new StreamWriter(tokenRequest.GetREquestStream())
{
    writer.Write(string.Format("wrap_client_id={0}&wrap_client_secret={1}&wrap_callback={2}&wrap_verification_code={3}",
    HttpUtility.UrlEncode("***YOUR CLIENT ID***"),
    HttpUtility.UrlEncode("***YOUR SECRET KEY***"),
    HttpUtility.UrlEncode("***YOUR CALLBACK URL***),   
    HttpUtility.UrlEncode(Request.QueryString["wrap_verification_code"])));
}

If the site that hosts the callback page is not using HTTPS, the callback URL that you provide in the URL request for a token must include session-specific information as shown in the following code example.

"***YOUR CALLBACK URL***" + "?wl_session_id=" + HttpContext.Current.Session.SessionID

After the callback page acquires the response that includes the access token from the Windows Live consent service, it can add the cookies that are required by the Messenger Connect UI controls and JavaScript API.

The following table lists the cookies that the callback page must write to the client system.

Cookie Name Description

wl_clientID

The client ID of your application.

wl_clientState

The value of the OAuth WRAP parameter, wrap_client_state.

wl_scope

The comma-delimited list of Windows Live Messenger Connect scopes, and expiration values for each scope.

wl_accessToken

The value of the OAuth WRAP parameter, wrap_access_token.

wl_accessTokenExpireTime

The expiration date of the access token.

wl_cid

The user's CID identifier.

wl_complete

Indicates that the authorization is complete.

wl_errorMessage

The error text from the consent service, if any.

The following example demonstrates how to process an access token.

// Added after the request to the consent service is complete.
WebResponse tokenResponse = tokenRequest.GetResponse();
string tokenResponseText = new StreamReader(
tokenResponse.GetResponseStream()).ReadLine();
NameValueCollection tokenResponseData =
   HttpUtility.ParseQueryString(tokenResponseText);
Response.Cookies["wl_clientId"].Value = "***YOUR CLIENT ID***";
Response.Cookies["wl_clientState"].Value = Request.QueryString["wrap_client_state"];
Response.Cookies["wl_scope"].Value = Request.QueryString["exp"];
Response.Cookies["wl_accessToken"].Value =   tokenResponseData["wrap_access_token"];
Response.Cookies["wl_accessTokenExpireTime"].Value =   tokenResponseData["wrap_access_token_expires_in"];
Response.Cookies["wl_cid"].Value = tokenResponseData["cid"];
Response.Cookies["wl_complete"].Value = "done";

When you are coding a callback page for your application, we recommend that you follow these guidelines:

  • Use SSL. Although it is not required, using SSL can help prevent a third party from intercepting your communications with the Windows Live consent service.
  • Make your callback URL unique for each user. If your application cannot use SSL, you should implement some mechanism that can protect against a third party gaining unauthorized access to your communications. One way to accomplish this is to add a unique variable to the callback URL on your sign-in page. This variable is included when a user first provides consent. The callback page must be able to recreate this variable when it acquires the access token.
  • Catch exceptions during the development process. We recommend that you place the access token request and subsequent code in a try-catch block during the development process.
  • Store the refresh token. The refresh token is also included in the initial consent request. Your application can use this token to acquire a new access token after the current token expires. We recommend that you store the refresh token in a database or other location, and use that token whenever your application detects that the current access token is no longer valid.

To add the callback page to your application, you must include it in the <wl:app> tag. (For more information, see Application Control.) This tag provides the information that is required by Windows Live services to verify your application and process information requests.

The following is an example of an application tag.

<wl:app
    client-id="<%= ***YOUR CLIENT ID*** %>"
    callback-url = "<%= ***YOUR CALLBACK URL*** %>"
    channel-url = "<%= ***YOUR CHANNEL URL*** %>"
    scope="Messenger.SignIn"
    >
</wl:app>

If the site that hosts the callback page does not use HTTPS, the callback URL you that provide for the callback-url attribute should match what is used in the custom callback page. For example, if the session-specific data was specified with a session ID, that ID must be used in the application tag as shown below. The example below also demonstrates how to use the web.config file to specify the necessary values for the application tag.

<wl:app 
    client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>" 
    callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?wl_session_id=<%= HttpContext.Current.Session.SessionID%>"
    channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>"
    scope="Messenger.SignIn"
    >
</wl:app>

The wl_wrap_client_callback key in the web.config file specifies the name of the callback page. For example, if your callback page is called CustomCallback.aspx, your domain is contoso.com, and you are using port 8080, the key in the web.config file is as follows.

<add key="wl_wrap_client_callback" value="http://contoso.com:8080/CustomCallback.aspx"/>

The following examples show a compete example of a callback page and a page that uses the custom callback page. To use these pages on your site, put the correct keys in the site's web.config file to specify the client ID, secret key, and callback URL with information that applies to your application. This example assumes that the callback page is invoked by using HTTP and therefore requires the additional session-specific information to uniquely identify the request.

The following is the callback page.

<%@ Page Language="C#"%>

<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Web.Services" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">  
    // For security purposes, we need to create session-specific callback URLs when not using HTTPS.
    public static string GetUrlForSession()
    {
        return ConfigurationManager.AppSettings["wl_wrap_client_callback"] + "?wl_session_id=" + HttpContext.Current.Session.SessionID;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        
        if (Request.QueryString["wrap_verification_code"] != null)
        {
            try
            {
                // Construct a request for an access token.
                WebRequest tokenRequest = WebRequest.Create(
                "https://consent.live.com/AccessToken.aspx");
                tokenRequest.ContentType = "application/x-www-form-urlencoded";
                tokenRequest.Method = "POST";
                using (StreamWriter writer = new StreamWriter(tokenRequest.GetRequestStream()))
                {
                    writer.Write(string.Format(
                    "wrap_client_id={0}&wrap_client_secret={1}&wrap_callback={2}&wrap_verification_code={3}",
                    HttpUtility.UrlEncode(ConfigurationManager.AppSettings["wl_wrap_client_id"]),
                    HttpUtility.UrlEncode(ConfigurationManager.AppSettings["wl_wrap_client_secret"]),
                    HttpUtility.UrlEncode(GetUrlForSession()),
                    HttpUtility.UrlEncode(Request.QueryString["wrap_verification_code"])));
                }
                // Send the request and get the response.
                WebResponse tokenResponse = tokenRequest.GetResponse();
                
                // Read the first line of the response body.
                string tokenResponseText = new StreamReader(tokenResponse.GetResponseStream()).ReadLine();

                // Parse the response body as being in the format of 'x-www-form-urlencoded'.
                NameValueCollection tokenResponseData = HttpUtility.ParseQueryString(tokenResponseText);

                // Store data in cookies where the JS API will pick them up.
                Response.Cookies["wl_clientId"].Value = ConfigurationManager.AppSettings["wl_wrap_client_id"];
                Response.Cookies["wl_clientState"].Value = Request.QueryString["wrap_client_state"];
                Response.Cookies["wl_scope"].Value = Request.QueryString["exp"];
                Response.Cookies["wl_accessToken"].Value = tokenResponseData["wrap_access_token"];
                Response.Cookies["wl_accessTokenExpireTime"].Value = tokenResponseData["wrap_access_token_expires_in"];
                Response.Cookies["wl_cid"].Value = tokenResponseData["cid"];
                Response.Cookies["wl_complete"].Value = "done";
            }
            catch (System.Net.WebException webException)
            {
                string responseBody = null;
                if (webException.Response != null)
                {
                    using (StreamReader sr = new StreamReader(webException.Response.GetResponseStream(), Encoding.UTF8))
                    {
                        responseBody = sr.ReadToEnd();
                    }
                }
                throw new Exception(String.Format(
                "Failure occurred contacting consent service: Response=\r\n\r\n----\r\n{0}\r\n----\r\n\r\n", responseBody), webException);
            }
            catch (System.Exception innerException)
            {
                throw new Exception("Failed to get access token. Ensure that the verifier token provided is valid.", innerException);
            }
        }
    }
</script>
<html>
<head><title>Callback</title></head>
<body onload="onLoad()">
    <script type="text/javascript">
        var windowClose = window.close;
        window.close = function () {
            window.open("", "_self");
            windowClose();
        }
        function onLoad() {
            window.close();
        }
    </script>
</body>
</html>

The following is the page that uses the callback page.

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Configuration" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wl="http://apis.live.net/js/2010">
<head runat="server">
    <title>Testing Custom Callback</title>
    <script type="text/javascript" src="http://js.live.net/4.1/loader.js"></script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
<wl:app 
    channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>"
    callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?wl_session_id=<%= HttpContext.Current.Session.SessionID%>"
    client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>" 
    scope="WL_Profiles.View, Messenger.SignIn, WL_Contacts.View">
</wl:app>
        <wl:signin id="mysignin">
        </wl:signin>
    </div>
    </form>
</body>
</html>
Show:
© 2015 Microsoft