Adobe Flex and ASP.NET authentication using HTTPService and IHttpHandler

June 27, 2008 at 8:45 AMAmer Gerzic

Lately, Adobe Flex has been getting more and more attention in programming community. Especially after the launch of open source version of Flex SDK developers are able to make rich Internet applications (RIA) using Flex, which (as everybody knows) produces a flash file (swf) that can be used in any web application. The article will focus on the following topics:

  1. Communication between Action Script (HTTPService) and .NET (HTTP Handler);
  2. Security - securing HTTP Handler calls from unauthorized access;
  3. ASP.NET Forms Authentication and Authorization through Flex;
  4. ASP.NET Handlers and session management;

It is assumed that the reader is familiar with basic concepts of ASP.NET handlers, forms authentication, and Adobe's Action Script.

Flex Application

Flex application is actually very simple. The application consists of couple of buttons/text boxes and two HTTPService objects, which are utilized for communication with ASP.NET handlers. Following is Flex MXML code:

<?xml version="1.0" encoding="utf-8"?>
<custom:FlexASPAuthentication 
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    layout="absolute"
    xmlns:custom="app.*" width="400" height="250">
    
    <mx:HTTPService id="httpLoginHandler" />
    <mx:HTTPService id="httpServerTimeHandler" />
    
    <mx:Button x="141.5" y="159" label="Get Server Time" id="getServerTimeButton"/>
    <mx:Text x="141.5" y="189" id="resultText" width="117"
             textAlign="center" color="#FFFFFF"/>
    <mx:TextInput x="131" y="33" id="usernameText" width="210"/>
    <mx:TextInput x="131" y="59" id="passwordText" width="210"/>
    <mx:Button x="285" y="89" label="Login" id="loginButton"/>
    <mx:Label x="61" y="35" text="Username"/>
    <mx:Label x="67" y="61" text="Password"/>
    <mx:HRule x="10" y="129" width="380" height="22"/>
    
</custom:FlexASPAuthentication>

As we can see from the code above, the application contains httpLoginHandler and httpServerTimeHandler; two HTTPService objects that are utilized for login and accessing the server time. The latter is simply for demonstration purposes i.e. the article will demonstrate that Flex application will not be able to connect to protected handler (server time handler) unless the proper authentication is performed. Following Action Script code is used to connect to server time handler.

private function OnGetServerTimeButtonClick(event:Event):void
{
    /* Populate http service parameters */
    httpServerTimeHandler.url="~/ServerTimeHandler.ashx";
    httpServerTimeHandler.method = "GET"; 

    /* Add event handler for asynchronous communication */
    httpServerTimeHandler.addEventListener(ResultEvent.RESULT, OnServerTimeHandlerResult);
    httpServerTimeHandler.addEventListener(FaultEvent.FAULT, OnServerTimeHandlerFault); 

    /* Send request - it will fail if we are not logged in */
    httpServerTimeHandler.send();
} 

private function OnServerTimeHandlerResult(event:ResultEvent):void
{
    resultText.text = event.result.toString();
} 

private function OnServerTimeHandlerFault(event:FaultEvent):void
{
    Alert.show("Could not retrieve server time. Are you logged in?", "Error");
}

Second HTTPService object, httpLoginHandler, is responsible for authentication. Once the authentication is performed, flex application is able to access all protected resources like server time handler. Let's look at following piece of code:

private function OnLoginButtonClick(event:Event):void
{
    /* Populate http service parameters */
    httpLoginHandler.url="~/LoginHandler.ashx";
    httpLoginHandler.method = "GET"; 

    /* Add event handlers for asynchronous communication */
    httpLoginHandler.addEventListener(ResultEvent.RESULT, OnLoginHandlerResult);
    httpLoginHandler.addEventListener(FaultEvent.FAULT, OnLoginHandlerFault); 

    /* Pass username and password as parameters */
    var param:Array = new Array(2);
    param[0] = usernameText.text;
    param[1] = passwordText.text; 

    /* Send request to be logged in */
    httpLoginHandler.send(param);
} 

private function OnLoginHandlerResult(event:ResultEvent):void
{
    var res:String = event.result.toString();
    if(res == "1")
        Alert.show("Login success!");
    else Alert.show("Login failed!");
} 

private function OnLoginHandlerFault(event:FaultEvent):void
{
    Alert.show("Could not log in!", "Error");
}

The code above, is very similar to server time handler except for parameter passing. Parameters are passed as an array of strings (in this case two strings for username and password). In this way, ASP.NET handler is receiving proper parameters for authentication. To verify that the authentication was performed properly, we are evaluating result event returned by the httpLoginHandler object. At this point if the ASP.NET handler returns "1" the authentication was successful, otherwise the authentication was not successful. 

ASP.NET Login Handler

As demonstrated in previous paragraph, Flex application is communicating with ASP.NET handler to perform necessary authentication. Obviously, the web application must be aware that all subsequent calls from the same client are authorized. In order to accomplish desired result, we have to perform all authorization steps manually. Following code does it:

public void ProcessRequest(HttpContext context)
{
    /* Get request parameters */
    string username = context.Request.Params[0];
    string password = context.Request.Params[1]; 

    /* Validate user using membership provider */
    if (Membership.ValidateUser(username, password))
    {
        /* Create session ticket */
        FormsAuthenticationTicket ticket =
            new FormsAuthenticationTicket(
                1,
                username,
                DateTime.Now,
                DateTime.Now.AddMinutes(20),
                false,
                Guid.NewGuid().ToString()); 

        /* Encrypt the ticket */
        string encrypted_ticket = FormsAuthentication.Encrypt(ticket); 

        /* Create cookie */
        HttpCookie cookie = new HttpCookie(
            FormsAuthentication.FormsCookieName,
            encrypted_ticket); 

        /* Add cookie */
        context.Response.Cookies.Add(cookie); 

        /* Send response that we are authenticated */
        context.Response.Write("1");
        context.Response.End();
        return;
    } 

    context.Response.Write("0");
    context.Response.End();
}

Once the handler is contacted by the client the method ProcessRequest is executed. As pointed out earlier, the client is sending username and password parameters to the handler to be verified. In order to verify username and password we are using Membership class. If the user is authenticated, we are ready to create authentication ticket that will be stored in a cookie for authentication of subsequent client calls. At this point it is important to stress that ASP.NET handler implements System.Web.SessionState.IRequresSessionState interface. It is not crucial for functioning of authorization, but it provides handler with access to application session in case we need to access those resources. Once the authentication ticket is created and stored in a cookie, the client is authenticated and we can send the response denoting success.

ASP.NET Server Time Handler

As mentioned earlier the server time handler is for demonstration purposes only and does not present crucial part of the application. Following code is executed to return server time:

public void ProcessRequest(HttpContext context)
{
    if (context.User.Identity.IsAuthenticated)
    {
        /* Prevent cashing */
        context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        context.Response.Cache.SetNoStore();
        context.Response.Cache.SetExpires(DateTime.MinValue); 

        /* Send server time */
        context.Response.Write(DateTime.Now.ToLongTimeString());
        context.Response.End();
    }
    else
    {
        /* User not authenticated so access is forbidden */
        context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
        context.Response.End();
    }
}

As the code shows, we are simply retrieving current server time and sending it to the client. However, the first step is to verify clients credentials. If the client is not authenticated we are returning HTTP code "forbidden". It is very important to secure all resources explicitly in this way because securing resources in web.config only using <location> tag does not provide full protection.

In addition, it is important to stress the prevention of caching. First when I wrote the application I did not prevent cashing so that Flex application displayed always cached result, which caused (besides frustration) the time to be constant. Caching simply ensures that each client call is executed on the server side and that most recent results are returned.

Web.Config

Following is web.config:

<?xml version="1.0"?> 

<configuration> 

  <appSettings/>
  <connectionStrings/> 

  <system.web>
    <!-- 
            Set compilation debug="true" to insert debugging 
            symbols into the compiled page. Because this 
            affects performance, set this value to true only 
            during development.
        -->
    <compilation debug="true" />
    <!--
            The <authentication> section enables configuration 
            of the security authentication mode used by 
            ASP.NET to identify an incoming user. 
        -->
    <authentication mode="Forms">
      <forms path="/" loginUrl="Default.aspx" timeout="20" />
    </authentication>
    <!--
            The <customErrors> section enables configuration 
            of what to do if/when an unhandled error occurs 
            during the execution of a request. Specifically, 
            it enables developers to configure html error pages 
            to be displayed in place of a error stack trace. 

        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
            <error statusCode="403" redirect="NoAccess.htm" />
            <error statusCode="404" redirect="FileNotFound.htm" />
        </customErrors>
    --> 

    <membership defaultProvider="MyMembershipProvider">
      <providers>
        <clear/>
        <add name="MyMembershipProvider"
             type="FlexAuthentication.Provider.MyMembershipProvider"/>
      </providers>
    </membership>
    <roleManager enabled="true" defaultProvider="MyRoleProvider">
      <providers>
        <clear/>
        <add name="MyRoleProvider" type="FlexAuthentication.Provider.MyRoleProvider" />
      </providers>
    </roleManager> 

    <httpHandlers>
      <add verb="*" path="LoginHandler.ashx" 
           type="FlexAuthentication.Handler.LoginHandler" />
      <add verb="*" path="ServerTimeHandler.ashx"
           type="FlexAuthentication.Handler.ServerTimeHandler" />
    </httpHandlers> 

  </system.web> 

  <location path="ServerTimeHandler.ashx">
    <system.web>
      <authorization>
        <deny users="?"/>
        <allow roles="Admin,User"/>
        <deny users="*"/>
      </authorization>
    </system.web>
  </location> 

</configuration>

As shown above we are using forms authentication with custom membership/role provider. Custom membership/role provider is implemented for simplicity and it contains hard-coded account with username "admin" and password "admin". We are also registering http handlers used for authentication. Finally we are using <location> tag to protect ServerTimeHandler from unauthorized access, which can be left out because we are already checking for user authorization.  

Demo

Try live demo here!

Download

Download Sample Application (260.89 kb)

Posted in: Adobe Flex | ASP.NET | C#

Tags: , , , , ,

Add comment

biuquote
Loading