In the existing SharePoint Server and TAMeb integration, there is a known issue whereby forms based authentication on WebSEAL breaks the Client integration feature of Microsoft Office Products. This is a direct result of the Microsoft Office application suite not supporting authentication types other than SPNEGO or BA, and further affected by regular session cookies not being passed between Internet Explorer and other applications.
An overview of the architecture for Tivoli Access Manager WebSEAL integrated with a SharePoint Server is illustrated in Figure 1.
Figure 1. Architecture of the Tivoli Access Manager SharePoint Services integration using Tivoli Access Manager WebSEAL
The diagram above shows an integration architecture that supports the following processes:
- A browser request to SharePoint Services is submitted through WebSEAL.
- WebSEAL intercepts the request, authenticates and authorizes the user as required.
- WebSEAL forwards the request to SharePoint Services along with the authenticated user-id in a HTTP header, called iv-user.
- IIS authenticates the request and executes the Tivoli Access Manager IIS impersonation extension. The extension reads the iv-user HTTP header and impersonates the user-id contained in the header.
- SharePoint Services returns the requested content, based on the impersonated user-id.
- The content is returned to the browser. WebSEAL performs filtering as appropriate for the junction method.
More details on the existing SharePoint and TAMeb integrations can be found at the following location:
- IBM Tivoli Access Manager Microsoft SharePoint Services Integration Adapter
- IBM Tivoli Access Manager Microsoft IIS Integration
Note: Completing the above Single Sign-On integrations are a compulsory pre-requisite for the remainder of this article.
In the existing integration when opening an Office document from a SharePoint server with WebSEAL forms authentication enabled, the Office application displays the WebSEAL forms login page as the document content (See Figure 2), and there is no way to manipulate the form to submit the authentication information.
Figure 2. Forms Authentication in a Microsoft Word® document.
This occurs when the ActiveX control is used to activate call the application and the file is downloaded through it rather than Microsoft Windows® Internet Explorer®. This does not occur when using Firefox or when Client Integration is turned off since the ActiveX control is bypassed and the file is simply downloaded via the browser session and then opened by the application from the local disk.
In order for the client integration to work when using forms authentication, it's necessary to implement the following components:
- The WebSEAL External Authentication C API Component
- The Session Management Application
- The AJAX Cookie Request Wrapper
The details of these components is are discussed in the following sections and there interaction is shown in Figure 3.
The WebSEAL External Authentication C API Component
The WebSEAL CDAS component uses the WebSEAL Authentication C API to convert the persistent session key cookie into a TAM user identity. It does this via an HTTP request to the backend session manager running on the SharePoint Server. This component is installed and run on the WebSEAL server.
Session Management Application
The Session Management Application is responsible for the management of the session keys. It provides a HTTP interface for the generation and consumption of the keys. The keys are maintained in a database for fast efficient access and for a high level of scalability. In addition, the Session Management Application was implemented using Active Server Page (ASP) technology for simplicity. This component is installed and run on the SharePoint Server.
The AJAX component is a wrapper to the existing JavaScript that calls the Office Applications ActiveX control. It performs a synchronous XMLHttpRequest to the ASP application requesting a one time use persistent Session Cookie. This cookie is made available to the office application through the Microsoft Cookie Jar. This component is installed on the SharePoint Server and run on the client host.
Figure 3. Component Interaction Diagram
Implementation of the WebSEAL External Authentication C API Component
This section outlines the steps required to implement the authentication component using the External Authentication C API that extracts and validates the persistent session key cookie.
The complete source code of this prototype implementation has been provided with this article in the downloads section.
During the authentication service initialization phase, the ASP application path must be loaded and initialized. The parameters required for initializing the libraries should be passed to the authentication service xauthn_initialize() function.
Listing 1 provides a sample code snippet illustrating the gathering of required parameters and library initialization.
Listing 1. Fragment of the xauthn_initialize() function
xauthn_status_t
xauthn_initialize(
int argc, /* in */
const char **argv /* in */
)
{
if (argc > 0)
{
CONSUME_COOKIE_URL = malloc(strlen(argv[0]));
if (CONSUME_COOKIE_URL == NULL)
{
return XAUTHN_S_OUT_OF_MEMORY;
}
strcpy(CONSUME_COOKIE_URL, argv[0]);
}
else
{
printf("CDAS XAUTHN - Init Failed, No Cookie URL provided.\n", argc);
return XAUTHN_S_FAILURE;
}
printf("\nCDAS XAUTHN - Init Complete\nUsing path: %s\n\n", CONSUME_COOKIE_URL);
return XAUTHN_S_COMPLETE;
}
|
The service authentication function is where the persistent cookie contained within the unauthenticated request is validated against the session database managed by the session management application. The session management application returns a username matching the persistent cookie to the WebSEAL authentication mechanism.
The following example just adds the value of the persistent cookie to a HTTP header and sends the request using libcURL. The libcURL library must be accessible by WebSEAL in order to work, and the C header files are necessary in order to build the component.
Listing 2 provides a sample snippet illustrating the authentication phase validating the
session key against the IIS Web Application via the function getUsername()
which is supplied in Listing 3.
Listing 2. Fragment of the xauthn_authentication function
xauthn_status_t xauthn_authenticate(
xnvlist_t *authnInfo,
xauthn_identity_t *ident
)
{
...
if (authnInfo != NULL)
{
int nCookieLength = COOKIE_BUFFER_LEN;
char** name = NULL;
/* Init the Cookie variable */
spsCookieValue = (char*)malloc(COOKIE_BUFFER_LEN);
if (spsCookieValue == NULL)
{
st = XAUTHN_S_OUT_OF_MEMORY;
goto exception;
}
memset (spsCookieValue, 0, sizeof(COOKIE_BUFFER_LEN));
/* Get the value of the cookie */
if ( XAUTHN_S_COMPLETE !=
(st = xnvlist_get(authnInfo, DEFAULT_COOKIE_NAME,
&spsCookieValue, &nCookieLength)) )
{
printf ("Failed to get the value of the cookie.\n");
goto exception;
}
printf("Sharepoint Cookie Value Found: %s\n", spsCookieValue);
/* Convert the Cookie into A Username, and expire the Cookie in DB. */
name = &ident->prin.data.dn;
username = (char *)malloc(USERNAME_BUFFER_LEN);
if (username == NULL)
{
st = XAUTHN_S_OUT_OF_MEMORY;
goto exception;
}
memset (username, 0, sizeof(username));
/* Get the username from IIS server application */
if (0 != strlen(spsCookieValue) && getUsername(username, spsCookieValue))
{
/* Name Retrieved */
}
else
{
printf ("Failed to get the value of the username.\n");
goto exception;
}
printf("XAUTHN: Authenticated username: '%s'\n\n", username);
/* Set the identities username */
*name = (char*)username;
ident->prin.prin_type = principalType;
/* Set the result code to complete */
st = XAUTHN_S_COMPLETE;
}
...
}
|
Listing 3 is the code fragment that uses libCURL to access the session management application as specified in the initialization (Listing 1) to validate the cookie.
Listing 3. Fragment of the getUsername function
int getUsername(
char* username, /* empty buffer for username */
char* sessionValue /* in */
)
{
CURL * easyhandle = NULL;
CURLcode curl_rc = 0;
char* cookies;
int ret = 0;
struct MemoryStruct chunk;
chunk.memory = NULL; /* we expect realloc(NULL, size) to work */
chunk.size = 0; /* no data at this point */
/* Init CURL */
curl_global_init(CURL_GLOBAL_ALL);
easyhandle = curl_easy_init();
if (easyhandle == NULL)
{
printf("ERROR: Curl Handle didn't Init.\n");
}
/* Generate Cookie Values */
cookies = (char*)malloc(COOKIE_BUFFER_LEN + DEFAULT_COOKIE_NAME_LENGTH + 3);
if (cookies == NULL)
{
printf("XAUTHN: ERROR - Cookie Malloc Failed\n");
return 0;
}
memset (cookies, 0, sizeof(COOKIE_BUFFER_LEN + DEFAULT_COOKIE_NAME_LENGTH + 3));
strncpy(cookies, DEFAULT_COOKIE_NAME, COOKIE_BUFFER_LEN);
strcat(cookies, "=");
strcat(cookies, sessionValue);
strcat(cookies, ";");
/* Prepare Curl for request */
if (CONSUME_COOKIE_URL == NULL)
{
printf("XAUTHN: ERROR - Curl Cookie URL is null.\n");
return 0;
}
curl_easy_setopt(easyhandle, CURLOPT_URL, CONSUME_COOKIE_URL);
curl_easy_setopt(easyhandle, CURLOPT_COOKIE, cookies);
curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(easyhandle, CURLOPT_HEADER, 1);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, (void *)&chunk);
curl_rc = curl_easy_perform(easyhandle);
curl_easy_cleanup(easyhandle);
if (curl_rc == CURLE_OK)
{
/* chunk.memory contains full response */
ret = extractUsername(&chunk, username);
if(chunk.memory)
free(chunk.memory);
}
else
{
printf("XAUTHN: ERROR - cURL Request, Error Code %d. See curl.h.\n", curl_rc);
}
return ret;
}
|
Configuring the WebSEAL environment
This section outlines the process required to configure the solution environment in order to allow WebSEAL to accept and process the persistent cookie.
This component utilizes the cURL Library for HTTP requests, you must download the library binary from the
cURL website: http://curl.haxx.se/. If you are going to build
the component using the source provided, you will also need the header files, and they should be extracted to
<build_directory>/include/curl. The libcURL binary
should be placed in WebSEAL's path.
Once you have installed the Web Security ADK, extract the provided source code (See Downloads, in the archives subdirectory "xauthn") to a directory on your machine. Follow the directions in the ADK to build the component including the libcURL C headers into the build.
Enable HTTP header authentication in WebSEAL.
In order for WebSEAL to extract the session cookie from the HTTP requests, WebSEAL should be configured for HTTP header authentication.
Perform the following actions to configure WebSEAL for HTTP header authentication:
- Open the WebSEAL configuration file. For example, with a default installation of WebSEAL, the configuration file is located at WebSEAL_root/etc/webseald-default.conf.
- Locate the
[http-headers]stanza. - Set the value of the parameter
timeoutto be 0. - Set the value of the parameter
inactive-timeoutto be greater than 10 minutes (>600 seconds). - Locate the
[http-headers]stanza. - Specify the protocols to support in your network environment, for example, "http", "https", or "both".
- Create a new stanza named
[auth-cookies]. - Create a new parameter within the new stanza called
cookie. - Set the value of the new parameter to
SPS-SSO-SU. - Locate the
[preserve-cookie-names]stanza. - Create a new parameter within the stanza called
name. - Set the value of the new parameter to
SPS-SSO-SU. - Within the
[authentication-mechanisms]stanza, locate thehttp-request parameter. - Set the http-request parameter to the value of the authentication service,
ensuring that the parameter is appropriately passed, including:
- a. The authentication service binary
- b. The URL for the cookie consumption (you may need to add this later once the IIS application is configured.)
Such that it looks like this:
<path to xauthn library> & <URL for Cookie Consumption>
An example might be as follows:
http-request = C:\PROGRA~1\Tivoli\PDWebRTE\pdxauthn_adk\example\xauthn.dll & http://www.yourserver.com/whoami/consumeCookie.aspx
- Save the changes and restart WebSEAL.
Implementation of the Session Management Application using ASP
With the WebSEAL environment configured, we must now create the ASP pages that generate and consume the persistent cookies. These pages also need access to a backend database, and in this example we will use SQL Server 2005. It may be possible in your environment to use the same installation as that used by the SharePoint Server. Ensure that you have access to it from your SharePoint server and it's ASP pages.
Add a new Application to your IIS SharePoint Server instance.
In order to ensure that the Office application sends the persistent cookie with the connection, the cookie generating application must be on the same host as the SharePoint Server. So we should create a new virtual directory, under the same IIS "Web Site" as shown in Figure 4. Follow the wizard prompts and map it to an empty directory on your SharePoint Server host.
Figure 4. Adding a new IIS Application
In this new virtual directory we need to create the pages necessary for cookie creation and cookie consumption.
This page will be called via the AJAX callout just prior to opening the Office document. In the getCookie.aspx
page we need to generate a session key, map it to the user ID presented in the iv-user header, and
return it with the response page. The cookie and session key should have short time-outs so that if they are not
consumed the window for the persistent cookie to be compromised is reduced. In this example Listing 4,
the database uses a 2 minute expiry. The database manipulation functions are all contained in the
getCookie.aspx.cs file which can be
found in the downloads section of this article.
Listing 4. getCookie.aspx ASP page
<%@ Page Language="C#" AutoEventWireup="true" Debug="true"
CodeFile="getCookie.aspx.cs" Inherits="com.ibm.tivoli.am.getCookie" %>
<%
Response.Cache.SetCacheability(HttpCacheability.NoCache);
String error ="";
com.ibm.tivoli.am.getCookie ops = new com.ibm.tivoli.am.getCookie();
com.ibm.tivoli.am.Cookie cookie = new com.ibm.tivoli.am.Cookie();
error = Request.Headers["iv-user"];
if (error != null && !error.Equals(""))
{
cookie = ops.generateCookie(error);
if (cookie.Equals(""))
{
error = "Session Key Generation Failed";
}
else
{
Response.Cookies["SPS-SSO-SU"].Value = cookie.sessionid;
Response.Cookies["SPS-SSO-SU"].Expires = cookie.expiry;
}
}
else
{
error = "iv-user Not Found, Make sure you are authenticated with WebSEAL";
}
%>
<html>
<body>
WEBTEXT<br />
<%= error %><br />
</body>
</html>
|
This page will be called when the External Authentication component uses cURL to validate the persistent cookie. It should extract the value from the HTTP headers and check it against the values in the session database. If it exists and it hasn't expired, then return the corresponding user-id to WebSEAL. Listing 5 contains a code sample.
Listing 5. consumeCookie.aspx ASP page
<%@ Page Language="C#" AutoEventWireup="true" Debug="true" CodeFile="getCookie.aspx.cs"
Inherits="com.ibm.tivoli.am.getCookie" %>
<%
Response.Cache.SetCacheability(HttpCacheability.NoCache);
com.ibm.tivoli.am.getCookie ops = new com.ibm.tivoli.am.getCookie();
String username = "";
if (Request.Cookies["SPS-SSO-SU"] != null)
{
username = ops.consumeCookie(Request.Cookies["SPS-SSO-SU"].Value);
if (username != null && !username.Equals(""))
{
Response.AppendHeader("am-user-id", username);
}
else
{
username = "'Cookie invalid'";
}
}
else
{
username = "'No Cookie Found'";
}
%>
<html>
Authenticated as <%=username %>
</html>
</code>
|
Prepare the SQL Server Environment
Access to the database by the ASP pages is specified through the connection string which is set in the
web.config file in the IIS application directory. There is
also option to configure the number of minutes until the cookie expires - currently set at
2 minutes.
In this prototype, the application uses its' own database and a custom table for session tracking. These must be created prior to running this application. The commands used are found in Listing 6.
Listing 6. Database commands
C:\ sqlcmd -S <servername>\<sessioninstance> -U <userid> -P <password> 1>create database sessiondb; 2>go 1>use sessiondb 2>go Changed database context to 'sessiondb'. 1>create table sessionmap 2>( 3>username VARCHAR (50) NOT NULL, 4>session VARCHAR (48) NOT NULL, 5>expiry datetime NOT NULL, 6>PRIMARY KEY (session) 7>); 8>GO 1>exit C:\ |
With the SQL server configured and the application installed on the SharePoint server we
can test that it is working appropriately. On the SharePoint Server, access the URL for the ASP page
getCookie.aspx. For example on the host "testserver.com" with the virtual
directory configured as "whoami" the url might look like:
http://www.testserver.com/whoami/getCookie.aspx
This should generate a response similar to the response shown in Figure 5.
Figure 5. ASP Application test 1
This is because the server is not being accessed via WebSEAL and providing the TAM username with each HTTP request.
Accessing the page via an authenticated WebSEAL session should generate the response shown in Figure 6 (spsuser should map to the authenticated username).
Figure 6. ASP Application test 2
This should also have created a cookie in your browser cache labeled:
SPS-SSO-SU with a value of 48 base64 characters and expiring within 2 minutes
(or another value as configured within the web.config file on the ASP application.)
If we then navigate to the 'consumeCookie.aspx' page whilst this cookie is still valid for example:
http://www.testserver.com/whoami/consumeCookie.aspx
The page should consume the cookie and return the user id associated with it, an example is seen in Figure 7.
Figure 7. ASP Application test 3
If the cookie has expired on the host machine or no cookie was requested, it won't be sent with the HTTP request and the following page shown in Figure 8 will be displayed.
Figure 8. ASP Application test 4
And if the page is visited again after the cookie has been consumed but before it has expired, the page shown in Figure 9 will be displayed.
Figure 9. ASP Application test 5
These responses can be traced using a network monitoring program to troubleshoot communications between your office application and the SharePoint server once the integration is complete.
In order to collect the cookie just before opening the Office Document, it is necessary to modify the JavaScript to make a synchronous AJAX HTTP request to collect the Cookie.
This means adding the JavaScript (shown in Listing 7) to any link that opens the Office Document via the ActiveX Controls.
Listing 7. AJAX Wrapper
var cookieRequest = false;
try {
cookieRequest = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
cookieRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
cookieRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
cookieRequest = false;
}
}
}
if (!cookieRequest)
alert("Error initializing XMLHttpRequest!");
var url = "/whoami/getCookie.aspx";
cookieRequest.open("GET", url, false);
cookieRequest.send(null);
if (cookieRequest.status != 200)
{
alert("ERROR: Single-Signon Cookie Request Failed!, Application may not load Document");
}
|
The notable locations this need to be added are within the CORE.JS file on the SharePoint Server at the beginning of the two functions:
function DispEx(
function createNewDocumentWithProgIDCore(
In a default SharePoint installation, the file was found at:
C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\CORE.JS
If a non-standard layout template is used, these paths could be different.
With all three components in place we should now be well placed to test this integration. Following the use case provided in the introduction, opening an Office Document that is located on a server protected by WebSEAL configured with Forms Authentication should now open successfully. All client integration features should work, and the user should be able to save the document directly to the server as shown in Figure 10. Any debugging information generated by the WebSEAL External Authentication component will be output to WebSEAL's log or the command console if running WebSEAL in the foreground.
Figure 10. Client Integration Features - Working
- Any expired session keys that are not consumed will remain in the SQL Database. An appropriate SQL statement should be run to clear these expired session keys out on a regular basis.
- The session key is currently set at 48 bytes.
- The session timeout on the WebSEAL session should be set to a period long enough to prevent it timing out while editing the Office document. If the session does timeout, the Office application does not handle this cleanly. See Session Timeouts for more information.
- If a long session time-out is not appropriate across your WebSEAL environment, you might consider using a separate instance just for the Microsoft Office SharePoint Server junction for better customization.
- When opening documents outside of Microsoft Windows Internet Explorer or without using the ActiveX Controls (for example web folders), the persistent cookie may not be retrieved and the WebSEAL form may be returned.
This article has described how to integrate WebSEAL with Microsoft Office SharePoint Server when using forms authentication with Client Integration activated. It uses WebSEALs' external authentication C API, a one time use persistent session cookie and validated through Active Server Pages.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample Code for this article1 | tam-sps-cdas_src.zip | 10KB | HTTP |
Information about download methods
Note
- The sample source does not provide the libcURL libraries and binaries. These are required to build and use the generated binary code. These files are available from the libcURL website. Refer to the section Download libcURL.
- Participate in the discussion forum.
-
For Tivoli Access Manager for e-Business 6.0 online documentation, visit
Tivoli Software Information Center.
-
For an overview of Tivoli Access Manager for e-business, visit the
IBM Web Site.
-
For downloading the libcURL library binaries and additional libcURL examples visit: http://curl.haxx.se/.
-
IBM Tivoli Access Manager Microsoft SharePoint Services Integration Adapter.
-
IBM Tivoli Access Manager Microsoft IIS Integration.
Philip Nye is a Software Development Engineer at the Gold Coast Tivoli Lab. In his time at IBM he has worked on the development of Tivoli Access Manager e-business integration components and developed for Tivoli Access Manager for Operating Systems, and is now working to develop stronger integration between Tivoli Products and Microsoft Office SharePoint. His undergraduate degrees are a Bachelor of Engineering in Software Engineering and a Bachelor of Business Management from the University of Queensland.




