Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles

We take a look at planned support for parallel programming for both managed and native code in the next version of Visual Studio.

Stephen Toub and Hazim Shafi

MSDN Magazine October 2008

...

Read more!

Matt Milner takes a look at some of the challenges and techniques related to testing Windows Workflow Foundation activities, workflows, and associated components.

Matt Milner

MSDN Magazine November 2008

...

Read more!

Bryan Sullivan discusses the new SDL for Web applications and Agile projects with more compressed release cycles.

Bryan Sullivan

MSDN Magazine November 2008

...

Read more!

John Papa tackles questions about calling services from Silverlight 2 applications.

John Papa

MSDN Magazine November 2008

...

Read more!

Our security experts present 10 vulnerable pieces of code. Your mission is to find the holes (a.k.a. bad security practices) in the code.

Michael Howard and Bryan Sullivan

MSDN Magazine November 2008

...

Read more!

Also by this Author

Leasing and sponsorship is the solution for managing the lifecycle of a remote object in .NET. Each object has a lease that prevents the local garbage collector from destroying it, and most distributed applications rely upon leasing. There are several ways in which objects and clients can extend the lease, including dedicated sponsor objects. In this article, the author explains leasing, shows how to configure it, and how it relates to the various remoting activation models. He then discusses design guidelines and options, along with their impact ...

Read more!

In this article Juval Lowy describes how exciting new features in Visual Studio 2005, that will improve your overall productivity compared to the first version of C#, so you can write cleaner code faster.

Juval Lowy

MSDN Magazine Visual Studio 2005 Guided Tour 2006

...

Read more!

C# 2.0 introduces a wealth of exiting new features, such as generics, iterators, partial classes and anonymous methods. While generics are the most talked-about feature especially for former classic C++ developers, the rest of the new features are important additions to your .NET development arsenal, enhancing power and improving overall productivity. This article is dedicated to all the new C# 2.0 capabilities besides generics to give you a good overall picture of the upcoming features.

Juval Lowy

MSDN ...

Read more!

The next version of COM+, COM+ 1.5, offers many improvements over COM+ 1.0. A comprehensive user interface that displays more data for each application as well as complete support for legacy components make the management of existing applications easier and more efficient. Enhanced queueing support provides more flexibility for managing queued calls, and pooling and recycling means better application lifetime management. Application partitioning in COM+ 1.5 surpasses that offered in COM+ 1.0, and transaction isolation can be configured for safer ...

Read more!

Role-based security allows administrators to assign access permissions to users based on the roles they play rather than on their individual identities. These privileges can be used to control access to objects and methods, and are easier to identify and maintain than user-based security. The .NET Framework provides two role-based security models, which are exposed as two namespaces: System.Enterprise-Services and System.Security.Permissions. Presented here is a comparison of the two options and a discussion of when each is the right choice. ...

Read more!

Popular Articles

C# allows developers to embed XML comments into their source files-a useful facility, especially when more than one programmer is working on the same code. The C# parser can expand these XML tags to provide additional information and export them to an external document for further processing. This article shows how to use XML comments and explains the relevant tags. The author demonstrates how to set up your project to export your XML comments into convenient documentation for the benefit of other developers. He also shows how to use comments ...

Read more!

The MVP pattern helps you separate your logic and keep your UI layer free of clutter. This month learn how.

Jean-Paul Boodhoo

MSDN Magazine August 2006

...

Read more!

James Avery does it again with his popular list of developer tools. This time he covers the best Visual Studio add-ins available today that you can download for free.

James Avery

MSDN Magazine December 2005

...

Read more!

Kenny Kerr sings the praises of the new Visual C++ 2008 Feature Pack, which brings modern conveniences to Visual C++.

Kenny Kerr

MSDN Magazine May 2008

...

Read more!

Here are some design patterns that allow you to achieve higher cohesion and looser coupling for more flexible, reusable applications.

Jeremy Miller

MSDN Magazine October 2008

...

Read more!

Our Blog

Silverlight and SharePoint provide a simple, yet powerful, infrastructure for building intranet and extranet applications with sophisticated user interface designs and interactions.

In the November 2008 issue of MSDN Magazine, Steve Fox and Paul Stubbs demonstrate how to build a SharePoint Web Part as a wrapper for a Silverlight application.

...

Read more!

Choosing the best alternative is a common task in software development and testing. A group of beta users may need to choose the best user interface from a set of prototypes. Or imagine the members of an open source project voting for a policy.

In the November 2008 issue of MSDN Magazine, Dr. James McCaffrey describes five of the ...

Read more!

Whether Web services are supplied by third parties or custom services hosted on the same server as your Silverlight app, Silverlight can request data, consume the data, and pass data back and forth between the client application and Web services.

In the November 2008 issue of MSDN Magazine, John Papa explains how to call and use data ...

Read more!

One stumbling block that developers encounter with asynchronous programming is that they become so concerned with getting concurrency right that they forget the core simplicity of the program.

F# lets you separate simple programs from the concurrent control flow and reveal the simplicity and readability of your core program.

In the October 2008 issue of MSDN Magazine, Chance Coble demonstrates ...

Read more!

Visual Studio 2008 Team Foundation Server Build (better known as Team Build) is a core feature of Team Foundation Server 2008. Microsoft designed Team Build to be an industrial-strength build automation tool.

In the November 2008 issue of MSDN Magazine, Brian A. Randell introduces you to Team Build 2008 and walks you through the process ...

Read more!

Security
Unify Windows Forms and ASP.NET Providers for Credentials Management
Juval Lowy

This article is based on prerelease versions of the .NET Framework 2.0 and Visual Studio 2005. All information contained herein is subject to change.
This article discusses:
  • .NET user credentials management
  • The ASP.NET 2.0 security providers model
  • Architecting custom security for Windows Forms
  • Code access security considerations for client applications
This article uses the following technologies:
Windows Forms, ASP.NET, Web services, C#
Code download available at: Security.exe (220 KB)
Browse the Code Online
Windows® Forms intranet applications often resort to storing user credentials in a database even when deployed in a homogenous Windows environment. This stems from the limitations of using Windows accounts. For ASP.NET applications, the Microsoft® .NET Framework 2.0 provides out-of-the-box custom credentials management. You can easily authenticate users and authorize them, without ever having to use Windows accounts. This saves developers valuable time and effort, not to mention providing a high-quality, secure solution.
This article presents a set of interacting helper classes that enable a Windows Forms application to use the ASP.NET credentials management infrastructure, with the same ease as if it were an ASP.NET application. Doing so provides the productivity benefits of ASP.NET as well as a unified credentials store, regardless of the application user interface that is being employed. I'll also present some .NET programming techniques and various other design and programming best practices.

ASP.NET Security Infrastructure
Before showing you how to take advantage of the ASP.NET user credentials management, you need to learn a bit about it. Out of the box, ASP.NET applications can store their custom user credentials in either SQL Server or SQL Server Express. That said, the credentials management architecture is that of a provider model, and you can easily add other storage options, such as a Microsoft Access database. ASP.NET developers can configure their application directly from within Visual Studio® 2005. When selecting ASP.NET Configuration from the Web site menu, Visual Studio 2005 browses to the ASP.NET administration pages and allows you to configure various security parameters, as shown in Figure 1. Here you have access to the site management provider where you can select which store to use, such as a SQL Server or SQL Server Express database. The administration pages also allow you to create new users and delete existing ones, create new roles and delete existing ones, and allocate users to roles. You should note that the same database tables are used to store the user information from multiple ASP.NET applications. As a result, each user or role record is also associated with a particular application name.
Figure 1 ASP.NET Application Security Configuration 
To use the SQL Server provider, run the setup file aspnet_regsql.exe found under \WINDOWS\Microsoft.NET\Framework\Version\. The setup program will create a new database called aspnetdb, which contains the tables and stored procedures required to manage the credentials.
At run time, ASP.NET will authenticate the callers using the credentials in the database. The easiest way to allow a user to provide the credentials is to drop a Login control on the Web Form. The Login control collects the user name and password from the user and authenticates using a class called MembershipProvider, defined as the following:
public abstract class MembershipProvider : ProviderBase
{
   public abstract string ApplicationName{get;set;}
   public abstract bool ValidateUser(string name, string password);
   ...
}
The goal of MembershipProvider is to encapsulate the actual provider used and the details of the actual data access, as well as to enable changing the membership provider without affecting the application itself. Depending on the configured security provider, the Login control will use a concrete data access class such as SqlMembershipProvider, which derives from the MembershipProvider class, when using SQL Server:
public class SqlMembershipProvider : MembershipProvider
{...}
However, the Login control interacts only with the MembershipProvider base class. The Login control obtains the required membership provider by accessing the Provider static property of the Membership class, defined as:
public sealed class Membership
{
   public static string ApplicationName{get;set;}
   public static MembershipProvider Provider{get;}
   public static bool ValidateUser(string userName, string password);
   ...
}
The Membership class offers many members, which support every aspect of user management. Membership.Provider returns an instance of the configured provider based on settings in the Web application configuration file.
I'll be focusing on two members of the Membership class. The first is the ApplicationName property, which is used to set and retrieve the application name. The second is the ValidateUser method, which authenticates the specified credentials against the store, returning true if they match and false otherwise. The static Membership.ValidateUser method is shorthand for retrieving the configured provider from Membership.Provider and using its instance ValidateUser method.
You can also apply role-based security to authorize operations or access to resources. The aspnetdb database stores the mapping of users to roles. Once the user is authenticated, ASP.NET will set the User property of the HTTP context and the page to a custom security principal object called RolePrincipal:
public sealed class RolePrincipal : IPrincipal
{...}
RolePrincipal uses the abstract class RoleProvider:
public abstract class RoleProvider : ProviderBase
{
   public abstract string ApplicationName{get;set;}
   public abstract bool IsUserInRole(string username, string roleName);
   public abstract string[] GetRolesForUser(string userName);
   ...
}
The ApplicationName property of RoleProvider binds the role provider to the particular application. The IsUserInRole method verifies the user's role membership. The GetRolesForUser method returns all the roles that a specified user is a member of.
Like the membership providers, depending on the configured security provider, RolePrincipal uses a corresponding data access class such as SqlRoleProvider to authorize the caller:
public class SqlRoleProvider : RoleProvider
{...}
You can obtain the required role provider by accessing the Provider static property of the Roles class, defined as:
public sealed class Roles
{
   public static string ApplicationName{get;set;}
   public static string[] GetRolesForUser(string username);
   public static bool IsUserInRole(string username, string roleName);
   public static RoleProvider Provider{get;}
   ...  
}
Both Roles.GetRolesForUser and Roles.IsUserInRole are shorthand, using the Roles.Provider property internally. Roles.Provider returns an instance of the configured provider based on settings in the Web application configuration file.

Solution Architecture
While my primary goal was to take advantage of the ASP.NET 2.0 credentials management infrastructure, I also wanted to provide a general-purpose custom authentication and authorization infrastructure for Windows Forms. Such infrastructure should not necessarily be coupled to ASP.NET 2.0, and could easily use any custom credentials store, such as Access or a Lightweight Directory Access Protocol (LDAP) database. The first step was to decouple it from the actual credentials store by defining the IUserManager interface, as follows:
public interface IUserManager
{
   bool Authenticate(string applicationName,string userName, 
       string password);
   bool IsInRole(string applicationName, string userName, string role);
   string[] GetRoles(string applicationName, string userName);
}
The Authenticate method is used to authenticate the specified user credentials against the credentials store. IsInRole authorizes the user when you're using role-based security. IUserManager also provides the GetRoles method which returns all the roles of which a specified user is a member. GetRoles is useful when caching role membership, which I'll discuss later.
Authenticate is used by an abstract Windows Forms custom control called LoginControl. LoginControl is used in a similar fashion to its ASP.NET cousin in that you add it (or rather, a subclass of it) to your Windows Forms application. LoginControl obtains an implementation of IUserManager and authenticates the supplied credentials using the Authenticate method. If the user specified valid credentials, LoginControl instantiates an implementation of IPrincipal called CustomPrincipal. LoginControl provides CustomPrincipal with the implementation of IUserManager, and attaches CustomPrincipal to the current thread. The CustomPrincipal class can use the IsInRole or GetRoles methods of IUserManager for authorizing the user. This architecture is shown in Figure 2. Both LoginControl and CustomPrincipal are defined in the WinFormsEx.dll class library assembly, available with the code download for this article.
Figure 2 LoginControl Architecture 
You should note that CustomPrincipal never authenticates the user since it implicitly trusts LoginControl to do so. This means that you should not allow CustomPrincipal to be attached to a thread without going through valid authentication. In order to enforce that, the CustomPrincipal class is an internal class, called only by LoginControl. While an instance of CustomPrincipal could still be created using reflection, that would require a high level of trust. Thus I recommend that you follow the principles of least privilege and not grant the assembly the ReflectionPermission that would allow this to happen.

