AJAX techniques within a Tivoli Access Manager WebSEAL Environment

Considerations, issues, potential solutions and best practices

This article describes the challenges found when introducing Asynchronous JavaScript™ and XML (AJAX) programming techniques into an IBM® Tivoli® Access Manager (TAM) WebSEAL environment. It provides a brief review of WebSEAL technology and a brief introduction to AJAX methods. The considerations are outlined for AJAX developers when working with WebSEAL. The potential solutions to issues that can arise are supplied, along with listing best practices that will assists AJAX developers to succeed in a WebSEAL environment.

Peter Tuton (ptuton@au1.ibm.com), Software Engineer, IBM

Peter TutonPeter Tuton is a Software Engineer in the Tivoli Security Integration Factory Team based on the Gold Coast, Australia. In this role, Peter is the Technical Lead for the team developing integration solutions for Tivoli Access Manager and Tivoli Federated Identity Manager. Peter has been involved in the certification of a number of IBM integration solutions with leading software companies, including Siebel, PeopleSoft and SAP.



Grant Murphy (gmurphy@au.ibm.com), Software Engineer, IBM Australia

Grant MurphyGrant Murphy is a Software Engineer based on the Gold Coast, Australia. Grant's current role is a developer on IBM's Global Security Toolkit.



29 April 2008

Introduction

Programming techniques and paradigms that are utilized in Web development are changing rapidly. One of the mainstream techniques being utilized today is Asynchronous JavaScript and XML (AJAX). This article describes the best practices for deploying AJAX applications in a centralized reverse proxy environment such as Tivoli Access Manager WebSEAL.

Before proceeding however, this article provides a brief introduction and review of the relevant WebSEAL concepts.

Background: WebSEAL operating in a traditional request/response paradigm

WebSEAL is a reverse proxy that uses URL rewriting to provide some of its core functionality. The role of a reverse proxy is to provide a centralized point to receive HTTP requests on behalf of a back-end Web server. When an HTTP request is received by the reverse proxy, it uses a junction (similar to a UNIX® mount) in order to determine to which back-end Web server to pass the request (see Figure 1-1). This generally requires that the URL be re-written in order to make sense to the Web server. The re-written request is then sent on to the back-end server. The back-end Web server accepts the request as a standard HTTP GET request and processes it accordingly.

Figure 1-1. Reverse proxy request processing
Reverse proxy request processing

The second point where the reverse proxy acts as a mediator is by filtering the response from the back-end Web server to replace all URLs to include the junction details (see Figure 1-2). This ensures that all links in the returned HTML page point to valid URLs.

Figure 1-2. Reverse proxy response processing
Reverse proxy response processing

Before you can fully consider how WebSEAL will work with AJAX applications, it is essential to understand the fundamental rules WebSEAL uses to filter URL in the HTML content. These rules vary depending on the type of junction deployed in the environment. The following sections address each type of URL.

Filter rules for relative URLs

Relative URLs are always handled appropriately by the browser. Therefore, WebSEAL does not filter relative URLs.

Table 1. Example - Relative URLs
Browser <-> IBM Tivoli WebSEALWebSEAL <-> Back-end Web server
RequestGET "example.html"GET "example.html"
Response
<html>
    <head>
        <title>Example Page</title>
    </head>
    <body>
        <a href="../sample.html">Sample link</a>
    </body>
</html>
<html>
    <head>
        <title>Example Page</title>
    </head>
    <body>
        <a href="../sample.html">Sample link</a>
    </body>
</html>

Filter rules for server-relative URLs

WebSEAL must add the junction name to the path of server-relative URLs that refer to resources located on junctioned servers.

Table 2. Example: Server relative URLs
Browser <-> IBM Tivoli WebSEALWebSEAL <-> Back-end Web server
RequestGET "/junction/html/example.html"GET "/html/example.html"
Response
<html>
    <head>
        <title>Example Page</title>
    </head>
    <body>
        <a href="/junction/html/sample.html">Sample link</a>
    </body>
</html>
<html>
    <head>
        <title>Example Page</title>
    </head>
    <body>
        <a href="/html/sample.html">Sample link</a>
    </body>
</html>

