Wednesday, October 27, 2010

New Hosting, New Blog

My previous blog hosting was discontinued, so I've had to relocate my blog.  I've decided to use GoDaddy.com for my domain registration and Blogger.com as my blogging software.

Friday, October 2, 2009

Handling a leftover Principal Identity with WebOrb for .NET

In order to authorize calls to a service, WebOrb for .NET stores a principal object created by the authentication handler. The IPrincipal interface has the IsInRole(roleName) method which is responsible for checking if the user represented by the principal’s identity belongs to a role. System administrators can secure individual methods, classes or namespaces using the WebORB configuration file or the management console. Upon authenticating a user, WebOrb stores the Principal Identity in a session variable and subsequent calls make use of the rolebased authorization.
The Problem
The following scenario demonstrates the problem I was having:
  • First, a user successfully authenticates and logs into the application
  • The user then closes the application browser tab without logging out.
  • The browser had multiple tabs open
  • A user reopens the application in a new tab and attempts to log in
  • The user supplies invalid credentials
  • A WebORBAuthenticationException is thrown, preventing WebOrb from creating a new Principal Identity
Everything should be fine since a new Principal Identity wasn’t created right? Wrong.
The previous principal is still stored in the session and is used for authorizing other service calls. Since the user had previously authenticated, that user’s roles will be used for authorization.
The Solution
At the beginning of my CheckCredential method in my AuthenticationHandler, I simply call:
HttpContext.Current.Session.Clear();
Everything works as expected. Since I don’t have any other session information, the Clear() method fulfills my need. If you have other session information, you may need to loop through the session looking for variables of type System.Security.Principal.GenericIdentity and Weborb.Security.Credentials. Remove both of those variables.
Note: I did have some issues using System.Web.HttpContext.Current in my service library. It was unrecognized. I simply had to add a reference to System.Web and all was ok.

Wednesday, September 30, 2009

Using WebOrb Role-Based Security with the Flex Mate Framework

While implementing a flex-based login solution that used WebOrb & .NET role-based security, I ran into problems attempting to utilize Mate’s RemoteObjectInvoker.
The History
In order to implement .NET’s role based security, we need to Authenticate and Authorize our users. Per WebOrb’s Documentation:
“Authentication is the process of verifying user credentials and establishing user identity. Authentication’s goal is to verify that the user is who they say they are. A result of a successful authentication in .NET is typically an instance of the System.Security.Principal.IPrincipal interface. A .NET principal carries the identity of the authenticated user. Additionally, it can check if a user belongs to a particular role. When a principal is associated with a thread, .NET automatically performs code-access security checks for the methods invoked by the thread and thus enforces role-based security.”
Flex provides an API for sending the authentication credentials in a RemoteObject. SetCredentials tells Flex to send the credentials with the next service call to authenticate and create an identity. Once an identity is established, its used for all other service calls that use the same ChannelSet until the logout() function is used.
public function setCredentials( userid:String, password:String ) : void
public function logout() : void
In order to handle authentication, WebOrb registers a custom handler for all setCredentials() calls. Excellent information can be found in the WebOrb documentation on how to set the AuthenticationHandler.
The Problem
Implementing the Mate Framework, I use RemoteObjectInvoker to make calls to my .NET services exposed by WebOrb. Mate provides properties (username and password) on the ServiceInvoker class which the RemoteObjectInvoker extends. So, I attempted the following:
<services:Services id="services"/>
<RemoteObjectInvoker instance="{services.loginService}" 
    method="getUserData"
    username="{event.username}"
    password="{event.password}"/>
Note: Services contains a list of my RemoteObjects per Mate’s Suggested Best Practices.
In the event the user was successfully authenticated, this worked great. If the user is not authenticated, a fault is returned. The problem is that when new credentials are entered, they are not sent to the server. Instead, the original credentials are sent.
Another problem I ran into is logging out the user. I couldn’t find a means to logout a user anywhere in the RemoteObjectInvoker.
The Solution - InlineInvoker
<?xml version="1.0" encoding="utf-8"?>
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" 
  xmlns="http://mate.asfusion.com/" 
  xmlns:services="services.*">
 <mx:Script>
  <![CDATA[
   import events.LoginEvent;
 
   public function setCredentials(username:String, password:String):void
   {
    services.userService.setCredentials(username, password);
   }
   public function logout():void
   {
    services.userService.logout();
   }
  ]]>
 </mx:Script>
 
 <services:Services id="services"/>
 
 <EventHandlers type="{LoginEvent.LOGIN}" debug="true">
 
  <InlineInvoker method="setCredentials" arguments="{[event.hcoid, event.password]}"/>
  <RemoteObjectInvoker instance="{services.userService}" method="getUserData" arguments="{[event.username]}">
   <resultHandlers>
   ...
   </resultHandlers>
  </RemoteObjectInvoker>
 </EventHandlers>
 
 <EventHandlers type="{LoginEvent.LOGOUT}" debug="true">
  <InlineInvoker method="logout"/>
 </EventHandlers>
</EventMap>
The solution is pretty simple and should probably be implemented into a Mate Extension (I’m thinking something like a CredentialInvoker or maybe an extension to the RemoteObjectInvoker). There might very well be a better way to do this or it could already be built in. Regardless, the solution wasn’t’ instantly clear for me, so hopefully this will help someone else.