Implementing IPrincipal
The sole purpose of CustomPrincipal is to replace the Windows security principal and service the PrincipalPermissionAttribute and PrincipalPermission classes. CustomPrincipal should only be installed after successful authentication. To further enforce and automate this design decision, CustomPrincipal doesn't have a public constructor, so its clients have no direct way to instantiate it. Instead, the clients use the Attach public static method. Attach first verifies that the identity provided is that of an authenticated user:
Debug.Assert(user.IsAuthenticated); 
If the user is authenticated, Attach creates an object of type CustomPrincipal, providing it with the security identity and the implementation of IUserManager to use, as well as with the role caching policy, shown in Figure 3.
internal class CustomPrincipal : IPrincipal
{
   IIdentity m_User;
   IPrincipal m_OldPrincipal;
   IUserManager m_UserManager;
   string m_ApplicationName;
   string[] m_Roles;
   static bool m_ThreadPolicySet = false;

   CustomPrincipal(IIdentity user,string applicationName,
                   IUserManager userManager,bool cacheRoles)
   {
      m_OldPrincipal = Thread.CurrentPrincipal;

      m_User = user;
      m_ApplicationName = applicationName;
      m_UserManager = userManager;

      if(cacheRoles)
      {
         m_Roles = m_UserManager.GetRoles(m_ApplicationName,m_User.Name);
      }
      //Make this object the principal for this thread
      Thread.CurrentPrincipal = this;
   }

   static public void Attach(IIdentity user,string applicationName,
                             IUserManager userManager)
   {
      Attach(user,applicationName,userManager,false);
   }

   static public void Attach(IIdentity user,string applicationName,
                             IUserManager userManager,bool cacheRoles)
   {
      Debug.Assert(user.IsAuthenticated);

      IPrincipal customPrincipal = new 
          CustomPrincipal(user,applicationName,
              userManager,cacheRoles);

      //Make sure all future threads in this app domain use this principal
      //In addition, because default principal cannot be set twice:
      if(m_ThreadPolicySet == false)
      {
         AppDomain currentDomain = AppDomain.CurrentDomain;
         currentDomain.SetThreadPrincipal(customPrincipal);
         m_ThreadPolicySet = true;
      }
   }

   public void Detach()
   {
      Thread.CurrentPrincipal = m_OldPrincipal;
   }

   public IIdentity Identity
   {
      get { return m_User; }
   }

   public bool IsInRole(string role)
   {
      if(m_Roles != null)
      {
         return Array.Exists(m_Roles,
            delegate(string roleToMatch) { return roleToMatch == role; });
      }
      else
      {
         return m_UserManager.IsInRole(
            m_ApplicationName, m_User.Name,role);
      }
   }
}
The constructor of CustomPrincipal saves the identity provided, as well as a reference to the previous principal associated with that thread. Most importantly, the constructor replaces the default principal by setting the CurrentPrincipal property of the current thread to itself, as shown in the following line of code:
Thread.CurrentPrincipal = this;
To support log-out semantics, CustomPrincipal provides the Detach method for detaching itself from the thread and restoring the old principal saved during construction.
In addition, Attach sets the default thread principal object to CustomPrincipal, so that it will be attached to new threads automatically. However, since you can only set the thread principal object once per app domain, Attach verifies first that it was not done already (it is possible to attach and detach the principal), using the static flag m_ThreadPolicySet:
if(m_ThreadPolicySet == false)
{
   m_ThreadPolicySet = true;
   currentDomain.SetThreadPrincipal(customPrincipal);
}
While authentication is a one-off cost, authorization can be a frequent operation. Since verifying role membership may potentially be expensive (such as when querying a database or calling a Web service), CustomPrincipal can cache all the roles the user is a member of, by saving the roles in the m_Roles member array. The constructor of CustomPrincipal takes a Boolean parameter called cacheRoles. If cacheRoles is true, the constructor will initialize m_Roles by calling the GetRoles method of the provided user manager. While this will enable almost instant role membership verifications, unfortunately the downside is that when caching roles, CustomPrincipal will not detect changes to the roles repository, such as removing the user from a particular role. Use caching only in cases when the allocation of users to roles is a relatively infrequent event and when the performance and scalability goals absolutely mandate it. This is why the Attach version that does not take a cacheRoles parameter defaults to no caching.
Implementing IPrincipal is straightforward. In its implementation of the Identity property, CustomPrincipal returns the saved identity. To implement IsInRole, CustomPrincipal checks if there are any cached roles. If so, it searches the roles array using the static Exists method of the Array type. Exists takes a delegate of type Predicate, defined as the following:
public delegate bool Predicate<T>(T t);
Exists will evaluate the method targeted by the predicate for each item in the array and will return true if a match is found. IsInRole initializes the predicate with an anonymous method that compares the role specified in the call to IsInRole with the role passed in as a parameter. If there are no cached roles, IsInRole delegates the query to the provided implementation of IUserManager, returning its results.

The LoginControl Class
LoginControl provides two textboxes for capturing the user name and password. In addition, the control uses the ErrorProvider component. LoginControl will only authenticate if the user provides both user name and password, and the error provider will alert the user to missing input. Authentication takes place in the Click event-handling method for the Log In button. Figure 4 shows a partial listing of LoginControl (with some of the mundane code omitted in the interests of space).
using LoginEventHandler = EventHandler<LoginEventArgs>;

public class LoginEventArgs : EventArgs
{
   public LoginEventArgs(bool authenticated);
   public bool Authenticated{get;internal set;}
}    

[DefaultEvent("LoginEvent")]
[ToolboxBitmap(typeof(LoginControl),"LoginControl.bmp")]
public abstract partial class LoginControl : UserControl
{
   string m_ApplicationName = String.Empty;
   bool m_CacheRoles = false;
   public event LoginEventHandler LoginEvent = delegate{};

   [Category("Credentials")]
   public bool CacheRoles //Gets and sets m_CacheRoles
   {...}

   [Category("Credentials")]
   public string ApplicationName //Gets and sets m_ ApplicationName
   {...}

   string GetAppName()
   {
      if(ApplicationName != String.Empty)
      {
         return ApplicationName;
      }
      Assembly clientAssembly = Assembly.GetEntryAssembly();
      AssemblyName assemblyName = clientAssembly.GetName();
      return assemblyName.Name;
   }

   static public void Logout()
   {
      CustomPrincipal customPrincipal = 
         Thread.CurrentPrincipal as CustomPrincipal;
      if(customPrincipal != null)
      {
         customPrincipal.Detach();
      }
   }

   static public bool IsLoggedIn
   {
      get { return Thread.CurrentPrincipal is CustomPrincipal; }
   }