Filter rules for absolute URLs

WebSEAL must add the junction name to the path of the absolute URLs that refer to resources located on junctioned servers. By default, WebSEAL changes the absolute URL into a server-relative URL. The rewrite-absolute-with-absolute parameter determines if the absolute URL is used. This parameter is located in the WebSEAL configuration file, which is by default located at WEBSEAL_HOME/etc/webseald-instance.conf

Table 3. Example: Absolute URLs
Browser <-> IBM Tivoli WebSEALWebSEAL <-> Back-end Web server
RequestGET "http://www.ibm.com/junction/html/example.html"GET "http://www.backend.com/html/example.html"
Response
<html>
    <head>
        <title>Example page</title>
    </head>
    <body>
        <a href="/junction/html/sample.html">Sample link</a>
    </body>
</html>
<html>
    <head>
        <title>Example Page</title>
    </head>
    <body>
        <a href="http://www.backend.com/html/sample.html">
        Sample link</a>
    </body>
</html>
Response (rewrite-absolute- with-absolute=yes)
<html>
    <head>
        <title>Example page</title>
    </head>
    <body>
        <a href="http://www.ibm.com/junction/html/sample.html">
        Sample link</a>
    </body>
</html>
<html>
    <head>
        <title>Example page</title>
    </head>
    <body>
        <a href="http://www.backend.com/html/sample.html">
        Sample link</a>
    </body>
</html>

Adding AJAX to the equation

The following sections consider the implications of adding AJAX to the WebSEAL environment.

Introduction to AJAX programming techniques

For the purpose of this article, an AJAX request is assumed to be any technique that uses asynchronous requests made to a remote system using client side JavaScript. The response object should ideally be a valid XML document or XHTML, however this is not always the case in practice. As such, other potential issues that might arise when using plain text response data are addressed by this article.

There are two mainstream technologies used to perform AJAX requests: the XMLHttpRequest object and IFRAMEs; which are described below.

Anatomy of the XMLHttpRequest object

The XMLHttpRequest object provides the methods and properties required to issue GET and POST requests asynchronously via JavaScript. Basic operations, instantiating an object, issuing a request, and processing the response, are outlined in listings 1-3, respectively.

Listing 1. Instantiating a request object
// Internet Explorer
var xhr = new ActiveXObject(Microsoft.XMLHTTP);

//Mozilla, Opera
var xhr = new XMLHttpRequest()
Listing 2. Issuing a request
xhr.onreadystatechange = yourCallbackFunction();
xhr.open(method, url, async)
xhr.send(data)
	
//where: 
//	method 	= GET or POST
//	url		= Url to request / post to. 
//	async	= Send request asynchronously (t/f) 
//	data		= Post data (use null for get)
Listing 3. Processing the response
// Useful properties and methods
xhr.responseText 			// The response as a string
xhr.responseXML  			// The response as XML
xhr.getResponseHeaders()	// Get http headers

AJAX with IFRAMEs

AJAX methodologies can also be accomplished by using IFRAMEs. Essentially, you can load content in a hidden IFRAME that can be displayed to the user when loaded. This is non-blocking and provides an alternative way to load content asynchronously without using the XMLHttpRequest object.

Listings 4-6 demonstrate the creation of an IFRAME, adding an event listener, and showing and hiding the IFRAME.

Listing 4. Creating an IFRAME
// Create the IFRAME 
myIFrame = document.createElement(“IFRAME”);

// Set some basic properties
myIFrame.id                     = “my_id”;
myIFrame.name                   = “my_iframe”;
myIFrame.height                 = “500”;
myIFrame.width                  = “500”;
myIFrame.style.visibility       = “visible”;

// Add the IFRAME to the page
document.body.appendChild(“load”, contentLoaded, true);
Listing 5. Adding an event listener
myIFrame.addEventListener(“load”, doThisWhenLoaded, true);
// where doThisWhenLoaded is the function that is called when 
// the IFRAME is loaded.
Listing 6. Showing and hiding the frame
myIFrame.style.visibility   = “visible”;
myIFrame.style.visibility   = “hidden”;

Considerations for AJAX developers

