Tuesday, May 11, 2010

Supporting Multiple Logins in a Windows Application

One of the issues that can come up when you're developing a line of business application is the ability to support a user logging out, then logging back in as a different user. This really only becomes an issue when you're trying to access resources that require an IPrincipal to be set. The first thing someone does in this situation is make a call to AppDomain.CurrentDomain.SetThreadPrincipal  with the principal of the user who just logged in. However, the second time you call this method (i.e. the user logged out and logged back in), you'll get a PolicyException: Default principal object cannot be set twice.

Don't even try it
You can always set the current thread's principal by calling Thread.CurrentPrincipal. So why can't you just set this on the UI thread each time the user logs in? This trick will not work because the .NET framework captures the execution context (i.e. credentials and other stuff) every time something is dispatched to the UI thread. It then restores that execution context when the message is being handled, and throws away the context when it's done. In other words you can set the principal, but it will be thrown away before the next UI event. .NET does this because it could be a potential security flaw. What if someone handled a UI event in your application and decided to post some work to be done that you didn't want them to have permission to do? Trapping the execution context keeps the security of the code that posts the work, the same as the code that executes the work.

My Solution
There are many solutions to this problem, and coming up with them is not hard. I just thought I'd share one that you might find elegant. The idea is to store the IPrincipal of the logged in user in your own class, and set the current thread's principal before making any calls that require that authentication. This sounds like a pain, but it doesn't have to be. You can setup an IDisposable object that when created, sets the current thread's IPrincipal and then restores the previous value when the object is disposed of.
public class ClientSecurityContext
{
    public static IPrincipal CurrentPrincipal
    {
        get;
        set;
    }

    public static IDisposable CreateSecurityContext()
    {
        //
        // Save a copy of the previous IPrincipal so we 
        // can restore it when the caller is finished.
        //
        IPrincipal previous = Thread.CurrentPrincipal;

        //
        // Set the current thread's principal.
        //
        Thread.CurrentPrincipal = CurrentPrincipal;

        //
        // Return a notifier that will call the action 
        // when the caller disposes of it. This will be 
        // used to restore the previous IPrinicpal back 
        // on the current thread.
        //
        return new DisposeNotifier(
            () => Thread.CurrentPrincipal = previous);
    }
}

The above code makes use of a handy DisposeNotifier object that calls a delegate when it's disposed of. It's really just my laziness in creating a special class just for this purpose. You can see how a call using this would look in action.
private string[] GetEmployeesUsingService()
{
    using (ClientSecurityContext.CreateSecurityContext())
    {
        //
        // This is where you would make a call that 
        // requires a particular IPrincipal.
        //
        return new string[]
        {
            Thread.CurrentPrincipal.Identity.Name,
            "Ben Bernanke",
            "Tim Geithner"
        };
    }
}

Download the sample project here if you're needing to support something like this.

2 comments:

  1. I see you still Phongize your code (as do I).

    - Aaron

    ReplyDelete
  2. This's what I need.thank you.

    ReplyDelete