Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Cross Domain/Platform Authentication and Data Transfer

4.87/5 (10 votes)
29 Dec 2008CPOL14 min read 91.8K   917  
Introduces a methodology for authenticating user in cross domain/platform and transferring user data from one site to another during the authentication process.

Introduction

In my previous article, Single Sign-on in ASP.NET and Other Platforms, I mainly discussed single sign-on using forms authentication within a second level domain. As a follow-up, in this article, I’ll present a methodology to cover the single sign-on cross multiple second level domains/platforms. I’ll also discuss how customer data in your database can be transferred to another site during the authentication process. Let’s look at the following two sample URLs:

  • Site 1: http://www.authenticationsite.com/app1/default.aspx (ASP.NET site)
  • Site 2: http://www.thirdpartysite.net/app2/default.cfm (ColdFusion site)

Site1 is an ASP.NET application located at the domain authenticationsite.com while Site 2 is a ColdFusion application at the domain thirdpartysite.net. Authentication across these two sites can be challenging. First of all, these two sites cannot read each other’s cookie since they are on different second level domains, authenticationsite.com vs. thirdpartysite.net. Secondly, they are running on different platforms, ASP.NET vs. ColdFusion. Each has its own authentication mechanism.

In the following sections, I’ll illustrate the methodology that makes it possible to authenticate a user across domains, and at the same time transfer user data from one site to another.

Basic Program Logic

Create a centralized authentication application that consists of an ASP.NET application and an ASP.NET authentication web service. The user is always brought to this site to sign on. After being successfully authenticated, the user is then redirected to a third party site where the third party application calls the authentication web service to confirm the user’s login status and to retrieve a set of user data to update the third party database. To accomplish this, the user comes to the authentication site through a formulated login link. This link contains two QueryString parameters, SiteID and ReturnUrl. It looks like this:

http://www.AuthenticationSite.com/login.aspx?SiteID=1&ReturnUrl=http://www.Third PartySite.net/ LandingPage.aspx?Para1=xxx&Para2=yyy

SiteID is a parameter that the authentication site uses to determine web page presentation style as well as data transfer logic (what Stored Procedure to use for delivering data to a third party site). ReturnUrl represents a landing page of a third party site to which the user will be redirected to. Para1 and Para2 are sample parameters as part of the ReturnUrl, and are used by the third party site for its own purposes. ReturnUrl should be UrlEncoded() so that the whole string including parameters and any preserved characters like “?”, “=”, “&” is treated as one single parameter, like the following:

http://www.AuthenticationSite.com/login.aspx?SiteID=1&ReturnUrl= http%3a%2f%2fwww. ThirdPartySite.net%2fLandingPage.aspx%3fPara1%3dxxx%26Para2%3dyyy

Depending on what platform the third party application runs on, the landing page could be implemented in any applicable web technology, including ASP.NET. The authentication site obtains the SiteID and the ReturnUrl from the QueryString, and then presents the user, based on the SiteID, with a login page that has a graphic presentation resembling the third party site. The user enters his username and password, and clicks the Login button. Upon his credentials being successfully verified, the application saves the authentication session data including SiteID, UserID, authentication ExpirationDateTime, and ReturnUrl (can add more, if needed) into the database, and generates a unique AuthID representing the data saved, and appends the AuthID to the end of ReturnUrl. ReturnUrl would now look like:

http://www.ThirdPartySite.net/LandingPage.aspx?Para1=xxx&Para2=yyyy&AuthID=RD69usAsVDrAf56Hdd8R

The user is then redirected to the landing page of the third party site using the above ReturnUrl.

At the third party site, the landing page grabs the URL parameter AuthID from the QueryString and calls the authentication web service. The web service retrieves the authentication session data saved earlier, and checks ExpirationDateTime. A valid ExpirationDateTime signifies that the user has been authenticated. The web service then retrieves a set of user data from the database using a Stored Procedure determined by the SiteID, and returns the data to the third party application. The landing page inserts/updates user data in its own database, and performs necessary actions to programmatically log the user into its system.

Run Demo

