Using the ASP.NET membership provider in a Windows forms application
 

Home
Mission
News
Blog
Tools
FAQ
Publications
Downloads
Contact
Contents

Using the ASP.NET membership provider
in a Windows forms application

- Maurice de Beijer -

 

One of the new features in ASP.NET 2.0 is the membership and role provider system. This is a nicely designed system that allows the developer to handle the storage of user information pretty much any way he likes, all he has to do is create the appropriate providers and configure his application to use these. While this is very powerful there is something else I was much more interested in and that is the fact that user and role management can be added to an application without having to do any of the implementation work. Now this is very convenient if you don’t already have a user database to work with. Also, if at all possible, I would like to be able to use these providers in my Windows forms applications as well as in my web application. Fortunately this is possible without much work at all! So let’s create a very small console application with user management.

 

Registering and validating users

First create a new Visual Basic console application in Visual Studio 2005. No problem if you prefer C#, all you need to do is translate the remaining syntax to C# but I leave that up to you :-). Next add a reference to the System.Web assembly in the project properties.

Add the following line to the top of the Module1.vb:

        Imports System.Web.Security

Now add the following code to the Sub Main()

        ' Validate a username/password
        If Membership.ValidateUser("Maurice", "Password_1") Then
            Console.WriteLine("User validated.")
        Else
            Console.WriteLine("User invalid!")
        End If

        Console.ReadKey()

 
Go ahead and run the application at this point. Not surprisingly the application reports that the user is invalid, after all it can’t know which users are valid.

Now add the following code to the top of the Sub Main(), before the ValidateUser call:

        ' Creating a new user
        Dim status As MembershipCreateStatus
        Membership.CreateUser( _
            "Maurice", _
            "Password_1", _
            "maurice@TheProblemSolver.nl", _
            "Password question", _
            "Password answer", _
            True, _
            status)
        Console.WriteLine(status.ToString())

 

Go ahead and run the application again at this point.

If you have never used the membership provider in a Web application you might me surprised to see that this just works, the user is created and is validated. Run the application again and the creation will return a DuplicateUserName error but the ValidateUser() still works. How is this possible as we haven’t done anything to configure the membership provider or told it which database to use? Well the secret is that the membership provider checks for, and creates if needed, a new directory under the application directory named App_Data with a SQL Express database ASPNETDB with the required structure. This database is then used to store the user information. To see the database click on the Show All Files button in the explorer, Open the bin/Debug folder and you will see an App_Data folder with the ASPNETDB.MDF database inside of it.

So far so good, I had to do very little work and I can already register and validate users. However I had to supply quite a bit of information, like password question and answer. Something I would not typically use in a windows application. Fortunately the Membership.CreateUser() also has an overload with just the username and password combination, more along the lines of what I would like to use. Unfortunately adding the following code the Sub Main doesn’t work but it raises a System.Web.Security.MembershipCreateUserException with the text “The password-answer supplied is invalid.”.

        Dim user As MembershipUser
        user = Membership.CreateUser("User2", "Password")
        Console.WriteLine(user.UserName)

 
Additionally there might be some other membership properties you might like to change, like the requirement for at least one non alphanumeric character in the password. Even though these settings are exposed as properties they are read-only so setting them like: Membership.MinRequiredNonAlphanumericCharacters = 0 will not work. To change them we need to use the applications app.config. As we don’t have one yet go ahead and add one to the application. Now add the following to the bottom of the <configuration> section:

<system.web>
    <
membership>
      <
providers>
        <
remove name="AspNetSqlMembershipProvider"/>
        <
add name="AspNetSqlMembershipProvider"
            
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            
connectionStringName="LocalSqlServer"
            
enablePasswordRetrieval="false"
            
enablePasswordReset="true"
            
requiresQuestionAndAnswer="false"
            
applicationName="/"
            
requiresUniqueEmail="false"
            
passwordFormat="Hashed"
            
maxInvalidPasswordAttempts="5"
            
minRequiredPasswordLength="1"
            
minRequiredNonalphanumericCharacters="0"
            
passwordAttemptWindow="10"
            
passwordStrengthRegularExpression="" />
      </
providers>
    </
membership>
  </
system.web>

This might seem a little strange at first, after all we are adding a system.web configuration block to a console application. But remember that the Membership provider was written by the ASP.NET team for ASP.NET use and the code just looks in the configuration file for the current application.  So what does this actually do? This configuration block removes the default membership provider as defined in the machine.config and adds the same one but this time with more relaxed password format and questions settings. Now run the application again and this time we are allowed to add a user with just a username and password combination. Because of the relaxed password requirements even a password of just 1 single letter would be accepted in this case.

 

Adding user roles

Now that we can add valid users we might want to start using roles as well. To work with security roles we need to use the class System.Web.Security.Roles. This class works pretty much in the same way as the Membership provider. To add a new role we use the CreateRole() function and to associate a user with this role we use the AddUserToRole() function. So adding the Developer role to my previously created user account can be done with the following code:

        Roles.CreateRole("Developer")
        Roles.AddUserToRole("Maurice", "Developer")

Unfortunately this isn't quite as simple as we receive a System.Configuration.Provider.ProviderException exception with the text “The Role Manager feature has not been enabled.”. Fortunately the fix isn’t difficult, all we need to do is add the following line to the <system.web> section in the App.Config:

        <roleManager enabled="true" />

Once this is done you are ready to add roles and associate users with these roles. Now you can use the following code to check if a user has a specific role:

        If Roles.IsUserInRole("Maurice", "Developer") Then
            Console.WriteLine("Is a developer.")
        Else
            Console.WriteLine("Doesn't write code.")
        End If

 

Remembering the current user

Of course this leaves us with a place to store the current user name so we know which user is logged in when we need to check for a specific role. The IsUserInRole() function has an additional overload that takes only a single parameter, the role name. Taking a look at the internals of the IsUserInRole() function shows that it determines the current user either via the HttpContext.Current.User or the Threading.Thread.CurrentPrincipal depending on the fact if the running application is hosted. As the Threading.Thread.CurrentPrincipal is the normal way to provide the current user and role information in a windows application this seems like the natural thing to do. Again there are several standard classes available to do this so we need to write almost no code. The Threading.Thread.CurrentPrincipal is defined as interface IPrincipal so we need a type that implements that interface. The ASP.NET team provides the System.Web.Security.RolePrincipal for just this purpose. This type takes an parameter of type System.Security.Principal.IIdentity in its constructor. Again this is readily available in the form of a System.Security.Principal.GenericIdentity type that takes the users name as the parameter to its constructor. Using these types it is easy to save the validated user. 

Add the following code to the top of the Module1.vb:

Imports System.Security.Principal

Now we can set the principal and check for specific roles using the following code:

        Dim user As MembershipUser = Membership.GetUser("Maurice")
        Dim identity As New GenericIdentity(user.UserName)
        Dim principal As New RolePrincipal(identity)
        Threading.Thread.CurrentPrincipal = principal

        If Roles.IsUserInRole("Developer") Then
            Console.WriteLine("Is a developer.")
        Else
            Console.WriteLine("Doesn't write code.")
        End If

 

An alternative way, but slightly more verbose way, of checking the user and role is:

        Console.Write(Threading.Thread.CurrentPrincipal.Identity.Name)
        If Threading.Thread.CurrentPrincipal.IsInRole("Developer") Then
            Console.WriteLine(" is a developer.")
        Else
            Console.WriteLine(" doesn't write code.")
        End If

Conclusion

In this article I have tried to demonstrate how easy it is to use the ASP.NET Memebership and Role providers in a non web application. It turns out that this is very simple to do and quite beneficial if you need that kind of functionality. Quite frankly user authentication is something that I need in most of my applications and role management is something that comes up quite often too so I will certainly be using these providers in future applications.

 

Complete listing

Imports System.Security.Principal
Imports
System.Web.Security

