Training



Disconnected Authentication in a DevForce App Level 300
DevForce Enterprise with Business Object Server
Sep 11, 2007

Note: A previous Tech Tip, “Authentication in DevForce”, outlined the basics. This tip assumes familiarity with the material presented there. You can find that Tech Tip, and others, at http://www.ideablade.com/techtips_summary.html.

Your n-tier app naturally requires that a user log in and be authenticated in order to access data in the corporate data stores. Based on the user’s identity, certain data is made available and other data is not; the same is true for facilities in the user interface. 

But your users also need to run when disconnected from the data servers. And even in that circumstance, there is still sensitive data at issue – that stored in one or more entity set files on the local machine. You’d also like the UI to function more or less as when connected, giving the user selective access to appropriate facilities based on her identity. 

How to do it? 

The Login() method of the DevForce PersistenceManager causes the PersistenceManager to request a login operation by the DevForce PersistenceServer. The latter searches for a server-side class that implements DevForce’s IPersistenceLoginManager interface; upon finding it, it calls another Login() method in that class. Therefore, for login to run successfully, the PersistenceManager must be able to connect to a PersistenceServer.  It will always be able to do so in an application deployed client-server, because in that configuration the PersistenceServer runs on your local machine. In the much more secure n-tier deployment configuration, however, the PersistenceServer runs on a remote machine. 

If the PersistenceManager cannot connect to the PersistenceServer, every call to
PersistenceManager.Login() will result in an exception. Therefore, your code that calls PersistenceManager.Login() will either need to test whether it can connect to the PersistenceServer before attempting PersistenceManager.Login(), or handle the exception that occurs from a failed attempt, and then perform authentication by other means. 

A typical server-based authentication might proceed something like the following: 

  1. The end user supplies a user name, password, and possibly a domain name to a login form.
  2. This information (after the password is hashed) is folded into an ILoginCredential object and passed to the PersistenceManager.Login() method.
  3. PersistenceManager.Login() forwards the login request to the PersistenceServer, which finds an IPersistenceLoginManager class and runs its Login() method, passing the credential and a server-side instance of the PersistenceManager.
  4. PersistenceServer.Login() performs authentication of the user described in the credential by any desired means. It might, for example, get an instance of a User object by calling PersistenceManager.GetEntity() and specifying as a criterion that the UserName of the retrieved User must match the user name included in the credential. If such a User is found and retrieved successfully, the authentication code then compares its password (stored in hashed form in the database) to the hashed password passed in with the credential. If the two match, the PersistenceServer.Login() returns to the originating PersistenceManager an IPrincipal object (representing the authenticated user) , wrapped in a DevForce SessionBundle. The PersistenceManager then attaches the authenticated IPrincipal to the client-side thread (whence it can be accessed, any time, as System.Threading.Thread.CurrentPrincipal). 

Since the above authentication process depends upon successful access to the back-end database (to retrieve the password and other information about the would-be user), it breaks down entirely in disconnected mode. One certainly would not want to solve this conundrum by retrieving and maintaining, client-side, a complete table of Users with passwords, or even a subset thereof!  So how to authenticate a prospective user when disconnected? 

As we mentioned in the opening comments, the most critical resource at risk in a disconnected mode is the data previously downloaded (while connected) and stored in a local entity set file.  Your application certainly took care to encrypt that before writing it out [see the “Non-Tutorial Solution” in the 100-series instructional unit, “Supporting Disconnected Users”, for a sample of encrypting and decrypting an EntitySet].   

Let’s assume, further, that you used the end user’s login password (or a hash of it) as the encryption key for scrambling the entity set.  If you did, then the only way the current would-be user can successfully access that data will be to supply the same password, so that it can be used to decrypt the entity set.  So there’s our authentication test: if we can turn the encrypted entity set back into a valid PersistenceManager cache, then they must have supplied the correct password! 

Getting Security Role Information While Disconnected 

Information about the user’s security roles was another tidbit we obtained from the back-end database when authenticating in connected mode. How shall we get it when disconnected? 

Well, how about we make sure it’s part of the cache that we save while we are connected?  All we need is a User object (complete with security role information) representing the authenticated user.  Although it probably wouldn’t hurt to include the user’s password in that User object (since we’re encrypting it for storage anyway), there’s really no need to do so. It’s only the role information that we will need later when authenticating disconnected.   After performing our authentication-by-decryption, we can retrieve the User’s role information from the decrypted cache. We can then create our own IPrincipal object, complete with role information, and attach it to the running client-side thread! 

Things That Go “Bump” When Disconnected 

At that point most of our regular code should work just fine, with the notable exception of things that must have access to the server.  Those include: 

  • Data retrievals that specify a trip to the datasource (specifically, those whose FetchStrategy is DatasourceOnly or DatasourceThenCache).
  • Pass-Thru and Stored Procedure Queries
  • Saves to the datasource
  • Remote Procedure Calls
  • Push Notifications 

Queries that specify a FetchStrategy of DatasourceOnly or DatasourceThenCache will trigger an exception when called in disconnected mode, as will Pass-Through and Stored Procedure Queries.  Your app must be ready for these exceptions, and must provide some reasonable experience to the end user, even if it is just gently informing them that they can’t do such and such while disconnected.  Of course, it’s quite a bit better if your app disables or makes invisible controls that aren’t appropriate when disconnected. 

As for saves, you might want to make your Save() method call PersistenceManager.SaveChanges() when connected, but call PersistenceManager.SaveEntitySet() when not connected.  In the latter case, be sure you communicate effectively with your end user so they know what they have and haven’t accomplished. It won’t be good if they think they’ve saved to the back-end database, and that their updates are now available to other users, when in fact they aren’t! 

A Sample Solution 

A new instructional unit, “Security_Disconnected Authentication” will be available in the 300 Advanced Series as of the 3.5.4 DevForce release (scheduled for a bit past mid-September, 2007). It illustrates the approach to disconnected authentication discussed in this article. We’ll be happy to hear your feedback!

 
 

All Tech Tips