To illustrate how the cross domain authentication works, I prepared a demo application for download. The solution contains two websites. One is the AuthenticationSite that provides a login interface to authenticate users as well as a web service for a third party to consume. The other is the ThirdPartySite that simulates a third party site. Although both sites are in the same solution and run on a local machine, the methodology is applicable to any site on a different domain/platform.

To run the demo, you’ll need SQL Server 2005 Express Edition (make sure that the service is running). It may be necessary to test its connection from Server Explorer in Visual Studio 2008. Microsoft Enterprise Library for data access is also required. However, the DLLs are included in the demo.

Open the solution in Visual Studio 2008. Expand the ThirdPartySite tree, right click on LandingPage1.aspx, and select View in the browser. When the page comes up, click the “Check Authentication Status” button. You’ll see that the user is not authenticated at this time. Close the browser. This action is to make the ThirdPartySite accessible by starting its local web server since it is a file system based application. Expand the AuthenticationSite tree. Right click the Default.aspx page and select View in the brower. Two links are now shown on the page. Looking at the HTML code for these two links, you'll see that one has a ReturnUrl that points to the Third Party #1 website and another to the Third Party #2 website. In the demo, they are not really two sites, but two pages, LandingPage1.aspx and LandingPage2.aspx, in the ThirdPartySite application. Please check the port number in the links, and make sure it is the correct number for the ThirdPartySite application.

Click the link "Third Party #1 Web Site", the login.aspx page shows up with a header image and a header text of Site1. Enter user name: johnd, and password: password (or janed/password, there are only two users in the customer table), and click the Login button. You are authenticated and redirected to the LandingPage1.aspx in the ThirdPartySite. The landing page calls the web service and pulls out John Doe’s data, and programmatically logs John into the ThirdPartySite. Click the “Check Authentication Status” button. This time, you’ll see that the user is authenticated. If you change the "AuthID" in the URL or leave the page open for over a minute and then refresh the page, you'll see a message indicating that the URL expired. Repeat the above steps with the Third Party #2 website link on the default.aspx page, you’ll see a different header image and header text, and will be redirected to LandingPage2.aspx after being authenticated.

AuthenticationSite Application

The AuthenticationSite application consists of a SQL Server database, an ASP.NET login page, a master page, an authentication class, a SiteInfo class, and a web service. Let’s take a closer look at each one of them.

Database

CustomerDB, a SQL database, is the backend data storage for the application. There are three tables, Customer, SiteInfo, and AuthenticationLog, and six Stored Procedures in the database. The Customer table holds the user contact information including login credentials. The SiteInfo table specifies style information for each third party site as well as a Stored Procedure name for pulling a set of customer data for data transfer. The AuthenticationLog table holds each authentication session data as a user logs in at the AuthenticationSite. This data will be retrieved by a third party application through a web service call to confirm a user’s authentication status.

The six Stored Procedures are described here:

  • Customer_Login - Verifies user credentials and returns CustomerID. More fields may be returned by editing this, if needed.
  • SiteInfo_GetSiteInfo - Retrieves style information based on SiteID, such as style sheet name, header image path, and header text, as well as a Stored Procedure name for data transfer.
  • Site1_GetCustomerInfo - Pulls a complete set of customer data for the third party #1 site. Note that there is only one Select statement in the Stored Procedure in this illustration. However, more statements (with Joins and conditions) can be added based on business requirements. Multiple Select statements will return multiple tables in a DataSet.
  • Site2_GetCustomerInfo - Retrieves customer data similar to the above for the third party #2 site.
  • AuthencationLog_insert – Upon successful authentication, save authentication session data into the AuthenticationLog table. This includes, SiteID, UserID, ExpirationDateTime, and ReturnUrl.
  • AuthencationLog_Get – The web service uses this procedure to retrieve authentication session data and confirm the user authentication status.

Classes

There are two classes in the AuthenticationSite application: SiteInfo and Authentication.

The SiteInfo class assists to set up the presentation of the login page based on SiteID. This class is straightforward without a need for explanation.

