views:

16

answers:

0

I have recently been prototyping an application using WPF and Client Application Services. I am trying to use all three features of the Client Application Services : Forms Authenticiation, Role Security, and Client Profile. I have managed to get everything working, but I feel like my solution is a kludge and want a better way.

For the discussion assume the following:

I have two WPF windows:

The first window is my main Application form. It has two buttons "Login" and "Check Thread Principal"

The second window is a login form which implements the IClientFormsAuthenticationCredentialsProvider interface to use the GetCredentials method to return a new ClientFormsAuthenticationCredentials object with the entered username and password.

The application starts, the Main Window is loaded and the user clicks the login button. In the button click event, a call is made to System.Web.Security.Membership.ValidateUser(String.Empty, String.Empty). This causes the GetCredentials method in my login form to be called which in turns shows the login window. The code in the button click event of the main window waits until the user enters information in the login window.

Once the user submits the login information, the login form closes, the flow of execution is returned to the main window login click event. If the username and password submitted were valid, the Thread.CurrentPrincipal has the appropriate FormAuthentication Principal.

If we go back to the main window form and click the "Check Thread Principal" button, and check the Thread.CurrentPrincipal on the button click event, we see that the Thread.CurrentPrincipal is no longer the FormsAuthentication Principal, but instead a Generic Windows Principal.

In my research, I've found a couple of ways to handle this:

The first is to call AppDomain.CurrentDomain.SetThreadPrincipal(Thread.CurrentPrincipal) just after the user is validated. This will ensure that the FormsAuthentication Principal sticks around for the lifetime of the application. The problem with this is that I cannot set it twice. So if I call the ClientFormsAuthenticationMembershipProvider.LogOut() method, and try to do the login process again. I get an "Default principal object cannot be set twice" execption.

The second method, and the method I chose for my prototype is to create a class called UserAuthentication. In this class is a static property called Current which returns a singleton of type UserAuthentication. In the UserAuthentication class I maintain my own CurrentPrinipal and Original Principal properties which are of type IPrincipal. I also have LogIn and LogOut methods which manage the calls to System.Web.Security.Membership.ValidateUser(String.Empty, String.Empty) and ClientFormsAuthenticationMembershipProvider.LogOut() and maintain the state of my encapsulated CurrentPrinipal and Original Principal properties.

By doing this as a singleton, I can still access the IsInRole functionality by calling CommonAssembly.UserAuthentication.Current.CurrentPrincipal.IsInRole("SomeRole"). And I can implement the client profile feature by ensuring that just before I call Properties.Settings.Default.Save(), I call System.Threading.Thread.CurrentPrincipal = CommonAssembly.UserAuthentication.Current.CurrentPrincipal.

This works, but I feel like it is a kludge, and in my research I have not been able to find an adequate solution. I understand that part of the problem is how WPF handles execution context, but I feel like there MUST be a better way.

I hope my explaination and inline code examples aren't too confusing. The code required to implement all of this is more than I can put in a post.

FYI. I have seen similar threads on stack overflow, but none really adequately answer the question. I have also seen client application services implementation examples on the web, but none specifically using non-silverlight WPF.

So is there a better method that allows me to use Thread.CurrentPrincipal for login/logout which obviates the need for me to maintain my own Principal?

I appreciate any help or insight that anyone can provide.

Kind Regards,

Bernie