The following sections address issues that an AJAX developer should consider when programming in a WebSEAL environment. An example of each issue is provided, along with a potential solution. The exact solution cannot always be given, because the environment specifics might need to be considered.

The sections include:

User session expiration

Issue

In normal operating conditions, if a session expires because of a timeout, the user is redirected to the login page when they next request a protected page. Upon the successful login they are redirected to the requested Web context. If AJAX is the mechanism utilized to make the request, then the login form will be contained within the AJAX response object and most likely the redirection will fail.

Similarly if a maximum number of concurrent sessions policy is enabled, problems arise when AJAX is the driving mechanism behind the request. The concurrent session policy limits the maximum number of sessions a user can have open at a single time. Depending on the configuration settings, WebSEAL will ask the user whether they want to displace an active session or simply terminate the other sessions without user intervention. If prompting session displacement is enabled, the user performing the displacement is not redirected to the displacement page. In both instances, if the next request made by the displaced user is done via AJAX, there is a strong chance that the user will not be correctly notified regarding the displacement.

Example

Figure 2. Session expiry
Example how session expiration can cause problems when using AJAX

In normal operating conditions, a user's browser will handle this type of response in an acceptable manner. That is, redirect to the appropriate login form or error message. However when the redirect returned by WebSEAL is concealed within the XMLHttpRequest object, this redirection does not occur, and therefore the user is not notified in an acceptable manner as to why their last request has not been granted.

Potential solution This problem requires that the client be able to recognize when the response object is not in the expected format. For example if the response object contains an HTTP status code other than “200 OK” chances are that the response object is not what the application expected. If the client can detect this, they can effectively escalate the issue by passing control to the user agent. By making the user agent issue the same request rather than doing so in the sub context of AJAX, the error message / form is displayed as intended by the back-end application.

Listing 7. Handling session expiry
XMLHttpRequest xhr = new XMLHttpRequest();

// Need to alter how we handle the response slightly
xhr.onreadystatechange = function(requestedUrl){

    if(xhr.readystate = READYSTATE_LOADED){
        try{
            // Assume that anything other than a HTTP status code of 
            // 200 OK is going to be something needing to be handled
            // by the browser. 
            if(xhr.status != HTTP_STATUS_OK){
                throw requestedUrl;
            }else{
                // Handle things normally here 
            }
        }catch(url){
            // Redirect them to the correct page by re-requesting
            // the original URL using the browser as the 
            // user agent rather than an AJAX request. 
            window.location = url;
        }
    }       
}

Listing 7 suggests a simplified solution to this problem. It involves making sure that the request returns a "200 OK" status and, if not, the browser is told to redirect to the original URL. In this instance if the user's session has expired the usery will be redirected to the login page.

Persistent connections

Issue

Maintaining a persistent connection between a client and a Web server is becoming a popular technique in AJAX environments, although it is not specific to such environments. The common reason for persistent connections and the non-traditional usage of HTTP is to simulate asynchronous communications from the server back to the client. Applications might want to do this for functionality such as notifications or server status. One example is Lotus® Sametime® and the awareness functionality, which allows the server to monitor the online status of the users and display incoming chat messages, among other things. When one of these connections times-out, the client attempts to immediately reconnect to the server.

The implication of this for WebSEAL is that users with active sessions will be holding open at least one long-running connection to the server, in addition to the shorter connections that are generated from other user-initiated requests. This will consume WebSEAL worker threads and the value of the [server] worker-threads parameter might need to be tuned in the WebSEAL configuration file. In extreme cases where a high number of active user sessions are anticipated, it might be necessary to add WebSEAL instances.

Example

Figure 3. Persistent connections
Persistent connections

Potential solution

Determine if there is an issue with the current setting of the [server] worker-threads parameter as configured in the WebSEAL configuration file. The simplest method to determine if there is an issue is to use the WebSEAL statistics functionality. Specifically, monitor the output of the pdweb.threads component.

The following example shows the output from a statistics get command for the pdweb.threads component:

#pdadmin> server task default-webseald-instance stats get pdweb.threads 
active : 0 total : 50

If the reported value of "active" is continually reaching the value of "total", you should make more worker threads available.

