Advanced server-side authentication for data connections, part 2

In the first part of this series I introduced the concepts involved in three tier authentication. Now let's drill into the details and work with some code.

 

Using Office Single Sign-on with individual mapping

 

An Office Single Sign-on (SSO) application definition, can be set up in one of three ways: individual, group, or group using restricted credentials. An InfoPath form published to the server can use either of the first two. However, in order to use individual credentials, the SSO database must contain a separate username and password for each user who connects to the form. A typical server application would check whether the connecting user had credentials in the database, and redirect to a credentials management page when appropriate to allow the user to enter their credentials. While InfoPath Forms Services does not do this generically for forms that use SSO, it’s possible to implement a solution for an individual form or a set of forms the same way an Office Server application would, by using the Pluggable SSO API.

 

The general approach is to have users link to an ASP.NET page rather than directly to the form. The ASP.NET page attempts to get the user’s credentials from the SSO database, and based on the result either redirects to the form or to a credential management page which allows the user to input their credentials.

 

Enough talk. Let’s get our hands dirty.

 

My code assumes that you have defined an SSO application definition called MyApp and that the application is set up to use individual credentials. I also assume that that have an administrator-approved form template called myform.xsn activated on the site collection, and that the form uses one or more data connections that reference the MyApp application.

 

In order to use SSO to make the data connection, you must be using data connection settings in a UDC file on the server. To add the reference to the MyApp app definition, you add an Authentication element to the UDC file. The authentication element must be the last subelement of the ConnectionInfo element, and it looks like this:

 

<udc:Authentication>

  <udc:SSO AppId="MyApp" CredentialType="NTLM"></udc:SSO>

</udc:Authentication>

 

To create the redirector page, the first thing is to add a new ASP.NET page to the root site of your SharePoint server. Find the home directory of your root site by opening up Internet Services Manager and checking the Home Directory page of the properties page for the web application. Open this web in Visual Studio and add a new ASP.NET page. In the properties for the page, enable session state. Finally, add a reference to the Microsoft.SharePoint.Portal.SingleSignon namespace.

 

Once this is done, the new page should look like this:

 

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SingleSignonRedirector.aspx.cs" Inherits="SingleSignonRedirector" EnableSessionState="True" %>

<%@ Import Namespace="Microsoft.SharePoint.Portal.SingleSignon" %>

 

Open the code file for the page, and add a using statement for the SingleSignon namespace

 

using Microsoft.SharePoint.Portal.SingleSignon;

 

The code itself is relatively straightforward. Start by using the pluggable API to get a reference to the installed SSO provider.

 

ISsoProvider provider = SsoProviderFactory.GetSsoProvider();

 

(this call will fail if no provider is installed, or if the Single Signon service is not running)

 

Your page will run under the credentials of the user attempting to access the form, so when you call GetCredentials, SSO will return a credential for that user, or throw SingleSignonCredsNotFoundException if the credential does not exist.

 

try

{

   SsoCredentials credentials = provider.GetCredentials("MyApp");

}

catch (SingleSignonCredsNotFoundException)

 

In the event that the credentials are not found, you can get the URL of the credentials management page for the installed provider.

 

Uri CredentialsManagementUrl = provider.GetCredentialManagementURL("MyApp");

 

Now, at this point you could simply redirect to the credentials management URL. However, you would miss part of the magic of the credentials management page. The credentials management page supplied with the Office single Sign-on provider will redirect back to the referring page once the user has entered valid credentials. I chose to handle this by presenting a link to the user to allow them to click through to the credentials management page.

 

Response.Write("<P>Before you can access this form, you must enter credentials to access backend data.</P>");

Response.Write("<P><A href="" + CredentialsManagementUrl.ToString() + "">Click here to enter your credentials</A></P>");

Response.End();

 

An alternate approach could be to add the HTTP referer header to the response before performing a redirect. Either way, once the user has entered their credentials, the redirector page will be called again. This time the GetCredentials call will succeed, and the page will redirect on to the form.

 

Here’s the complete code-behind for the page:

 

using System;

using System.Data;

using System.Configuration;

using System.Collections;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Web.UI.HtmlControls;

using Microsoft.SharePoint.Portal.SingleSignon;

 

public partial class SingleSignonRedirector : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        ISsoProvider provider = SsoProviderFactory.GetSsoProvider();

        try

        {

            SsoCredentials credentials = provider.GetCredentials("MyApp");

        }

        catch (SingleSignonCredsNotFoundException)

        {

            Uri CredentialsManagementUrl = provider.GetCredentialManagementURL("MyApp");

            Response.Write("<P>Before you can access this form, you must enter credentials to access backend data.</P>");

            Response.Write("<P><A href="" + CredentialsManagementUrl.ToString() + "">Click here to enter your credentials</A></P>");

            Response.End();

        }

        catch (Exception ex)

        {

            Response.Write("<P>Single Sign-on returned an error: " + ex.Message + "</P>");

            Response.End();

        }

 

        // Good to go - on to the form

Response.Redirect( "http://myserver/formservertemplates/myform.xsn?openin=browser" );

       

    }

}

 

Getting jiggy with it

For a real-world application, there are a few more things I might do to this page to round out the implementation. Depending on the needs of the specific application, one or more of the following might apply: