Getting rid of password prompting on the BlackBerry® Browser for web applications that require authentication
Tim Jones, Bracewell & Giuliani LLP
Context
Our company, Bracewell & Giuliani LLP, has been using BlackBerry® Enterprise Server for several years. In the last 5 years, more and more wireless devices started circulating that included a built-in web browser. As a web application developer just becoming familiar with ASP.NET, I started thinking about all the web applications I could write to target these devices. So, what did I do? I opened Microsoft® Visual Studio®, created a new web application and deployed it to our intranet web server. When I first browsed to the application I was prompted for my credentials (Figure 1). I chose the option to remember my username and proceeded to use my application. Later on that day, however, I was prompted to enter my credentials again. Every time I accessed the web application after waiting for a period of inactivity (such as the following day), it would keep prompting me.
At this point, I had been creating web applications for years that didn't require a credential prompt. I had been spoiled by Windows® Internet Explorer®'s Intranet security zone single sign-on (SSO) using Integrated Windows Authentication (IWA). When ASP.NET was released, I was further spoiled with authorization by simply typing in an Active Directory (AD) group name in the web.config to validate group membership. Knowing that my company's users wouldn't want to keep entering their password, I pushed BlackBerry smartphone web application development aside out of frustration. However, I returned a couple years later for an application with a mobility requirement. I originally deployed this application as a native BlackBerry Java Application so I could transmit the BlackBerry smartphone PIN as an authentication token, but it was difficult to maintain and it performed poorly (partly because it relied on web services for communicating to the server).
Figure 1: Authentication prompt on a BlackBerry Browser.
Later that same year, I completely rewrote my Java® application as a web application, but during this process I needed a method to avoid the continuous credential prompting (and achieve SSO). I chose to use cookies as an authentication mechanism instead of the web server's software (for example, Microsoft IIS). This article will describe most of the challenges and solutions that accompanied my development of a reusable library (hereafter referred to as Mobile Site Library, or MSL) to accomplish SSO for our intranet web applications. Some of these challenges include, but are not limited to the following concerns.
- Authentication
- Authorization
- Unsupported browsers
- Page caching
- BlackBerry Enterprise Server push deployment
HTTP cookies offer a method of storing state between browser sessions. Cookies have been around for so long that most browsers, including mobile browsers, support them. I decided to use a cookie to store a user's identity after considering other options. For example, generating a GUID for each user to be used as a URL parameter could be one method of authentication, while issuing each user a client certificate is another. However, in the end, issuing an authentication cookie seemed to be the easiest and most maintenance-free option available.
Disadvantage: SecurityUsing cookies has its own set of disadvantages, most of which are security-related. For example, the first step towards using cookies for authentication is to disable the web server's built-in authentication methods. For some web servers, this is referred to as enabling anonymous access.
Cookies, since they are located on the client, can be extracted, examined and modified. Over the wire, cookies can be intercepted and replayed. Therefore, the content of a cookie and the connection to web servers should each be encrypted. Since the BlackBerry® Mobile Data System Connection Service automatically includes encryption (AES or 3DES) from the BlackBerry Enterprise Server to the BlackBerry smartphone (highlighted in Figure 2 by the lock and key icons), some will decide this is sufficient. If encryption between the BlackBerry Enterprise Server and the destination web server is desired, SSL must also be used.
Figure 2: Typical BlackBerry Enterprise Server topology.
A symmetric encryption algorithm should be used to protect the cookie's contents. I recommend using a symmetric algorithm for performance reasons. .NET offers several algorithms to pick from (The Rijndael algorithm is one) in this category.
Advantage: Global TicketOne advantage of cookies is that they are host-bound. By host-bound, I mean that a cookie is available to any path within the host name of the web site. For example, if a cookie is issued from www.blackberry.com, all subdirectories beneath www.blackberry.com will automatically have the cookie transmitted to it. In IIS, each directory can be an independent ASP.NET application. Given this fact, one of the subdirectories could be a cookie provisioning application. Cookies issued by this application will be transmitted to all other subdirectories, or applications, within this host.
The topology I'm describing might not represent your environment. If you use a separate web site for each application, this method will not offer much of an advantage. The method I'm proposing only becomes a true SSO implementation when all mobile applications are deployed as subdirectories within a common web host name. Consider the example intranet web site in Figure 3 below. This web site contains several directories, each of which is a separate ASP.NET web application. Some of these directories are mobile applications while others are not. Even though each application is independent from the other, they each have access to the same cookie collection because they share the same host name (http://webserver).
Figure 3: Example of web site hierarchy displaying cookie collection access.
If an authentication cookie is created by any of these applications, it can act as a ticket for all other applications within the web site. Considering you can place all of your mobile applications on the same site, this allows the ability to sign in only once, and then reuse the cookie as long as determined by the cookie's expiration date.
Provisioning the CookieAnother advantage of storing the authenticating identity in the cookie is the freedom it offers. Since authentication is no longer enforced by the web server, the web application developer can determine precisely how to provision the cookie. The cookie provisioning application could use any authentication method supported by IIS out of the box to obtain the identity (such as IWA), or it could be determined by something as custom as a series of questions the user must successfully answer on a web form.
Creating the Mobile Site LibraryIn order to start this solution in the right direction, I created a new C# library assembly (shown in Figure 4) that will be referenced by all web applications. This will keep all of the logic for the solution in one location to make it easier to maintain. For simplicity, I added a single static class called Common as a generic container of the code (seen later on in this article). In your project, you'll probably want to create separate static classes for each functional area (authentication, authorization, cryptography, etc.). I then added a reference to System.Web and System.Configuration for later use.
Figure 4: Class Library project template in Visual Studio.
As mentioned above, the first step towards setting up the mobile application is to enable anonymous access. In ASP.NET's web.config, the authentication element's mode attribute should be set to "None" (Listing 1). You'll also need to verify the directory's authentication method is set up for anonymous access. Figure 5 shows what the authentication options page looks like in IIS. Since IIS will attempt anonymous access first, you can leave other authentication methods enabled if you want.
<?xml version="1.0"?>
<configuration>
<system.web>
<authentication mode="None" />
</system.web>
</configuration>
Listing 1: Web.config for mobile application with anonymous access enabled
Figure 5: IIS authentication methods dialog box with Integrated Windows Authentication turned on
Since we cannot rely on the web server for authentication, the mobile web application should inspect every request for the authentication cookie. When it's not found, it should redirect the request to the cookie provisioning application. A typical provisioning sequence is illustrated in Figure 6. In order for the provisioning application to know how to redirect back to the mobile application, the current request path must be URL-encoded and attached as a parameter.
Figure 6: MSL authentication sequence.
Listing 2 is an example that uses a master page to check for the existence of the cookie. A master page is an easy way of making sure all of your application's pages validate the cookie's existence without having to place code in each page. In this listing, if the cookie does not exist, it is redirected to the provisioning application (which we'll discuss next). Request.RawUrl provides the current path that we can pass as a parameter. Overriding OnInit is preferred to OnLoad since it executes sooner in ASP.NET's execution cycle. Either event will execute before code on the page that uses this master page, however.
public partial class _default : System.Web.UI.MasterPage
{
protected override void OnInit(EventArgs e)
{
HttpCookie authCookie = Request.Cookies["MSLAuthentication"];
if (authCookie == null)
Response.Redirect("/msl/authenticate.ashx?r=" + HttpUtility.UrlEncode(Request.RawUrl));
base.OnInit(e);
}
}
Listing 2: Master page code to check for MSL cookie
Creating the Cookie Provisioning Application
I mentioned earlier that the provisioning application could use any method for issuing the authentication cookie. For my purposes, I wanted to reuse AD as my authentication store. I created a new virtual directory application in my IIS web site and set the authentication to IWA as shown in Figure 5. Next, I created an empty ASP.NET web application.
Once IIS authenticates a request to this application, an authentication cookie will be sent to the client. I used an HTTP handler to increase performance of the request since no user interface is required. These operations and classes are included in MSL and can be seen in Listing 3. In this listing, line a1 (commented in the listing) returns the current user's identity after authentication has occurred and line a3 redirects back to the original application by decoding the URL parameter "r". Line a2 shows that we're issuing a cookie that expires in 1 year. You may decide that this period of time should be extended or shortened depending on your environment. Based on this configuration, the following list is an example sequence between a mobile expense reimbursement application and the IWA provisioning application.
- Mobile web application requested at http://webserver/expense/m/default.aspx
- Cookie does not exist, so browser is redirected to
http://webserver/msl/authenticate.ashx?r=http%3A%2F%2Fwebserver%2Fexpense%2m%2Fdefault.aspx - Provisioning application authenticates request using IWA
- HTTP handler issues cookie and redirects back to http://webserver/expense/m/default.aspx
using System;
using System.Web;
namespace Company.Web.Mobile
{
public static class Common
{
public static string CookieValueName
{
get { return "Login"; }
}
public static string AuthenticationCookieName
{
get { return "MSLAuthentication"; }
}
public static HttpCookie
CreateAuthenticationCookie(string identityName, DateTime expiration)
{
HttpCookie authCookie = new HttpCookie(AuthenticationCookieName);
authCookie.Expires = expiration;
authCookie.Values.Add(CookieValueName, Encrypt(identityName));
return authCookie;
}
public static String Encrypt(String plaintext)
{
String ciphertext = null;
// replace this with your
// encryption algorithm
ciphertext = plaintext;
return ciphertext;
}
public static String Decrypt(String ciphertext)
{
String plaintext = null;
// replace this with your
// decryption algorithm
plaintext = ciphertext;
return plaintext;
}
}
public class MSLAuthenticationHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
{
// create login cookie based on
// authenticated windows account
context.Response.Cookies.Add( Common.CreateAuthenticationCookie(
/* line a1 */
context.Request.LogonUserIdentity.Name
/* line a2 */
, DateTime.Now.AddYears(1)));
//redirect back to calling page
/* line a3 */
context.Response.Redirect(
HttpUtility.UrlDecode(context.Request.QueryString["r"]));
}
catch (Exception ex)
{
context.Response.ContentType = "text/plain";
context.Response.Write("<html><body><h3>");
context.Response.Write(ex.ToString());
context.Response.Write("</h3><p>");
context.Response.Write(ex.StackTrace);
context.Response.Write("</p></body></html>");
}
}
public bool IsReusable
{
get { return false; }
}
}
}
Listing 3: Example C# code for MSL with cookie provisioning
In order to use the HTTP handler, the handler needs to be registered in the web.config. If the MSL assembly is built into a file named MobileSiteLibrary.dll and placed in the application's bin directory, the web.config in Listing 4 will transfer all GET requests for resource authenticate.ashx to the handler. No additional registration is required in IIS since the ashx extension is provided in ASP.NET by default for generic handlers.
<?xml version="1.0"?>
<configuration>
<system.web>
<authentication mode="Windows"/>
<authorization>
<deny users="?"/>
<allow users="*"/>
</authorization>
<httpHandlers>
<add path="authenticate.ashx" verb="GET"
type="Company.Web.Mobile.MSLAuthenticationHandler, MobileSiteLibrary"/>
</httpHandlers>
<customErrors mode="RemoteOnly" />
</system.web>
</configuration>
Listing 4: Web.config for IWA cookie provisioning application
Mobile Data System Unique Identification Headers
If requiring users to enter a username and password is unacceptable, BlackBerry Enterprise Server environments offer another alternative. BlackBerry Mobile Data System can be configured to transmit the user's PIN and/or email address in a header to a web server (See article DB-00435). If you store this information in a database (or in AD) that associates it to the user's account name, the provisioning application could be written to use these headers for authentication and avoid the credential prompt. Depending on your deployment size, you might be able to get away with avoiding a dedicated provisioning application entirely. For example, the redirection to the provisioning application could be replaced by a database lookup routine within your page processing pipeline.
Anonymous AuthorizationNow that we've turned off IIS and ASP.NET built-in authentication and authorization methods, we have to roll our own authorization method. If this sounds a bit challenging, it's because it is. However, if you happen to be running the mobile application on a Windows Server 2003 (or later) web server and your environment has standardized on AD as your primary user and group information store, the task of verifying group membership for authorization becomes quite easy.
Verifying AD Group MembershipWindows developers will probably already be familiar with AD group membership verification, which is easy to do with a WindowsPrincipal object. This object verifies membership using the IsInRole method. The reason I mention Windows Server 2003 explicitly is because this version was the first that introduced a Kerberos extension named Service-for-User-to-Self (S4U2Self).
This is significant because S4U2Self creates a WindowsIdentity object from a User Principal Name (UPN) without requiring the user's password. A UPN is a formatted user account name that looks similar to an email address. For example, if the AD fully qualified domain name is company.com and the account name is tjones, the UPN is tjones@company.com.
Once the WindowsIdentity object has been created by S4U2Self, it is passed as an argument to an overloaded constructor of the WindowsPrincipal object. The nesting relationship of this process is shown in Figure 7. An example method that uses this extension is shown in Listing 5.
Figure 7: WindowsPrincipal nesting with S4U2Self
public static WindowsPrincipal GetPrincipal(string upn)
{
WindowsPrincipal wp = null;
// Use S4U2Self to create WindowsIdentity
// (as of Windows Server 2003)
// try catch is used because Security
// exception will be thrown if not executed
// on this platform
try
{
wp = new WindowsPrincipal(new WindowsIdentity(upn));
}
catch { }
return wp;
}
Listing 5: WindowsPrincipal generation using S4U2Self
Now that we've obtained a WindowsPrincipal object, group membership verification against AD is one line of code by using the IsInRole method (As seen in Listing 6 below). If the user isn't authorized, it would be friendly to show an access denied message. To show the message, you could use an HTML page or create a dynamic page that provides more details about the request.
Server.TransferI used Server.Transfer instead of Response.Redirect for performance reasons since it avoids a round-trip call to the server. Be careful when using Server.Transfer, however. If you try to use Server.Transfer to a dynamic page, such as an aspx file, ASP.NET will attempt to load the class for the page in memory during the transfer from the current application. You will have problems if you attempt to transfer to a dynamic page in another web application because ASP.NET will not find the assembly for that class in the current application's bin directory. If your intention is to build a universal unauthorized server-side page, I recommend using Response.Redirect to an HTTP handler
WindowsPrincipal wp = GetPrincipal(authCookie.Values["Login"]);
if(!wp.IsInRole(@"Company\Expense Users"))
Server.Transfer("unauthorized.html");
Listing 6: Code for AD authorization check
Supported Browsers
Browser detection is required if access to the mobile application should be limited only to supported BlackBerry smartphones that have been tested.
To accomplish this, we need to check the browser identifier HTTP header, HTTP_USER_AGENT. For example, if we want to limit access to BlackBerry smartphones only, we'd verify that the text "BlackBerry" appears in this header. Article DB-00435 further explains browser identification. When an unsupported browser is detected, a message similar to the unauthorized error should be returned.
Browser detection could also be used if a mobile web application has an equivalent standard web application. In this scenario, a user might have a link to the mobile application in an email, but tries to access the link from his or her computer. It would be desirable to redirect the user to the standard web application if this occurs.
Browser detection needs to happen before checking for the cookie to avoid the authentication process we've created up to this point. Listing 7 shows a browser check that redirects unsupported callers to a non-mobile version of the expense application (See Figure 3).
if (Request.UserAgent.IndexOf("BlackBerry")== -1)
Response.Redirect("/expense/default.aspx");
Listing 7: Code to only allow BlackBerry smartphone browsers
Overcoming Page Caching
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1)); Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.SetNoStore();
Listing 8: MSL code for preventing page caching
Web page caching and dynamic web sites are typically in conflict with one another. Caching the results of a web site in general is a great idea since it saves server resources and increases performance. However, when caching a dynamic web application, a common side effect is stale results. This is sometimes experienced when a caching system such as a proxy server is used.
When I first started testing my mobile application, I noticed that the BlackBerry browser would always display cached copies of my aspx pages after a postback. To prevent this caching, cache control HTTP headers must be included in the web server's response headers. Instead of manually adding these headers to every page in the application, they could be added during the execution of OnInit (Listing 8).
BlackBerry Enterprise Server Push DeploymentIn a BlackBerry Enterprise Server environment, several options are available for deploying a web application to a BlackBerry smartphone. They can range from the very simple, such as sending an email with a hyperlink to the application, to the complex, such as initiating a BlackBerry MDS browser push. Having worked with both methods, I prefer a browser push for those applications that need a dedicated entry point on the BlackBerry smartphone. Several browser push methods could be chosen, and it's worth taking a look at each one to determine if it meets your requirements. A browser channel push, for example, will create a custom icon on the home screen of the BlackBerry smartphone.
To understand how this method of deployment interacts with our applications, it's helpful to know what is going on behind the scenes of a browser push. Figure 8 illustrates the process of a typical BlackBerry MDS browser push. In this figure, the workstation issuing the push downloads the results from a web page then issues a custom HTTP POST to the BlackBerry Enterprise Server running BlackBerry MDS. If BlackBerry MDS responds with a 100 Continue header, the results are sent back to the BlackBerry Enterprise Server then forwarded to the BlackBerry smartphone's cache. A 200 OK header is then returned as a confirmation to the workstation.
Figure 8: A typical BlackBerry Mobile Data System browser push request
Creating a custom POST request is not something terribly difficult for a web developer (or anyone with HTTP diagnostic utilities), but it still represents a significant hurdle when you factor in reading the documentation and testing. A browser push utility is not currently included in BlackBerry Enterprise Server, but developers have historically provided source code for sample push applications. Most recently, a sample .NET project was included with the BlackBerry® Plug-in for Microsoft® Visual Studio®. Figure 9 is a screenshot of this application (with some minor enhancements).
Figure 9: Sample BlackBerry Mobile Data System browser push application
During deployment testing, the push utility threw an error when I pointed it to my application. The failure occurred because the utility only accepts HTTP 200 responses, and MSL was redirecting (HTTP 302) to other locations. One of the following two situations was happening.
- The mobile application was configured to only allow BlackBerry smartphones, so the request was redirected to the standard site.
- The mobile application detected the missing authentication cookie and redirected to the cookie provisioning application.
We have a couple of options to resolve this. We can either add a server-side check for a push deployment (what I initially did), or modify the push utility to avoid the GET request to the application if caching is not desired. The latter is more desirable in my opinion since it simplifies the MSL processing logic by avoiding push detection entirely.
However, if you'd like to make MSL more robust to handle this scenario, it doesn't involve a lot of overhead. Since the GET request made to the application is done via HTTP APIs in the sample code, no browser identifier is included by default. If you decide to implement push detection server-side, a missing user agent header would be a reasonable detection mechanism (shown in Listing 9).
if (Request.UserAgent == null)
Server.Transfer("/msl/public/push.html");
Listing 9: Server-side browser push detection
In order to return content from the push request, I transferred to an HTML page in an anonymous access-enabled subdirectory (named public) within the cookie provisioning application. In this configuration, the page could be shared between all mobile applications.
The default behavior of a push is to cache the results on the BlackBerry smartphone for 12 hours in the absence of any expiration headers. After transmitting the deployment page during the push request, it was shown on the BlackBerry smartphone instead of navigating to the application. The only way to open the web application was to choose Refresh from the browser menu. To make sure the BlackBerry smartphone didn't show the cached results, an Expires HTTP header was added to the push application and set to -1 during the push.
ReusabilityCombining the techniques used for authentication, authorization, unsupported browsers, page caching and BlackBerry Enterprise Server push deployment will result in something similar to the code in Listing 10. Some will argue that this is good enough and proceed to copy and paste this code into each master page. Others with larger deployments would argue that this amount of code duplication makes the solution too difficult to maintain. Increasing the reusability of the solution will make it easier to maintain. The following sections explain several methods that can be used to make this solution reusable.
using Company.Web.Mobile;
public partial class _default : System.Web.UI.MasterPage
{
protected override void OnInit(EventArgs e)
{
// MDS browser push detection
if (Request.UserAgent == null)
Server.Transfer("/msl/public/push.html");
// browser detection
if (Request.UserAgent.IndexOf("BlackBerry") == -1)
Response.Redirect("/expense/default.aspx");
// check for cookie existence
HttpCookie authCookie = Request.Cookies[Common.AuthenticationCookieName];
if (authCookie == null)
{
Response.Redirect("/msl/authenticate.ashx?r=" + HttpUtility.UrlEncode(Request.RawUrl));
}
else
{
// authorization check
WindowsPrincipal wp = Common.GetPrincipal(authCookie.Values[Common.CookieValueName]);
if(!wp.IsInRole(@"Company\Expense Users"))
Server.Transfer("unauthorized.html");
}
// Prevent caching
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
base.OnInit(e);
}
}
Listing 10: Master page OnInit with all features enabled
Global Assembly Cache
One of the most obvious solutions for reuse will be to install the MSL assembly into the Global Assembly Cache (GAC) on the web server. This will make it easy to run multiple versions of MSL side by side and also remove the requirement of copying the assembly into the bin directory of each mobile web application. After this is done, your web.config HTTP handler entries will need to be altered to account for the version and strong name of the assembly.
Declarative ConfigurationThe current solution requires that all the application-specific string literals be hard-coded and it will require a rebuild and deployment when changes need to be made. If all of the application-specific information were removed from this code and placed in an external configuration file, the code could be generalized to run for any application.
This is only half of the battle, since the current solution also forces the same logic upon every page, which is not very flexible. If pages exist in the application with different authorization rules, caching requirements or browser restrictions, we'd have to create another master page or litter the existing one with switch statements and other conditional logic, resulting is something difficult to maintain.
Given the complexity of a real-world application, the best way to address these issues is to take advantage of .NET's built-in configuration system by creating our own custom configuration section. If you haven't done this before, you're not alone. My first experience with it began with this project. If you are interested, I recommend John Rista's article, Unraveling the Mysteries of .NET 2.0 Configuration. I also highly recommend opening the machine.config file on your workstation. Every element in a typical web.config is defined in machine.config. Look at the section elements defined within the system.web sectionGroup element. After reviewing this, understanding how a custom configuration section entry works is easier. Figure 10 and Listing 11 displays the implementation I ended up with. Yours would most likely be different (and better). Looking back, I see things I would change.
Figure 10: Class diagram of a custom configuration section for MSL
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="mslsection" type="Company.Web.Mobile.MSLConfigurationSection,
MobileSiteLibrary"/>
</configSections>
<mslsection>
<msl location="*" preventBrowserCaching="true">
<pages authorizationFailed="unauthorized.aspx" standardPage="/expense/default.aspx" />
<authorization>
<allow roles="Company\Expense Users,Company\Accounting" />
</authorization>
</msl>
<msl location="admin.aspx">
<authorization>
<allow roles="Company\Expense Admins" />
</authorization>
</msl>
</mslsection>
<system.web>
<authentication mode="None" />
</system.web>
</configuration>
Listing 11: MSL custom configuration section for implementation in Figure 10
Take a quick look at the MSLConfigurationElement class in Figure 10. I'm reusing the AuthorizationRuleCollection class that .NET uses in system.web (I found out the class name by examining the machine.config by the way). Because of this, authorization rules for MSL become the same as rules for a typical ASP.NET application. Listing 12 is an example of a utility method that verifies users and/or AD group membership given an authorization rule and a Windows principal. Listing 13 uses this method for each rule in the collection. In this listing, UserAccount is a WindowsPrincipal object.
public struct AuthorizationCheck
{
public bool Matched, Authorized;
public AuthorizationCheck(bool matched, bool authorized)
{
Matched = matched;
Authorized = authorized;
}
}
private static AuthorizationCheck isAuthorized
(AuthorizationRule rule, System.Security.Principal.WindowsPrincipal user)
{
AuthorizationCheck ac = new AuthorizationCheck(false, false);
// check user entries
System.Collections.Specialized.StringEnumerator users = rule.Users.GetEnumerator();
while (users.MoveNext() && !ac.Matched)
if (users.Current.Equals(user.Identity.Name, StringComparison.OrdinalIgnoreCase)
|| users.Current.Equals("*"))
ac.Matched = true;
if (!ac.Matched)
{
// check role memberships
System.Collections.Specialized.StringEnumerator roles = rule.Roles.GetEnumerator();
while (roles.MoveNext() && !ac.Matched)
if (user.IsInRole(roles.Current))
ac.Matched = true;
}
// if an allow rule is matched, set
// authorization to true
// if a deny rule is matched,
// or if no match was found,
// leave authorization as the default of false
if (ac.Matched && rule.Action == AuthorizationRuleAction.Allow)
ac.Authorized = true;
return ac;
}
Listing 12: Authorization verification method using WindowsPrincipal and AuthorizationRule
AuthorizationCheck ac = new AuthorizationCheck(false,false);
// if autorization rules found,
// check AD group memberships
if (authCollection != null)
{
foreach (AuthorizationRule rule in authCollection)
{
ac = isAuthorized(rule, UserAccount);
// break out of loop as soon
// as a rule is matched
if (ac.Matched)
break;
}
}
if (!ac.Authorized)
Server.Transfer("unauthorized.html");
Listing 13: Authorization snippet for the AuthorizationRuleCollection
Page Controller
One popular method of reuse is the Page Controller pattern (The C in the popular MVC pattern). A page controller is basically a custom base class that is used by application pages instead of the default ASP.NET page base classes (MasterPage, Page, and MobilePage). The controller inherits from the default base class then injects custom logic before the resulting page is executed. If we had only one page class in ASP.NET to control, it would be a little easier, but in order to control all three, we should probably create an interface that contains all the data we need for our logic. Figure 11 is a class diagram of a possible interface and a page controller master page base class. Notice that the master page class inherits from MasterPage, the default class for master pages in ASP.NET, and also implements MSLPageInterface, a custom interface.
Figure 11: Page Controller interface and master page base class implementation
If you've taken my advice and implemented declarative configuration, one way of implementing Page Controller would be to separate the configuration fetch from the execution of the page during OnInit. This would allow the implementation a little more freedom to alter the configuration values before page processing begins (You'd need to override OnInit for this, of course). The resulting execution pipeline is shown in Figure 12.
Figure 12: Page controller pipeline
Listing 14 is the master page base class shown in Figure 11. Since it implements the MSLPageInterface, a self reference (C# keyword this or Visual Basic's Me) can be passed as an argument to the methods in Listing 15 and Listing 16, which can be used by all base classes.
using System;
using System.Web;
using System.Security.Principal;
using System.ComponentModel;
namespace Company.Web.Mobile
{
public class MSLMasterPage : System.Web.UI.MasterPage, MSLPageInterface
{
protected String _login;
protected String _UPN;
protected WindowsPrincipal _wp;
protected String _authenticationSourcePage;
protected String _standardPage;
protected String _deploymentPage;
protected bool _preventBrowserCaching;
protected string _unauthorizedPage;
public MSLMasterPage()
{
Common.Constructor(this);
}
protected override void OnInit(EventArgs e)
{
Common.Page_Init(this);
base.OnInit(e);
}
[Browsable(false)]
public string UserLogin
{
get { return this._login; }
set { this._login = value; }
}
[Browsable(false)]
public string UserPrincipalName
{
get { return this._UPN; }
set { this._UPN = value; }
}
[Browsable(false), ReadOnly(true)]
public WindowsPrincipal UserAccount
{
get
{
if (this._wp == null)
this._wp = Common.GetPrincipal(this._UPN);
return this._wp;
}
}
[Browsable(false)]
public bool PreventBrowserCaching
{
get { return this._preventBrowserCaching; }
set { this._preventBrowserCaching = value; }
}
[Browsable(false)]
public string AuthenticationSourcePage
{
get { return this._authenticationSourcePage; }
set { this._authenticationSourcePage = value; }
}
[Browsable(false)]
public string StandardPage
{
get { return this._standardPage; }
set { this._standardPage = value; }
}
[Browsable(false)]
public string DeploymentPage
{
get { return this._deploymentPage; }
set { this._deploymentPage = value; }
}
[Browsable(false)]
public string UnauthorizedPage
{
get { return this._unauthorizedPage; }
set { this._unauthorizedPage = value; }
}
}
}
Listing 14: Master page base class for Page Controller
public static void Page_Init(MSLPageInterface mp)
Listing 15: Method declaration for shared OnInit
public static void Constructor(MSLPageInterface mp)
Listing 16: Method declaration for shared constructor
Now that we've moved all the hard work of overriding OnInit into the base class, Listing 17 is an example of what the code-behind class for the web application's master page might look like. This has reduced the task of implementation to the replacement of the default base class. This means we now only have to modify one line of code for the implementation.
public partial class _default : MSLMasterPage
Listing 17: Modified master page code-behind class that implements Page Controller
For a strongly typed reference to the master page from a constituent page, add a MasterType declaration to the aspx file as shown in Listing 18. Once this declaration is made, MSL base class properties or any other custom properties added to the master page will be available from the Master property. Listing 19 is an example of accessing the UserLogin property from a page that uses this master page.
<%@ MasterType VirtualPath="default.master" %>
Listing 18: MasterType declaration in a web page
this.UserName.Text = this.Master.UserLogin;
Listing 19: Use of strongly-typed MSL master page (C#)
Intercepting Filter
Page Controller is very impressive so far, but it is dependent on the developer for enforcement at the page level. Even though it may be the developer's intention to enforce rules across an entire application, it's still possible to ignore the base classes and introduce rogue pages. If this dependency concerns you, or if Page Controller is not an elegant enough solution, you should consider the Intercepting Filter pattern.
Implementation of Intercepting Filter involves writing a custom HTTP module, which is the deepest level of web server integration that ASP.NET offers. When implemented, it is no longer necessary to inherit from or create any base classes. All requests for resources that ASP.NET is configured to manage are filtered through the module. To enable the module, only one entry in the mobile web application's web.config is required. Listing 20 shows an example of web.config entry for a custom HTTP module.
<httpModules> <add name="MSLModule" type="Company.Web.Mobile.MSLHTTPModule, MobileSiteLibrary"/> </httpModules>
Listing 20: Custom HTTPModule registration in web.config
HTTP modules have built-in hooks for authentication and authorization, which is a good fit for the code we've already written for this purpose. If these hooks are used, it makes the downstream page processing code identical to the code web developers are currently using for their applications that use ASP.NET's built-in authentication and authorization. However, you might decide to use this in combination with Page Controller if you liked its custom inherited properties.
IIS 7 HTTP Module EnhancementsDeciding to spend the extra time building an HTTP module has a couple of significant advantages if you are targeting the latest version of IIS (IIS 7 in Windows 2008). Starting in IIS 7, HTTP modules will be able to execute directly within the IIS pipeline. This means that in addition to those extensions registered and handled by ASP.NET, the HTTP module will execute for all resource requests, including images and html files.
Moving all the MSL processing logic into the server pipeline will also abstract the solution from the web application consuming it. Since IIS 7 now supports other frameworks such as PHP, you could conceivably use MSL for a PHP web application. Yes, you read that correctly. That's a pretty good incentive to push for that upgrade to 2008.
ConclusionI hope this article has been of some use for you. If you had abandoned a server-side solution for your mobile applications for the reason I did, the concepts in this article should give you an excuse to revisit the issue. In my environment, this solution delivered SSO for our BlackBerry smartphone deployment successfully. Even if your enterprise doesn't use BlackBerry smartphones, most of these concepts will work with any mobile device and platform.
Tim Jones has specialized in database design, extranets, web applications and systems integration for over ten years. A senior member of Bracewell & Giuliani's Data Systems group, he holds a B.S. degree in Computer Science and recently completed work on a mobile electronic voting system for the Firm. His favorite activities outside of work include spending time with his wife and two daughters, reading and researching, and manning his church's audio mixer on Sunday mornings.
Please email your comments, suggestions and editorial submissions to