During SharePoint deployments, one of the requests I get from time to time is how to leverage Single Sign On (SSO) capabilities to automatically sign in to external web applications. For example, perhaps you have an web-based time keeping system that your company uses. Every time you log into the system, you must supply the obligatory username and password in the login like this sample form here:

Figure 1: Typical forms authentication web page
Most organizations have many of these internal or external applications where this is an inconvenience. The annoying part is that you might be in an out of the system several times a day, and each time you must log in. Wouldn’t it be great if you could host a page within SharePoint that automatically signs you in to these systems? Well, that’s exactly what we’ll do in this article.
Let’s first see how it looks.
Taking our Time Reporting System system, here is how our Time Reporting System looks like within a custom developed Web Part. This is the initial view as credentials are pulled from the SSO database and passed to the time reporting application.

Figure 2: Custom Web Part – automated sign in
Once authentication is complete, you are presented with the default page for the application:

Figure 3: Custom Web Part – login complete
The concept here is not much different than the Page Viewer Web Part. The one big difference, however, is the automatic login. Here are two other examples using ebay and Amazon (click images to enlarge):

Figure 4: Automatic login to ebay
Figure 5: Automatic login to Amazon
Solution Architecture
The basic idea is that the custom code generates an HTML form that is then programmatically posted using JavaScript to the application’s login page. The user’s credentials, which are pulled from the Single Sign On database, are embedded into the form. Here is a sample HTML form that is generated:
<HTML>
<body onload='document.login.submit();'>
<form name='login' id='login' method='post' action='http://localhost/login.aspx'>
<input type='hidden' name='txtUsername' id='txtUsername' value='testuser' />
<input type='hidden' name='txtPassword' id='txtPassword' value='password' />
</form>
</body>
</HTML>
As you can see, this form contains the username called testuser that is automatically passed to the login.aspx page. Depending on the web application, there may be additional hidden variables that may be needed. I’ll address this in just a bit.
The custom code for this solution consists of a Web Part and a SharePoint application (.aspx) page. The Web Part is basically a shell that generates an IFRAME (just like the Page Viewer Web Part) and sets the source (i.e. src attribute) to the application page. The application page (stored within the _layouts virtual directory) is what calls into the SSO database, pulls the current user’s credentials and generates the HTML form. The Web Part also contains a few shared personalization settings as shown here:
Figure 6: Web Part settings
These settings are delivered from the Web Part to the application page via the querystring. As you can see, they make the Web Part more generic so that is can be used across a number of systems.
Solution Code
Now that you have an idea of how the solution works, let’s take a look at some code. We’ll start with the Web Part, but there is not much to look at here. Let’s start by introducing one of the six personalization settings. This one shown here is for the login URL:
[Personalizable(PersonalizationScope.Shared),
WebBrowsable(true),
WebDisplayName("Enter URL for the Login page"),
WebDescription("Enter URL for the Login page")]
public string Url
{
get { return _url; }
set { _url = value; }
}
Here is the code within CreateChildControls.
//Generate querystring to pass data to application page
StringBuilder querystring = new StringBuilder();
querystring.Append("?url=" + HttpContext.Current.Server.UrlEncode(_url));
querystring.Append("&uc=" + HttpContext.Current.Server.UrlEncode(_userIdControlName));
querystring.Append("&pc=" + HttpContext.Current.Server.UrlEncode(_passwordControlName));
querystring.Append("&msg=" + HttpContext.Current.Server.UrlEncode(_errorMessage));
querystring.Append("&app=" + HttpContext.Current.Server.UrlEncode(_ssoApp));
if (_ssoHiddenVars.Length > 0)
querystring.Append("&hid=" + HttpContext.Current.Server.UrlEncode(_ssoHiddenVars));
//Generate iframe src for pointing to SSO.aspx application page
StringBuilder html = new StringBuilder();
html.Append(@"<iframe src='/_layouts/SSODemo/SSO.aspx" + querystring.ToString() + @"' id='frame1'
width='100%' height='100%'>Your browser does not support iframes</iframe>");
this.Controls.Add(new LiteralControl(html.ToString()));
As you can see, the six personalization settings are delivered to the SSO.aspx application page via the querystring. The first five are pretty self-explanatory, but let me introduce the last one since that one is not so obvious.
Many systems (like Amazon and ebay) require additional form variables to be present when authenticating the user. These control things like whether you should automatically remain logged in or which page should you be redirected to after login. Of course, each system is different. Without providing this necessary input, the login process may fail. To address this, the sixth setting allows you to enter in any custom form variables that are needed. These will be converted into hidden html controls.
The only way to know which of these variables you need is to take a look at the HTML source page for a login screen. From here you can determine which variables you need. You just need to enter them in one at a time separated by a pipe (|) character. For example, to correctly log into to Amazon, I must pass these additional values: useRedirectOnSuccess=1|path=/gp/css/homepage.html|action=sign-in|protocol=https. This line is then translated into this html which is included in the form that is automatically submitted:
<input type='hidden' name='useRedirectOnSuccess' id='useRedirectOnSuccess' value='1' />
<input type='hidden' name='path' id='path' value='/gp/css/homepage.html' />
<input type='hidden' name='action' id='action' value='sign-in' />
<input type='hidden' name='protocol' id='protocol' value='https' />
Let’s now take a look at the SSO.aspx application page. It’s more interesting than the Web Part. This page is what generates the HTML form that is posted to the web application. When the page initializes, it loads the six settings as shown here in the Page_Load event:
//load querystring values passed over from web part
string url = Request.QueryString["url"];
string userCtrl = Request.QueryString["uc"];
string passCtrl = Request.QueryString["pc"];
string errorMsg = Request.QueryString["msg"];
string ssoApp = Request.QueryString["app"];
string hiddenVars = Request.QueryString["hid"];
From here, I have the code to pull the user’s credentials and generate the first part of our HTML form:
//Pull credentials for this user from SSO database
GetCredentials(ssoApp, out username, out password);
//Generate HTML form
StringBuilder html = new StringBuilder();
html.Append(@"
<HTML>
<body onload='document.login.submit();'>
<form name='login' id='login' method='post' action='" + url + @"'>
<p/><p/><p/>
<center>
Logging in...
<br/>
<br/>
<img src='/_layouts/images/GEARS_AN.GIF' />
<input type='hidden' name='__VIEWSTATE' id='__VIEWSTATE' value='' />
<input type='hidden' name='" + userCtrl + "' id='" + userCtrl + "' value='" + username + @"' />
<input type='hidden' name='" + passCtrl + "' id='" + passCtrl + "' value='" + password + @"' />");
The second part is where the extra hidden HTML controls are created along with the closing part of the form.
//generate out additional hidden form variables
if (hiddenVars != null)
{
foreach (string str in hiddenVars.Split('|'))
{
//split out key and value pair within variable
string[] var = str.Split(new char [] {'='},2);
html.Append("<input type='hidden' name='" + var[0] + "' id='" + var[0] + "' ");
html.Append(" value='" + var[1] + "' />");
}
}
html.Append(@"</center>
</form>
</body>
</HTML>");
this.Controls.Add(new LiteralControl(html.ToString()));
In the first part above, you’ll notice that that I have a helper method called GetCredentials. This is where the user’s actual username and password are pulled from the SSO database. Here is the code:
public void GetCredentials(string ssoApp, out string username, out string password)
{
//connect to default SSO provider
ISsoProvider provider = SsoProviderFactory.GetSsoProvider();
SsoCredentials creds;
try
{
//get credentials for currently logged in user
creds = provider.GetCredentials(ssoApp);
}
catch (SingleSignonCredsNotFoundException ex)
{
string msg = "User " + HttpContext.Current.User.Identity.Name;
msg += " does not have credentials stored in the Single Sign On database";
throw new Exception(msg, ex);
}
//convert credentials into a string
IntPtr pUserName = IntPtr.Zero;
IntPtr pPassword = IntPtr.Zero;
try
{
//NOTE: After this has been converted to a String object, it remains in
//memory until the garbage collector collects it.
pUserName = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(creds.UserName);
username = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(pUserName);
pPassword = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(creds.Password);
password = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(pPassword);
}
finally
{
// Free zero out and free the BSTR pointers.
if (pUserName != IntPtr.Zero)
{
System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(pUserName);
}
// Free zero out and free the BSTR pointers.
if (pPassword != IntPtr.Zero)
{
System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(pUserName);
}
}
}
This code was only slightly modified from the sample code found on MSDN
here. There is additional code in other sections to handle errors, but I’ll leave that to your study if you download the code.
Configuring Single Sign On
Of course, all of this code requires that you have the SSO service installed with an application configured. I’ll not go into detail on how to do this and instead direct you to this TechNet article and a nicely summarized list of steps from Dave Wollerman’s blog. Once the SSO service is configured and a SSO database has been created, you need to create an application definition. This is done through the third link in the Manage Single Sign-On Settings as shown here highlighted in red:

Figure 7: Setting up an application definition in SSO
This takes you to this screen where you set up each of your application definitions. Each definition will contain username and password combinations for a specific system. In this screen shot, I have three set up for Amazon, ebay and Time Reporting.
Figure 8: List of Application Definitions
Once the Application Definition is configured, you need to populate the SSO database with credentials for each user. SSO works by using a mapping system where the user’s SharePoint credentials map to a username and password stored in the SSO database. You have two basic ways to populate the SSO database with these credentials. One way is to manually add username and passwords using these Manage SSO screens. This is done with this link shown highlighted in red:

Figure 9: Storing credentials in SSO for a user
You then choose the Application Definition and enter in the SharePoint user account name and click the Update account information radio button as shown here:

Figure 10: Choosing the Application Definition and SharePoint user
Finally, this will then take you to this screen where you can store the username and password for this user:

Figure 11: Storing the new username and password within SSO
This option works well for just a few users; however, it gets tedious if you’ll be adding lots of them. Also, you must also edit them if the external system’s credentials ever change.
For the second way of storing credentials in the SSO database, I won’t cover that in detail this article. If there is enough interest, I’ll cover this in a follow up blog. The basic idea, though, is that if credentials for the external system do not exist, you prompt the user to provide the username and password. These are then securely stored in the SSO database. This could all be done within the custom application page. A similar design would be used when the credentials have changed and you need to store the current ones.
Caveats
One drawback to this design is that it requires the web application that you are signing into to allow a form to be directly posted without validation. This can be a problem with some applications, especially newer ASP.NET systems. If you have control over the web application, you may be able to disable this by using the <pages enableEventValidation=”false”> setting in Web.Config. I cannot promise that this solution will work in all situations, however.
For ASP.NET web applications, you may still need a __VIEWSTATE form variable present. You can add this to the additional form variables section when configuring the Web Part.
Some systems log you in using the credentials passed in the querystring to the web application. This is not currently supported in this code, but you could download the source and easily modify it to support this feature.
There are issues if the browser’s security zone for the SharePoint site and for the web application mismatch. For example, if the SharePoint site is in the trusted zone, but the web application is in the internet zone. This solution works best if both sites are in the same zone.
Downloading the Code
I have developed this solution using VS 2008 with Visual Studio Extensions for WSS (VSeWSS) v1.2. The Web Part and application page are stored in one project and is deployable through a single WSP solution.
Click here for those interested in downloading the source code.
Click here for those only interested in the WSP solution. You will need to deploy this using STSADM commands.
Conclusion
In this article, you have seen how to develop a custom Web Part and application page that can automatically log users in to their commonly used web applications. The advantage to this design is that you can use one Web Part for multiple applications. In this article, I have the one Web Part used to auto-authenticate into the Time Reporting System, ebay, and Amazon.
Hopefully this will simplify some of your web application access and also encourage staff to use SharePoint as a single portal for all application needs. Enjoy.