   protected virtual void OnLogin(object sender,EventArgs e)
   {
      string userName = m_UserNameBox.Text;
      string password = m_PasswordBox.Text;

      /* Validation of userName and password using the error provider */

      string applicationName = GetAppName();     
      IUserManager userManager = GetUserManager();

      bool authenticated;   
      authenticated = userManager.Authenticate(
         applicationName,userName,password);
      if(authenticated)
      {
         IIdentity identity = new GenericIdentity(userName);
         CustomPrincipal.Attach(
            identity,applicationName,userManager,CacheRoles);
      }
      LoginEventArgs loginEventArgs = new LoginEventArgs(authenticated);
      LoginEvent(this,loginEventArgs);
   }
   protected abstract IUserManager GetUserManager();
}
When using LoginControl, you need to provide it with the credentials provider to use, the application name, and the role caching policy. Specifying the credential provider is done by subclasses of LoginControl. The subclasses need to override GetUserManager and return an implementation of IUserManager.
LoginControl provides the properties ApplicationName and CacheRoles. Because LoginControl derives from UserControl, it natively integrates with the Windows Forms Designer. These two properties are available for visual editing during the design time of a form or a window that uses LoginControl. To enrich the Designer support, the properties are decorated with the Category attribute, as shown here:
[Category("Credentials")]
When you select the Categories view of the control properties in the Designer, these properties will be grouped together under the Credentials category. Subclasses of LoginControl can add their own properties to this category, too. The CacheRoles property can be set to true or false, and LoginControl simply passes it as-is to CustomPrincipal.Attach. Unaltered, CacheRoles defaults to false.
The LoginControl cannot be used stand-alone, but it must be contained in another form or dialog. That container can, in turn, be used by different applications with different application names. You therefore have two options for supplying the application name. You can simply use the ApplicationName property to specify the application name. Or, if you do not know that name in advance (if you're developing a general-purpose container), LoginControl can retrieve the application name from the Windows Forms entry application assembly used to launch the control. During authentication, if the LoginControl has no value set in the ApplicationName property, LoginControl will use the friendly name of the entry assembly as the application name. This logic is encapsulated in the private helper method GetAppName.

Authenticating the User
The OnLogin method is called when the user clicks the Log In button. After validating the user name and password, OnLogin calls GetAppName to retrieve the application name. It then calls GetUserManager to obtain an implementation of IUserManager. Authentication itself is done simply by calling the Authenticate method of the user manager. If authentication is successful, OnLogin wraps a generic identity object around the user name and attaches the custom principal. Note that OnLogin never interacts with the custom principal directly. All OnLogin does is provide it with the IIdentity object, the application name, the caching policy, and the implementation of IUserManager to use.
The question now is what LoginControl should do after authentication. The control has no knowledge of the required behavior of its hosting container. If authentication fails, perhaps it should present a message box, or perhaps it should throw an exception. If authentication succeeds, maybe it should close the hosting dialog, or move to the next screen in a wizard, or something else. Since only the hosting application knows what to do after both successful and failed authentication, all LoginControl can do is inform it by firing an event. LoginControl declares the event LoginEvent of the type EventHandler<LoginEventArgs>. LoginEventArgs contains a Boolean property called Authenticated, indicating the outcome of the authentication. To avoid a multithreaded race condition where another thread removes all subscribers from the LoginEvent just before publishing, LoginControl initializes LoginEvent with an anonymous method.
LoginControl also provides two handy helpers: the static Boolean property IsLoggedIn, and the static method Logout. IsLoggedIn allows the caller to query if a user is logged in. LoginControl retrieves the current principal and checks if it is of the CustomPrincipal type. If the user is logged in, this of course will be the principal used. The Logout method allows the user to log out. Logout retrieves the current principal, and if it is of the CustomPrincipal type, detaches it from the current thread. As explained previously, calling Detach on CustomPrincipal will also restore the previous principal. Note that if multiple threads are involved, you will need to log out on each one of them.

The AspNetLoginControl
Let's take a look now at the definition of my AspNetLoginControl, as shown here:
public partial class AspNetLoginControl : LoginControl
{
   protected override IUserManager GetUserManager()
   {
      return new AspNetUserManager();
   }
}
AspNetLoginControl derives from LoginControl, and in its overriding of GetUserManager it returns my AspNetUserManager implementation of IUserManager, which uses the ASP.NET providers directly, as shown in Figure 5.
Figure 5 AspNetLoginControl 
AspNetUserManager is capable of using any valid ASP.NET 2.0 provider, hence its name. To use AspNetLoginControl, you will need to add to the application's configuration file the same values you would have placed in a Web application configuration file, indicating which provider to choose, as well as any provider-specific values and settings.
For example, to use the SQL Server providers, you must add the settings that are shown in Figure 6 to the application configuration file. The connection string value shown in Figure 6 is used to connect to the aspnetdb database on the local machine after default installation. Note the use of the enabled attribute of the roleManager tag to enable authorization.
<?xml version="1.0"?>
<configuration>
   <system.web>
      <membership defaultProvider="AspNetSqlProvider" />
      <roleManager enabled="true" defaultProvider="AspNetSqlProvider" />
   </system.web>
   <connectionStrings>
      <remove name="LocalSqlServer" />
      <add name="LocalSqlServer" connectionString="
         data source=(local);Integrated Security=SSPI;
         Initial Catalog=aspnetdb" />
   </connectionStrings>
</configuration>
By default, the configuration file generated by Visual Studio 2005 uses NTFS security to enforce access permission security to the application configuration file, so only application administrators can modify the provider's settings.

Implementing IUserManager
AspNetUserManager contains as member variables instances of the ASP.NET membership and role providers. All the functionality of IUserManager is accomplished by delegating to the ASP.NET providers, as shown in Figure 7.
class AspNetUserManager : IUserManager
{
   public bool Authenticate(string applicationName, string userName,
      string password)
   {
      Membership.ApplicationName = applicationName;
      return Membership.ValidateUser(userName,password);
   }

   public bool IsInRole(string applicationName, string userName,
      string role)
   {
      Roles.ApplicationName = applicationName;
      return Roles.IsUserInRole(userName,role);
   }

   public string[] GetRoles(string applicationName,
      string userName)
   {
      Roles.ApplicationName = applicationName;
      return Roles.GetRolesForUser(userName);
   }
}
To implement Authenticate, AspNetUserManager calls the ValidateUser method of Membership. To implement IsInRole and GetRoles, AspNetUserManager calls the IsUserInRole and GetRolesForUser methods of Roles.
One thing that you should be aware of is that my code is not thread-safe in the face of another thread changing membership.ApplicationName. As a result, the code shown here is fine as long as multithreaded access is not a requirement.

AspNetLoginControl and Code Access Security
AspNetLoginControl works well, and you can certainly use it, but it does have one important shortcoming. The ASP.NET providers were designed to be used on the server, and they require a few nontrivial permissions, including unmanaged code access permisions, unrestricted SQL Server client access permissions, and minimal ASP.NET Hosting permissions. Typically, server-side applications run in an elevated trust environment, potentially even full trust, and will have no problem obtaining those permissions and executing properly.
The AspNetLoginControl, on the other hand, is going to be used by Windows Forms applications. It is quite likely that such applications will be executing in a partial trust environment, perhaps as ClickOnce applications. The client environment may very well not grant the applications using AspNetLoginControl the permission they require to operate.
Figure 8 lists the permissions currently required for AspNetLoginControl and lists who demands them. These permissions are the product of both the control itself and the components it uses: CustomPrincipal and AspNetUserManager.

Permission Type Specific Permission Value Demanded By
Security Execution Any managed application in order to run
Security Unmanaged code access The ASP.NET providers for their internal implementation
ASP.NET hosting Minimal The ASP.NET providers such that any party that hosts them has permission to do so
SQL Server client Unrestricted The ASP.NET providers for accessing the database
User interface Safe subwindows LoginControl in order to display itself
Security Control principal CustomPrincipal to be able to set principal policy
You have a number of options in choosing how to deal with these permission demands. Although you can grant full trust to the WinFormsEx.dll assembly and all its clients, this is inadvisable because it opens the way for the assemblies to be lured into doing things they are not supposed to do.
You can grant most of these permissions using the .NET Configuration tool to both the WinFormsEx.dll assembly and to every client application that wants to use it. You can even list these permissions in the ClickOnce application manifest. To ease the task of granting the permissions, the source files accompanying this article contain the WinFormsEx.xml file. This is a custom permission set file that you can add to the .NET Configuration tool and grant the necessary permissions.
However, the best solution is to avoid the permissions demanded by the ASP.NET providers altogether, and to use a different implementation of IUserManager, one that does not demand server-side permissions in a client environment. I'll present this option next. Also note that the ASP.NET team is investigating shortening this list of required permissions prior to the final product release.

The UserManager Web Service
The solution to the partial-trust problem is to wrap the ASP.NET providers with a Web service. When using a Web service, none of the security permission demands made by the providers will ever make their way back to the client.
Using a Web service also has the advantage of better scalability, since only the Web service will be using the connection to the database, rather than each individual client application. Another benefit of a Web service is that it avoids some potential security issues with clients authenticating themselves against SQL Server and with secure connection string management on the client side.
There are, however, a few downsides to using a Web service. For example, you should secure the communication between the clients and the Web service, because the clients will be sending credentials over the wire. This can easily be done using HTTPS. You also have the problem of additional call latency, which should be resolved using role caching. Authenticating against the Web service itself may be an issue, but this is mitigated if you can sustain anonymous access to the service in your intranet environment. Finally, authorizing the Web service calls may present problems. The Web service allows callers to retrieve role information about a user. Role membership information may be sensitive information in its own right authenticate. This can be dealt with by adding role-based security to the Web service and authorizing the callers. Note, though, that authorization requires authentication.
Using the WebServiceBinding attribute, you can define IUserManager as a Web service interface and implement it, as shown in Figure 9. The UserManager class in Figure 9 uses both Membership and Roles to obtain the configured providers from the Web service configuration file. Note that each Web service method of UserManager also accepts the application name to use, so that a single Web service can support multiple Windows Forms applications.
[WebServiceBinding]
public interface IUserManager
{
   [WebMethod(Description="Authenticates the user.")]
   bool Authenticate(string applicationName, string userName,
      string password);

   [WebMethod(Description="Verifies user role's membership.")]
   bool IsInRole(string applicationName, string userName, string role);
   
   [WebMethod(Description="Returns all roles the user is a member of.")]
   string[] GetRoles(string applicationName, string userName);
}

[WebService(Namespace = "http://SecurityServices",
   Description = "Wraps with a web service the ASP.NET providers. " + 
                 "This web service should be accessed over https.")]
class UserManager : IUserManager
{
   public bool Authenticate(string applicationName, string userName,
      string password)
   {
      if(HttpContext.Current.Request.IsSecureConnection == false)
      {
         HttpContext.Current.Trace.Warn(
           "You should use HTTPS to avoid sending cleartext passwords.");
      }
      Membership.ApplicationName = applicationName;
      return Membership.ValidateUser(userName,password);
   }

   public bool IsInRole(string applicationName, string userName,
      string role)
   {
      Roles.ApplicationName = applicationName;
      return Roles.IsUserInRole(userName,role);
   }

   public string[] GetRoles(string applicationName, string userName)
   {
      Roles.ApplicationName = applicationName;
      return Roles.GetRolesForUser(userName);
   }
}

The WSLoginControl
WinFormsEx.dll contains the definition of WSLoginControl, shown in the following code snippet:
public partial class WSLoginControl : LoginControl
{
   protected override IUserManager GetUserManager()
   {
      return new UserManager();
   }
}
WSLoginControl derives from LoginControl, and in its overriding of GetUserManager, it returns UserManager, a client-side Web service proxy class used to invoke the UserManager Web service. WSLoginControl can use any Web service that manages user credentials as long as it supports the IUserManager interface, hence its name. To generate the proxy class, add a Web reference to the UserManager Web service. Then you should change the machine-generated UserManager Web service proxy class to derive from IUserManager. Since the proxy class is a partial class and is machine-generated, it's preferable to add that code in a separate file:
partial class UserManager : IUserManager
{}
WinFormsEx.dll already contains the definition of the UserManager Web service proxy class. The machine-generated code in the proxy class looks up the Web service address from the application configuration file. Add to the application configuration file under the appSettings section a key called UserManager whose value is the Web service address:
<?xml version="1.0"?>
<configuration>
   <appSettings>
      <add key="UserManager" 
           value="http://localhost/SecurityServices/UserManager.asmx"/>
   </appSettings>
</configuration>
Figure 10 WSLoginControl and Its Supporting Classes 
Figure 10 shows WSLoginControl, CustomPrincipal, and their interactions with the UserManager Web service.

WSLoginControl and Code Access Security
Using a Web service keeps the ASP.NET providers permission demands on the server. The trade-off is that you will need to grant the clients Web access permission to connect to the user management Web service. You will also need to grant the rest of the permissions required by LoginControl and CustomPrincipal. Figure 11 lists the reduced permissions required when using a credentials management Web service.

Permission Type Specific Permission Value Demanded By
Security Execution Any managed application in order to run
Web access Connect to the user manager Web service The UserManager proxy class to be able to use the Web service
User interface Safe subwindows LoginControl in order to display itself
Security Control principal CustomPrincipal to be able to set principal policy
You can grant all the permissions from Figure 11 using the .NET Configuration tool to both the WinFormsEx.dll assembly and to every client application that wants to use it. You can also use Visual Studio 2005 to list these permissions in the ClickOnce application manifest. For example, in Visual Studio 2005, go to the Security tab in the project settings of a ClickOnce application that uses WSLoginControl. Check the "Enable ClickOnce Security Settings" checkbox and the "This is a partial trust application" checkbox. Under "Zone your application will be installed from," select (Custom). This will remove all permissions except execution permission. Next, select SecurityPermission, and under Settings, select Include. Click Properties to bring up the Permission Settings dialog. Select assembly execution and principal control permission, and click OK. To grant permission to call the Web service, include the WebPermission, and in its properties, specify the UserManager's URL and allow the application to connect to it.
In a similar manager, grant the user interface permission that's listed in Figure 8. When you publish your ClickOnce application, its application manifest will include these permissions. You can even use Visual Studio 2005 to debug the application under these partial trust settings.

The Sample Application
Figure 12</