For more details on configuring WebSEAL worker threads, refer to the WebSEAL Performance Tuning Guide. For more details on gathering WebSEAL statistics, refer to the Tivoli Access Manager for e-business Problem Determination Guide and the Tivoli Access Manager for e-business Auditing Guide.

Authentication and authorization

Issue

Accessing protected objects from an unauthenticated or an account with lower privileges than required will return a login prompt or "HTTP 401" error, respectively. A user agent such as the browser understands how to handle these scenarios; however, a typical client who is using AJAX as the requesting mechanism won't understand.

Example

Figure 4. Protected Web site
Example Web site structure.

It is quite feasible for a typical Web application that is publicly accessible to contain a link to a Web object that is in a protected space. In this situation when an unauthenticated user clicks on the link, they will be redirected to the appropriate authentication mechanism. When they have authenticated, the decision can be made as to whether they are authorized to access this particular Web resource. If they aren't authorized, the browser will be sent a “401 Unauthorized” HTTP status code.

Again if AJAX is the underlying medium issuing the request when either an authentication or authorization decision needs to be made problems will ensue. These problems follow the same pattern as those described in the user session expiration section. As the client side script is effectively acting as the user agent (browser) in this scenario it needs to be able to handle the response in a similar fashion. That is, notify the user that they need to authenticate to access the requested resource, and they have insufficient access to access this resource.

Potential solution

The solution here again comes down to building in some intelligence into the client side application. The ability for you application to perform under non-standard operating conditions is an important point for the Web application developer to consider. A similar solution as described in the “User session” issue could be implemented here. An alternate approach could be to simply avoid using AJAX as the requesting medium in circumstances where an authentication or authorization decision is likely to be made. Regardless of the decision made here it is important for Web developers to keep this issue in mind when designing their applications.

Link re-writing and the same origin policy

Issue

For a standard WebSEAL junction, JavaScript filtering is only performed if it is configured to do so. That is, the junction is created using the -j option.

The majority of Web developers will write their JavaScript code to contain URL information. This URL information will not be re-written by WebSEAL and as such can cause a variety of issues, mainly around dead links. However a specific issue also exists for requests made with the XMLHttpRequest object, due to the same origin policy that is enforced by the JavaScript language.

Example

The following example demonstrates how a Web developer would typically write their AJAX content. By separating JavaScript operations from HTML content it enables better reuse of resources. However if there is URL content defined in the JavaScript then you will need to enable script filtering in WebSEAL. Consider the following scenario.

Listing 8. Content of example.js
const server = “sales.acme.org”;
const port   = “80”;

// A custom wrapper the XMLHttpRequest object that I wrote
var xhr = new XHR();        
function doAjaxRequest(){
    var url_1 = “http://sales.acme.org/sales.xml”;
    var url_2 = “http://” + server + “:” + port + “/sales.xml”;
    var url_3 = “/sales.xml”;
    var url_4 = “sales.xml”;
    xhr.setStateChangeCallback(4, doCallback, this);
        xhr.request(“GET”, url_1, false, null);
}

function doCallback(){
    alert(“A response was received.”);
}
Listing 9. Content of example.html
<html>
    <head>
        <script type=”text/javascript” src=”example.js”></script>
    </head>
    <body>
        <a href=”javascript:doAjaxRequest()”>Do Ajax Request</a>
    </body>
</html>

In the example, above the following problems occur.

  • The URLs will not be rewritten unless script filtering has been configured correctly.
  • If the "http://sales.acme.org/sales.xml" link is not re-written an exception will be raised by the browser, as it would be seen as violating the same origin policy.
  • If script filtering was enabled, the variable "url_1" defined in example.js would be re-written by WebSEAL.
  • Even with script filtering configured correctly, WebSEAL is not able to interpret the level of dynamic link construction that is used in "url_2".
  • Although WebSEAL has the capability to filter JavaScript, it is often to differentiate between a variable which is intended to be a string literal and a relative / server relative URL. Absolute URLs will be re-written correctly, and as relative URLs do not need to be altered, the only type of URLs that will cause problems here are server-relative URLs. This is because WebSEAL must add the junction name to the path of server-relative URLs that refer to resources located on junctioned servers. If it is unable to determine if a variable declared like “url_3” is a server relative URL, then all requests that are issued in this way will result in dead links.