The authentication class does most of the authentication work. The VerifyCredentials method performs user credential check, and returns the user's identity in a DataTable. The number of fields returned is based on the stored procedure - Customer_Login. In our example, only UserID (CustomerID) is returned. The WellFormReturnUrl method creates a well formed ReturnUrl by properly appending an AuthID to the original ReturnUrl. The RetrieveUserDataSet method pulls back a complete set of user data for transferring to a third party. The argument siteID determines what stored procedure to use. The code in each method is shown below:

C#
public static DataTable VerifyCredentials(string userName, string password)
{
    //confirm credentials. upone success, return a single 
    //record in a DataTable for this user
    return ExecuteDataSet("Customer_Login", 
           new object[] { userName, password }).Tables[0];
}

//The return url to be used to send user back to a third 
//party site needs to be parsed to add the AuthID properly
public static string WellFormReturnUrl(string originalReturnUrl, 
                                       string authID)
{
    string WellFormedUrl = "";
    //check if the original return url has parameters attached already.
    //encryptedParameter has to be UrlEncoded.
    int Position = originalReturnUrl.IndexOf("?");
    if (Position != -1)
    {
        //? exists. original url has some parameters already,
        // append the ecryptedParameter to the end with a "&"
        WellFormedUrl = originalReturnUrl + "&AuthID=" + 
                        HttpUtility.UrlEncode(authID);
    }
    else //original url does not have any parameters, append AuthID with "?"
    {
        WellFormedUrl = originalReturnUrl + "?AuthID=" + 
                        HttpUtility.UrlEncode(authID);
    }
    return WellFormedUrl;
} 

//this method retrieve a complete set of user data that a third party app needs
public static DataSet RetrieveUserDataSet(int siteID, string userID)
{
    //siteID determines storedproc name. 
    DataSet ds = ExecuteDataSet(GetDataTransferProc(siteID), new object[] { userID });
    return ds;
}

The rest of the methods in the Authentication class do data manipulation and interaction with the database. Here, the authentication session data is first placed into a name value collection using the BuildUserDataCollection method. This makes it easy to retrieve any name value pair from the authentication session data. The data is then serialized into one single string by calling the SerializeParameters method, and is saved into the AuthenticationLog table. By using serialization, more data pairs can be added, if needed, without modifying the table structure. Retrieving the authentication session data is a reverse action that creates a user data collection through deserializing the saved data string by calling DeserializeUserData.

Master Page

The graphic presentation style of the authentication site is achieved using a Master page, AuthMaster.master. The style is dynamically applied based on SiteID so that the authentication site looks similar to its target third party site. In the demo, the style is relatively simple. However, it does illustrate the methodology for more sophisticated graphic presentation.

There are two style sheets, two header images, and two header texts which match two third party sites in the demo application (actually two landing pages), respectively. The code listed below is the function that applies styles in the AuthMaster.master page. The variable TableWidth controls the display width of the page, and is directly placed on the master page in the HTML table tag, and therefore, DataBind() needs to be called to bind the variable. The rest is either an ASP.NET server control (for example, imgHeader and lblHeaderText) or HTML server control (for example, MainStyle). Their properties change with SiteID.

C#
private void ApplySiteStyle(SiteInfo siteInfo)
{
    MainStyle.Href = siteInfo.StyleSheetName;
    imgHeader.ImageUrl = siteInfo.HeaderImagePath;
    imgHeader.Width = Unit.Pixel((int)siteInfo.HeaderWidth);
    imgHeader.Height = Unit.Pixel((int)siteInfo.HeaderHeight);
    lblHeaderText.Text = siteInfo.HeaderText;
    TableWidth = (imgHeader.Width == 0 ? "800" : 
                  imgHeader.Width.ToString());
    DataBind();
}

Login page

The login page basically does three things during a user authentication process.

  • Calls the VerifyCredentials method in the Authentication class to check user credentials against the database, and returns the UserID in a DataTable. The reason that a DataTable is used here is to give the flexibility that more fields can be returned by modifying the Customer_Login Stored Procedure;
  • Serialize the authentication session data including SiteID, UserID, ExpirationDateTime, and ReturnUrl, and then generate an AuthID and save the information into the AuthenticationLog table. The ExpirationDateTime is set to be 1 minute from the execution date time, indicating that the AuthID will expire in 1 minute from the time the user credentials are verified;
  • Append the AuthID to the end of the ReturnUrl and redirect the user to a third party site.