Module Module1
    Sub Main()
        ' Creating a new user
        Dim status As MembershipCreateStatus
        Membership.CreateUser( _
            "Maurice", _
            "Password_1", _
            "maurice@TheProblemSolver.nl", _
            "Password question", _
            "Password answer", _
            True, _
            status)
        ' Check the status for errors
        Console.WriteLine(status.ToString())

        ' Validate a username/password
        If Membership.ValidateUser(
"Maurice", "Password_1") Then
            Console.WriteLine(
"User validated.")
        Else
            Console.WriteLine(
"User invalid!")
        End If
 

        ' Create a new Developer role.
        ' Add the <roleManager enabled="true" /> to the app.config for this to work
        If Not Roles.RoleExists("Developer") Then
            Roles.CreateRole("Developer")
        End If

        ' Add a new role to a known user.
        If Not Roles.IsUserInRole("Maurice", "Developer") Then
            Roles.AddUserToRole("Maurice", "Developer")
        End If

        ' Create a second user with only username/password
        ' Add the <membership><providers> element to the app.config first
        Dim user As MembershipUser
        user = Membership.GetUser("User2")
        If user Is Nothing Then
            user = Membership.CreateUser("User2", "p")
            Console.WriteLine(user.UserName)
        End If


       
' Check is a specified user is in a specific role
        If Roles.IsUserInRole(
"Maurice", "Developer") Then
            Console.WriteLine(
"Is a developer.")
        Else
            Console.WriteLine(
"Doesn't write code.")
        End If 

        ' Set the current application principal information to a known user
        Dim identity As GenericIdentity
        Dim principal As RolePrincipal
        user = Membership.GetUser("Maurice")
        identity = New GenericIdentity(user.UserName)
        principal = New RolePrincipal(identity)
        Threading.Thread.CurrentPrincipal = principal

        ' Check if the current principal is in a specific role
        If Roles.IsUserInRole("Developer") Then
            Console.WriteLine("Is a developer.")
        Else
            Console.WriteLine("Doesn't write code.")
        End If


        ' Set the current application principal information to another known user
        user = Membership.GetUser("User2")
        identity = New GenericIdentity(user.UserName)
        principal = New RolePrincipal(identity)
        Threading.Thread.CurrentPrincipal = principal

        ' Use the principal to check for role information
        Console.Write(Threading.Thread.CurrentPrincipal.Identity.Name)
        If Threading.Thread.CurrentPrincipal.IsInRole(
"Developer") Then
            Console.WriteLine(
" is a developer.")
        Else
            Console.WriteLine(
" doesn't write code.")
        End If

        Console.ReadLine()
    End Sub
End
Module

 

Complete App.Config

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>
  <
system.web>
    <
roleManager enabled="true" />
    <
membership
      <
providers>
        <
remove name="AspNetSqlMembershipProvider"/>
        <
add name="AspNetSqlMembershipProvider"
            
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            
connectionStringName="LocalSqlServer"
            
enablePasswordRetrieval="false"
            
enablePasswordReset="true"
            
requiresQuestionAndAnswer="false"
            
applicationName="/"
            
requiresUniqueEmail="false"
            
passwordFormat="Hashed"
            
maxInvalidPasswordAttempts="5"
            
minRequiredPasswordLength="1"
            
minRequiredNonalphanumericCharacters="0"
            
passwordAttemptWindow="10"
            
passwordStrengthRegularExpression="" />
      </
providers>
    </
membership>
  </
system.web>
</
configuration>

 

 

 

Maurice de Beijer is an independent software developer, beta tester and a recipient of the MVP award. He specializes in .Net, object orientation, Visual FoxPro and solving technically challenging problems. Maurice is The Problem Solver and can be reached at mauricedb@computer.org or at www.TheProblemSolver.nl.

 

 

 

Send mail to webmaster@TheProblemSolver.nl with questions or comments about this web site.
Copyright © 1995 - 2007 ABL - The Problem Solver
Last modified: 10/21/06
WF
RSS 2.0