Potential solution

WebSEAL is intelligent enough to filter JavaScript, however you need to tell it to do so. If you turn on JavaScript filtering only, you won't get the desired effect. A standard configuration for WebSEAL with JavaScript filtering enabled will only look for URLs in content similar to the entries in the table below.

Table 4. JavaScript filtering
Unfiltered contentFiltered content
<a href="javascript:doRequest('http://sales.acme.org/somecontent.html')">click here</a><a href="javascript:doRequest('http://acme.org/sales_junciton/somecontent.html')">click here</a>
<script type="text/javascript" src="http://sales.acme.org/js/script.js"></script><script type="text/javascript" src="http://acme.org/sales_junction/js/script.js"></script>

To get the content defined within an external JavaScript inspected, you need to use a configuration as follows:

Listing 10. Changes required in webseald.conf
[filter-content-types]
type = text/html
type = text/xml
type = text/javascript

[script-filtering]
script-filter = yes

Another possible approach is to utilize virtual host junctions, if this suits your existing infrastructure. By doing this, you are effectively eliminating the need to filter URLs within HTML content. You will have a lot less issues when it is time to rewrite URLs within your client side scripts. However, virtual host junctions are not suitable for all deployments of WebSEAL within an enterprise, so it is really a matter of choosing what is best for your environment.

Junction cookies

Issue

When you create a WebSEAL junction using the -j option to enable junction cookies, special HTML code is inserted at the beginning or end of the HTTP response that sets the correct cookie path in the browser (the location determined by the -j option). Generally, the returned page will look similar to that in Listing 11.

Listing 11. Example of junction cookie insertion
<SCRIPT>
    document.cookie = "IV_JCT=%2Fjunction_name";
</SCRIPT>

<html>
    <title>Example page </title>
    <body>
        Rest of the document...
        .
        .
        .

Developers need to keep this in mind when they are issuing HTTP requests using AJAX; especially if the rendered response is to be used as anything other than HTML.

Example

For example, an application might issue an AJAX request that calls a server-side function, expecting the result to be used later in the calling application. If the result in returned using a content type of "text/html" and the junction is configured with the -j option, WebSEAL will insert the JavaScript that adds the junction cookie to the response.

Figure 5. Junction cookie
Junction cookie JavaScript added to response

Potential solution

If the response from an AJAX request is not to be rendered as HTML, the response should not be sent with a content type of "text/html". A more appropriate content type should be used, for example, "text/plain". WebSEAL does not add the junction cookie code to responses that do not have a content type of "text/html".


Best practices

Propagate unexpected HTTP status codes to the user's browser

There are a number of possible scenarios where the server might return a response that is not what the Web application developer expected. Among these are things previously addressed in this article such as session expiration. As the user's browser is already equipped to handle these situations, it makes sense to pass the control back to it for the best user experience.

Avoid dynamic link construction in client-side scripts.

It might make programmatic sense to divide a URL into a series of components; however if this is done on the client side, the URL will not be correctly filtered by WebSEAL.

Use relative or absolute links

Use relative or absolute links where possible to avoid dead link issues. Server relative links aren't touched by WebSEAL when in JavaScript. This will result in dead links.


Conclusion

Using AJAX in a WebSEAL environment is possible, and works well when the potential issues are considered. When developing in such an environment, there are a number of potential issues that can arise without considering the technical requirements of a URL re-writing reverse proxy server, such as WebSEAL. This article highlights a number of potential issues that can be overcome simply by following the outlined best practices.

Resources

Learn

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Security on developerWorks


  • Bluemix Developers Community

    Get samples, articles, product docs, and community resources to help build, deploy, and manage your cloud apps.

  • Security

    Pragmatic, intelligent, risk-based IT Security practices.

  • DevOps Services

    Software development in the cloud. Register today to create a project.

  • IBM evaluation software

    Evaluate IBM software and solutions, and transform challenges into opportunities.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Security, Tivoli, Web development
ArticleID=297851
ArticleTitle=AJAX techniques within a Tivoli Access Manager WebSEAL Environment
publish-date=04292008