The SiteID and ReturnUrl are requested in the Page_Load event, and their values are assigned to two invisible labels, lblSiteID and lblReturnUrl (Label.Visible=false). They are used when the Login button is clicked. The code in the Login button click event is self explanatory, as shown below:

C#
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        lblReturnUrl.Text = (Request.QueryString["ReturnUrl"]??"").ToString();
        lblSiteID.Text = (Request.QueryString["SiteID"] ?? "").ToString();
        if (lblReturnUrl.Text == ""||lblSiteID.Text=="")
        {
            lblError.Text = "The ReturnUrl or SiteID is missing. Can't proceed.";
            btnLogin.Enabled = false;
            return;
        }
        txtUserName.Focus();
        //provide testing accounts for user to log in
        lblError.Text = "Login credentials: user: johnd    " + 
                        "pw: password or user: janed   pw: password";
    }
}

protected void btnLogin_Click(object sender, EventArgs e)
{
    try
    {
        DataTable tbl = Authentication.VerifyCredentials(txtUserName.Text, 
                                                         txtPassword.Text);
        
        if (tbl.Rows.Count>0)
        {
            //Authentication will expire in 1 minute. Can be set to other values
            int MinutesToExpire = 1;
            ProcessAuthenticationData(tbl, MinutesToExpire, 
                   Convert.ToInt16(lblSiteID.Text), lblReturnUrl.Text);

        }
        else
        {
            lblError.Text = "No user with these credentials has been found.";
        }
    }
    catch (Exception ex)
    {
        lblError.Text = ex.Message.ToString();
    }

}

private void ProcessAuthenticationData(DataTable tbl, 
        int minutesToExpire, int siteID, string returnUrl)
{
    //place user data into a collection which is easy to handle
    NameValueCollection UserData = Authentication.BuildUserDataCollection(tbl, 
                                   minutesToExpire, siteID, returnUrl);

    //build all data into a text string so that it can be stored in database
    string UserString = Authentication.SerializeParameters(UserData);

    //Save querystring parameter and user data string
    string AuthenticationID = Guid.NewGuid().ToString().Replace("-", "");
    //save user information. When the third party app calls web service,
    // this information will be retrieved
    Authentication.ExecuteNonQuery("AuthenticationLog_Insert", 
                   new object[] { AuthenticationID, UserString});
    
    Response.Redirect(Authentication.WellFormReturnUrl(lblReturnUrl.Text, 
                      AuthenticationID));
}

Web Service

AuthenticationService.asmx is a web service for the landing page of a third party to consume in order to confirm a user’s authentication status and to transfer user data. Upon receiving the AuthID (Request.QueryString[“AuthID”]), the third party landing page calls the RetrieveUserDataSet web method (or any other web method as needed) with the AuthID to retrieve a complete set of user data. During this process, if the AuthID is expired or tampered with, the web service call will fail.

The web service provides three web methods, as described below.

  • RetrieveUserDataSet – the AuthID is passed. Upon success, returns a set of user data as a DataSet. Upon failure, returns null, and sends back an error message in the reference parameter returnMessage.
  • RetrieveUserDataXml - Does the same thing as the above, but returns a set of user data as a serialized XML string.
  • RetrieveUserID - The AuthID is passed in. Upon success, returns UserID as a string. Upon failure, returns an empty string, and sends back an error message in the reference parameter returnMessage. If you do not need user data for transfer, but need only to confirm a user’s authentication status, this is the method to call.

The RetrieveUserDataSet and RetriveUserDataXml web methods return exactly the same data. The reason that two methods are exposed here is to accommodate non .NET third party applications in which a DataSet is not a recognized data type. Similarly, an error message is sent back to the caller through a reference parameter ReturnMessage, instead of throwing an exception for the same reason. Depending on what platform a third party uses, more variation of methods may be needed to return data in the right formats for a particular third party application to consume. In the demo, only RetrieveUserDataSet is called. More web methods are listed here to illustrate the flexibility in the program development.

Third Party Site

The application ThirdPartySite simulates a remote third party site as the target that a user is redirected to upon successful authentication. As mentioned before, a landing page at the ThirdPartySite receives an AuthID from the URL, and then consumes the authentication web service which returns a complete set of user data. The third party application then processes the data returned and updates its database, and then programmatically logs the user into its site. If the web service call fails because of either the AuthID being expired or tampered with, an error message is returned to the third party application. As a demo, the user data returned to the third party is not saved into a database, but displayed in a GridView.

A web reference pointing to AuthenticationSite/AuthenticationService.asmx in the same solution is added to the site and named as AuthenticationService. There are two landing pages in the application, simulating two different third party sites. The code in LandingPage1.aspx and LandingPage2.aspx is exactly the same in the demo app. Therefore, we only need to take a look at LandingPage1.aspx. Looking at the code listed below, the page gets the AuthID from the QueryString, and requests for Parameter1 and Parameter2, which are used by the third party for its own purpose. For the demo, any code involving Parameter1 and Parameter2 are omitted. The page then declares an instance of the AuthenticationService and calls the RetrieveUserDataSet web method. If a DataSet is returned, signifying the success of the cross domain authentication, this page performs necessary actions to handle the user data, and then logs the user in programmatically. Otherwise, authentication fails, and an error message is displayed.

C#
//request for the AuthenticationID
string AuthenticationID = Request.QueryString["AuthID"];
if (AuthenticationID == null)
{
    lblError.Text = "A required parameter is missing from url. ";
    return;
}
//Request p1 and p2 from Url. p1 and p2 
//are the parameters that the third party app needs
string p1 = Request.QueryString["Para1"].ToString();
string p2 = Request.QueryString["Para1"].ToString();
//additional code here to process the parameters

//Add a web reference to your app and name it anything you like.
//Here it is named as AuthenticationService. 
//declare web service and a reference variable - ReturnMessage
AuthenticationService.AuthenticationService AuthService = 
                      new AuthenticationService.AuthenticationService();
string ReturnMessage = "";
DataSet ds = null;
//Call Web Method: RetrieveUserDataSet
//success: user authenticated, get a DataSet. 
//Failure: user not authenticated or Url exipred. Return null and error message.
try
{
    ds = AuthService.RetrieveUserDataSet(ref ReturnMessage, AuthenticationID);
}
catch (Exception ex)
{
    lblError.Text += ex.Message.ToString();
}

if (ReturnMessage != "")
{
    lblError.Text += ReturnMessage;
    return;
}
if (ds != null)
{
    //depending on the stored procedure used to retrieve the data
    //The DataSet can contain multiple tables 
    //Write code here to loop through the DataSet
    //insert or update the third party database
    //and then programmatically log in the user 
    //by setting up appropriate cookies and session variables
    //For asp.net form authentication, call SetAuthCookie method
    FormsAuthentication.SetAuthCookie("LoginUser", false);

    //display user data for demo purpose
    gvUserData.DataSource = ds.Tables[0];
    gvUserData.DataBind();
}
else
{
    lblError.Text += ReturnMessage;
}

Summary

I have discussed the methodology for cross domain/platform authentication and data transfer. The demo application is a greatly simplified version in order to illustrate the concept. In real world, at least several additional parts have to be in place, such as “Remember Me”, “Forgot My Password”, “Sign up” etc. In addition, the accessibility of the web service should be restricted to third parties that you have a partnership with. Depending on the programming platform that a third party uses, more variations of a web method may be required to return data in an appropriate format (for example, a string array, or a special character like "|" separated string, etc.) that the third party can process.

This article is based on my previous work published on aspalliance.com, Cross Site Authentication and Data Transfer. Since then, passing a long encrypted data string through URL is no longer used, but a database table is created to keep track of user authentication session data. This approach reduces the complexity of program implementation, and minimizes the possible side effects related to encryption and long URL.

Reference

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)