Securing ASP.NET Applications - With Less Code

 

Michele Leroux Bustamante
IDesign: .NET Design and Business Solutions

October 2003

Applies to:
    Microsoft® ASP.NET Whidbey

Summary: Learn how ASP.NET Whidbey and its new configuration tools, controls and components support a complete system for authenticating users and managing protected resources. (23 printed pages)

Download ASPNETWhidbeySecuritySample.msi.

Contents

Even Grandma can Do It
Configuration Interface
Drag and Drop Security
Filtering Content by Roles
Membership and Role Providers
But, Is It Extensible?
Conclusion

Note This document was developed prior to the product's release to manufacturing and, as such, we cannot guarantee that any details included herein will be exactly the same as those found in the shipping product. The information represents the product at the time this document was published and should be used for planning purposes only. Information is subject to change at any time without prior notice.

I build a lot of Microsoft® ASP.NET applications: client applications and prototypes, my own growing list of sites, sites for family and friends who can't code, and code for articles, presentations, and training courses. I constantly find myself repeating certain tasks for every application, and among those tasks defining an authentication model is high on the list. Protecting application resources is a necessary part of designing almost any application. ASP.NET 1.x made things easier by providing a fairly simple and secure forms-based authentication process, but you still had to roll your own role management, among other things. If I had a nickel for every new login form I designed I'd have at least $10 more in my pocket—do the math, that's a lot of forms. The upcoming version of ASP.NET, code name ASP.NET "Whidbey" (after the code name for the upcoming release of Microsoft® Visual Studio® .NET), has provided a number of new configuration tools, controls and components to support a complete system for authenticating users and managing protected resources. These new features are so darned intuitive that even your grandmother can build a secure site in a day.

Even Grandma can Do It

The traditional approach for building an authentication model for a new Web site usually includes these steps:

  1. Gather requirements for protecting resources and activities and define applicable roles and permissions.
  2. Design relational database tables to store users, roles and related permissions.
  3. Design a login page.
  4. Write code to authenticate users and gather associated roles and permissions.
  5. Add configuration to protect Web resources based on roles.
  6. Write code to control page content based on roles and permissions.

Even if you have built some reusable components that encapsulate some of these repetitive tasks, you are still about to feel a huge weight lifted from your shoulders. No less than five of these steps are reduced to a fraction of the effort, if not almost completely eliminated by the new components of ASP.NET Whidbey. Using the new Web-based administrative wizard I will automatically create a set of tables to handle users, roles and permissions. With the same administrative interface I will configure the application for login, membership and role management. I'll design a login page in one second (or, as long as it takes me to drag and drop the Login control on a Web form) and I won't write a line of code to authenticate the user or associate roles since that will be handled automatically. Menus and page content will be presented differently based on user role and login status, but I won't write any code for that either.

Does that sound like a dream come true? Let's see how it works.

Configuration Interface

ASP.NET Whidbey includes a new Web-based configuration tool that runs in the context of a specific application so that it can interactively modify an application's own web.config file. This tool has a number of wizards including one that takes you through the steps of setting up security options. After creating a new Web site using Visual Studio "Whidbey," you can launch the configuration tool by either selecting the ASP.NET Configuration icon from Solution Explorer or by selecting ASP.NET Configuration from the Website menu.

Aa479008.aspnet-securingapps-01(en-us,MSDN.10).gif

Figure 1. Launching the Web-based configuration utility can be done from Solution Explorer, or from the main menu.

The configuration Web interface is launched with a query string indicating the application target for the configuration. I created a new application for this article called MySecureNewsletter and launched the administrative interface as shown here:

Aa479008.aspnet-securingapps-02(en-us,MSDN.10).gif

Figure 2. The URL to launch the administrative interface on my machine was https://localhost:10245/ASP.NETWebAdminFiles/default.aspx?applicationPhysicalPath=H:\WebSites\MySecureNewsletter\&applicationUrl=/MySecureNewsletter.

One of the options from the Security tab is to run the Security Setup Wizard. For a new application you should go through this wizard first. The left panel of the wizard interface displays the list of steps you will go through, and indicate the current step you are on:

Aa479008.aspnet-securingapps-03(en-us,MSDN.10).gif

Figure 3. The Security Setup Wizard leverages some of the other cool features of ASP.NET Whidbey, such as the Wizard control for navigation.

The steps in this setup process are fairly intuitive so I will just provide you with a summary of the options I selected for the sample application. First, I was asked if the site would be an intranet or Internet site. The former would default to Windows authentication and the latter (the one I chose) configures the application for forms authentication. Next I was given the option to create a database to store users and roles. If I were to select this option I would have been asked to choose a Microsoft Access or Microsoft SQL Server™ database, and the tool would take me through a few additional steps. I did not select this option, and so a default Access database will be created beneath the \Data directory of the application. In the machine.config file, a<connectionString>element specifies the default location of the Access database:

    <connectionStrings>
          <add name="LocalSqlServer" connectionString="data
            source=127.0.0.1;Integrated Security=SSPI" />
          <add name="AccessFileName" 
            connectionString="~\DATA\ASPNetDB.mdb" />
    </connectionStrings>

Next I enabled role management for the application, and at this point the default Access database is created for me with a number of tables that will ultimately be used by the new security controls and membership APIs. At this point, I can optionally create roles and then users. A simple string value is required to create each role, as shown below:

Aa479008.aspnet-securingapps-04(en-us,MSDN.10).gif

Figure 4. The Wizard panel on the left side of the interface shows me where I am in the steps for setting up the security model for the application.

To create users some assumptions are made about which fields you might want to collect for each user, specifically user name, password, e-mail, description and an optional secret question with answer. If any roles were created, those are also made available as checkbox selections:

Aa479008.aspnet-securingapps-05(en-us,MSDN.10).gif

Figure 5. The e-mail field entered here will be later used to interact with the user for password recovery procedures, for example.

With these few simple steps a new database was created beneath the \Data directory (the filename is AspNetDB.mdb) to store membership information. After refreshing Solution Explorer you can also see that a web.config file was created to enable role management within the<roleManager>setting. This is necessary because the default setting inside the machine.config is disabled. This new web.config file looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.web>
        <roleManager enabled="true">
            <providers />
        </roleManager>
    </system.web>
</configuration>

In these few short steps I created a standard membership and credential management database, roles and users were created, the necessary Web configuration for role management is complete, and we are ready to build a secure application.

If you are not prepared to create roles and users during your first configuration pass you can either return to this configuration tool at any time to enter or modify settings, but I will be showing you how to build administrative pages of your own as well.

Drag and Drop Security

By the end of this section of the article, you still will not have written any code. I'm going to show you how to generate an operational role-based authentication system using the new security controls for ASP.NET Whidbey. I'll start out with my sample newsletter application, MySecureNewsletter. Currently, it has no security model in place, aside from the steps I just completed with the wizard. As a side note, this application leverages the new Master Pages feature for Whidbey which you can read more about in Master Pages in ASP.NET Whidbey. From Solution Explorer you'll see a /templates directory that contains reusable user control and master page templates, an /images directory where all the supporting graphics are stored, and in the root directory a few application pages exist.

Aa479008.aspnet-securingapps-06(en-us,MSDN.10).gif

Figure 6. All *.aspx pages in the sample application will use a master page as a template for layout. Content pages are just like Web Forms pages, but they specify a Master Page template and all content is placed within content controls.

Logging In

After adding a new content page named login.aspx we can start having some fun. The Toolbox has a Security tab that lists the cool new security-related controls for ASP.NET Whidbey.

Aa479008.aspnet-securingapps-07(en-us,MSDN.10).gif

Figure 7. The ToolBox has been reorganized for ASP.NET Whidbey and new security controls are grouped on their own tab.

I'll explore these security controls one by one starting with the Login control that I have just dropped on the new login.aspx page:

Aa479008.aspnet-securingapps-08(en-us,MSDN.10).gif

Figure 8. The Login control provides an interactive designer interface for editing the default layout of the control.

You can select a canned format for the control's appearance by selecting one of the AutoFormat options:

Aa479008.aspnet-securingapps-09(en-us,MSDN.10).gif

Figure 9. Currently two canned formats are available, but if you have a default style sheet for your application, you can also assign styles to individual elements of the control through the Properties window.

The Properties window also provides access to the label, value, and validation error messages for each element of the control. You can also customize the appearance of the login button, and provide a link to create new members. For now, let's see how things turn out with my trusty style sheet and the defaults for the control. The source code generated by the control looks like this:

<asp:login id="Login1" runat="server">
</asp:login>

And with that, my login page is complete. Since I have already configured users and roles for the application, I need only modify the web.config file to set the authentication mode to Forms (since the default behavior is Windows authentication). And, in order to test the login process I will also reject anonymous users:

      <authentication mode="Forms"/>
<authorization>
         <deny users="?" />
      </authorization>

Note   I mentioned earlier that the Security Setup Wizard configures our application for forms authentication, and it does this by generating a default database to store credentials. What it doesn't do (in the Whidbey Alpha) is modify the <authentication> settings of our application's web.config file, to indicate forms authentication mode as the listing shows.

Now all anonymous users will be taken to the login page, and thanks to the use of Master Pages and style sheets, this page doesn't look so bad after spending one second dropping a login control on the form:

Aa479008.aspnet-securingapps-10(en-us,MSDN.10).gif

Figure 10. Without modifying any properties, this is the default appearance of the Login control, using my style sheet, and displayed within my page template.

Who's Logged In?

If I enter a valid user name and password, the Log In button automatically authenticates the user and redirects me to the originally requested page. Of course, after logging in, it is also customary to give the user a personalized welcome and also provide them with a way to log out. I will add these two features to the menus.ascx user control, which appears on every page. The LoginName control displays the name of the authenticated user and the LoginStatus control provides a convenient hyperlink that toggles between log in and log out actions according to the current authentication state. Here's the HTML source for these two new controls, after adding them to the menus.ascx file in the appropriate location:

You are currently logged in as: <asp:loginname id="LoginName1"
  runat="server"></asp:loginname>

<asp:loginstatus id="LoginStatus1" runat="server">
</asp:loginstatus>

Wow, that was rough. Let's see, now we have a login page, we are personally welcoming the user after they log in, and we have provided a way for the user to log out...all without writing code.

Aa479008.aspnet-securingapps-11(en-us,MSDN.10).gif

Figure 11. Toggling between Login/Logout

The LoginName control performs the single task of displaying the authenticated user's name. The LoginStatus control automatically toggles between Login/Logout by default, but can be further customized using templates. The actual process of logging out is also encapsulated by the control.

The only problem with this personalized welcome is that it displays a blank user name prior to log in, and we might need to write some code to control that part. Then again, maybe we can do it without writing code.

Controlling Access to Content

To display the currently authenticated user with earlier versions of the ASP.NET we would have written code to access the current context's user identity. To display this information only after the user is authenticated, more code would be written to check the status of the user. The LoginView control is probably one of the most interesting new security controls because it gives us the ability to control page content based on authentication status and roles. To try this out, I will add a LoginView control to the menu.ascx file. Interacting with the design environment you can edit the information you want to display to anonymous users (not logged in yet) or authenticated users.

Aa479008.aspnet-securingapps-12(en-us,MSDN.10).gif

Figure 12. Common LoginView Tasks allows you to alternate between views, or begin editing the templates by clicking the Edit Templates hyperlink.

You can always modify HTML directly from Source view. The following listing shows the changes made to the personalized welcome section of the menus.ascx file, by adding the LoginView control:

<asp:loginview id="LoginView1" runat="server">
    <anonymoustemplate>
        You are not currently logged in.
    </anonymoustemplate>
    <loggedintemplate> 
    You are currently logged in as:<asp:loginname id="LoginName1" 
      runat="server">
                </asp:loginname>
    </loggedintemplate>
</asp:loginview>

The LoginView control displays the contents of the<anonymoustemplate>section when the user is not yet authenticated, and displays the contents of the<loggedintemplate>section when the user has been authenticated. So, without writing any code we have leveraged the security controls to provide users with a login page, ability to log out, personalized welcome, and customized page content based on authentication status.

What's That? Forgot Your Password?

You're not going to believe how easy it will be for you to automatically manage passwords. The PasswordRecovery control provides a completely functional recovery system that includes looking up passwords based on your secret question, and automating the process of resetting passwords and sending them to users via e-mail. I created a password recovery page in the application root, and after dropping a PasswordRecovery control on the page I see the following:

Aa479008.aspnet-securingapps-13(en-us,MSDN.10).gif

Figure 13. Common PasswordRecovery Tasks allows you to customize the look and feel of each stage in the process.

Password management involves a number of activities, all which are configurable. For example, you can require users to create a secret question and answer as part of your password security model. Part of the PasswordRecovery control includes an interface that handles asking the user to answer their own secret question that was hopefully provided when they created their account. As shown below, you can edit the settings for each part of password recovery, including requesting the user name, asking the secret question, and indicating a response to the user after they complete the process.

Aa479008.aspnet-securingapps-14(en-us,MSDN.10).gif

Figure 14. The presence of a secret question and answer is configurable by the membership providers that I will mention later in this article.

This control automatically resets the password and sends the new password to the user's e-mail account. Properties of the generated e-mail can be configured through public properties of the control, and in my example I customized the subject heading and address from which the e-mail is sent. The resulting HTML looks like this:

    <asp:passwordrecovery id="Passwordrecovery1" runat="server">
        <maildefinition bodyformat="Html" subject="Password Recovery for
          MySecureNewsletter" from="mailto:mailadmin@dotnetdashboard.com">
        </maildefinition>
    </asp:passwordrecovery>

If I have properly configured an SMTP server for sending e-mail I will actually receive e-mail with my new password:

Aa479008.aspnet-securingapps-15(en-us,MSDN.10).gif

Figure 15. You can provide an HTML template for e-mail by setting the BodyFilename and BodyFormat properties through the Properties window.

In my sample I tested this feature using my own SMTP server, however you will see in the web.config file this example for SMTP settings:

       <smtpMail
            serverName="smtp.mysmtpserver.com"
            serverPort="25">
        </smtpMail>

This is a wonderful feature for a quick and dirty solution that handles password management, but you will likely take closer look at your component architecture for a high volume site. For example you want to be sure the component generating e-mail will scale, and you may want to write some code that controls how passwords are sent to members via e-mail, perhaps providing a link to an expiring Web page that will display the decrypted password.

Filtering Content by Roles

Most applications rely on roles to control access to resources, information display, and permissible activities. Earlier, I created a number of users and assigned them roles using the security administration tool. To leverage these roles in earlier versions of ASP.NET I would have to write code to manually retrieve roles from the credential store for the authenticated user. The LoginView control interacts with these roles through configured membership providers, or, membership API, and supports the provision of content templates for any valid role.

Let's assume I'm going to add a set of administrative pages to the application that should only be accessed by an administrator. If I add the Admin menu item to the header I probably only want it to show up for administrators. To implement this behavior I will add another LoginView control to my menu interface. One of the properties of the LoginView control, accessible through the Properties window, supports adding a list of roles to the RoleGroups collection through a dialog interface:

Aa479008.aspnet-securingapps-16(en-us,MSDN.10).gif

Figure 16. The RoleGroup Collection Editor requires that you manually enter roles. You can also group roles so that multiple groups can share the same template interface.

In Design view the LoginView control now reflects the list of roles as template choices:

Aa479008.aspnet-securingapps-17(en-us,MSDN.10).gif

Figure 17. The HTML view will be updated to reflect any templates you design for each role.

From the HTML source below, you can see that new content templates are being used for Admin and Member roles. The<anonymoustemplate>will still be used prior to authentication, but after authentication the first template matching one of the user's roles will be used. If no match is found<loggedintemplate>settings are considered the default.

<asp:loginview id="lvMenu" runat="server">
    <anonymoustemplate>
        <asp:loginstatus id="anonLoginStatus" runat="server">
        </asp:loginstatus>
    </anonymoustemplate>
    <rolegroups>
        <asp:rolegroup roles="Admin">
            <contenttemplate>
    <tr>
       <td class="OtherTabs">
        <asp:hyperlink id="adminHome" runat="server"
          navigateurl="~/default.aspx">Home</asp:hyperlink>
            &nbsp;|&nbsp;</td>
        <td class="OtherTabs">
         <asp:hyperlink id="adminAbout" runat="server"
           navigateurl="~/about.aspx">About</asp:hyperlink>
             &nbsp;|&nbsp;</td>
        <td class="OtherTabs">
        <asp:hyperlink id="adminAdmin" runat="server"
          navigateurl="~/admin/manageMembers.aspx">Admin
            </asp:hyperlink>&nbsp;|&nbsp;</td>
        <td class="OtherTabs">
        <asp:loginstatus id="adminLoginStatus" runat="server">
        </asp:loginstatus>
        </td>
    </tr>
            </contenttemplate>
        </asp:rolegroup>
        <asp:rolegroup roles="Member">
            <contenttemplate>
    <tr>
       <td class="OtherTabs">
        <asp:hyperlink id="memberHome" runat="server" 
          navigateurl="~/default.aspx">Home</asp:hyperlink>
            &nbsp;|&nbsp;</td>
        <td class="OtherTabs">
         <asp:hyperlink id="memberAbout" runat="server" 
           navigateurl="~/about.aspx">About</asp:hyperlink>
             &nbsp;|&nbsp;</td>
        <td class="OtherTabs">
        <asp:loginstatus id="memberLoginStatus" runat="server">
        </asp:loginstatus>
        </td>
    </tr>
            </contenttemplate>
        </asp:rolegroup>
    </rolegroups>
    <loggedintemplate>
    <tr>
       <td class="OtherTabs">
        <asp:hyperlink id="authHome" runat="server" 
          navigateurl="~/default.aspx">Home</asp:hyperlink>
            &nbsp;|&nbsp;</td>
        <td class="OtherTabs">
         <asp:hyperlink id="authAbout" runat="server" 
           navigateurl="~/about.aspx">About</asp:hyperlink>
             &nbsp;|&nbsp;</td>
        <td class="OtherTabs">
        <asp:loginstatus id="authLoginStatus" runat="server">
        </asp:loginstatus>
        </td>
    </tr>
    </loggedintemplate>
</asp:loginview>

These templates are parsed in order of appearance, and the first matching role will be used as the content for this LoginView. That means that care must be taken to place roles in appropriate order. My example results in restricting the new Admin menu item to those users assigned the Admin role.

We can also use roles to control access to other resources by specifying<authorization>rules to deny or allow specific roles. This can be achieved in the application level of the web.config file using<location>tags, or by adding web.config files to protected subdirectories. The following<authorization>setting is placed in my sample's /admin directory, and restricting access to those users assigned the Admin role:

           <authorization>
               <allow roles="Admin" />
               <deny users="*" />
            </authorization>

Now it's time to create some administrative pages to manage our members, and code against the membership API provided with ASP.NET Whidbey.

Membership and Role Providers

Most of what I have shown you thus far has been based on using the new security controls, however there are some underlying components that will allow us to directly manage users and roles. These components provide a layer of abstraction from the database access layer. To demonstrate, I will create a new content page within the /admin directory, named manageMembers.aspx. This page will display the list of newsletter members, and provide a central interface to add, edit or delete newsletter members.

I dropped a DataView control on the page with the intention of populating it with a list of users. The Page_Load event includes code to retrieve all users by leveraging the intrinsic Membership object. The new Membership class has methods and properties that provide direct access to the membership database created for us by default. For example GetAllUsers() returns a collection of MembershipUser objects for the application. The following code converts that into a format that can be bound to the DataView control (a workaround for the Alpha since the collection could not be bound):

MembershipUserCollection members = Membership.GetAllUsers ();
ArrayList arr = new ArrayList ();
foreach (MembershipUser member in members)
{
arr.Add (member);
}

GridView1.DataSource = arr;
GridView1.DataBind ();

From this page, the user can add, edit or delete members from the list. The delete link simply executes the following line of code:

Membership.Provider.DeleteUser(user);

Adding and editing members is handled by another new page I created, newMembers.aspx. When adding new members, this page collects the required information for a new member to be added to the Members database. For my newsletter, I will collect the new member's e-mail address and password, that's it. But, the database has support for both a user name, and an e-mail address, so I will use the e-mail address for both. In addition, I will collect role selections for the new user, which means I have to write code to dynamically list all of the roles available to the application.

The Page_Load event includes code to load available roles, and I'm using a Repeater control to dynamically build a list of checkboxes (similar to what you see in the security configuration wizard).

  
    // From newMember.aspx

  <asp:repeater runat="server" id="roleRepeater">
                        <itemtemplate>
                        <asp:checkbox runat="server" id="chkRole" 
                          text='<%# Container.DataItem.ToString()%>' 
                            checked="<%# m_theUser == null ? false : 
                              Roles.IsUserInRole(m_theUser.Username, 
                                Container.DataItem.ToString())%>"/>
                        <br/>
                        </itemtemplate>
</asp:repeater>

// From newMember.aspx.cs
roleRepeater.DataSource = Roles.GetAllRoles ();
roleRepeater.DataBind ();

The resulting entry page looks like this:

Aa479008.aspnet-securingapps-18(en-us,MSDN.10).gif

Figure 18. You can also add password reset, change password, and secret question and answer functionality to your member management process.

This page I had to design manually but the code required to add the new user is trivial using the Membership component once again:

  
    Membership.CreateUser(email.Text, pw.Text, email.Text);

  

A few more lines of code were necessary to extract the role selections from the Repeater control, and then populate the role table with the results for this user. Again, access to the roles database is trivial, this time using the Roles component:

string[] users = {email.Text};
string[] addRoles = new string[roleRepeater.Items.Count];
string[] remRoles = new string[roleRepeater.Items.Count];
int addIndex = 0;
int remIndex = 0;

foreach (RepeaterItem itm in roleRepeater.Items)
{
CheckBox c = (CheckBox)itm.FindControl ("chkRole");
string role = c.Text;

if (c.Checked && !Roles.IsUserInRole(users[0], role)) 
addRoles[addIndex++]=role;
else if (!c.Checked && Roles.IsUserInRole(users[0], role))
   remRoles[remIndex++]=role;
}

if (addIndex > 0)
{
string [] theRoles = new string[addIndex];
   Array.Copy (addRoles, 0, theRoles, 0, addIndex);
   Roles.Provider.AddUsersToRoles (users, theRoles);
}

if (remIndex > 0)
{
string [] theRoles = new string[remIndex];
   Array.Copy (remRoles, 0, theRoles, 0, remIndex);
   Roles.Provider.RemoveUsersFromRoles (users, theRoles);
}

The same newMembers.aspx page is used for editing users and a QueryString is used to indicate the current mode. When in edit mode, the Page_Load populates the interface with the user's information and current roles. Once again, the Membership class provides methods for finding a particular user record, and for updating the user record with changes. If I were using clear text passwords, and if I enable password retrieval in the membership provider configuration settings, the following code would update the user's password changes:

MembershipUser user = Membership.GetUser (email.Text);
  user.ChangePassword (user.GetPassword (), this.pw.Text);

GetPassword() fails by default, since the machine.config settings for each membership provider are installed for added security. Furthermore, hashed passwords are the default and recommended behavior for providers, and since hashed passwords are not retrievable you would have to collect the user's old password from the user interface, to invoke the ChangePassword() function.

But, Is It Extensible?

So far I have focused on the ease with which you can put together a soup-to-nuts implementation for authentication and role-based access control. Though all of this canned functionality will satisfy 80% of your needs, you can also extend this model quite easily.

Database Creation

The database creation process, for example, can be extended to support SQL server, or another custom Access database, directly from the Security Configuration Wizard. The following figure is an example of what you will see if you opt to create a new database during the Wizard steps:

Aa479008.aspnet-securingapps-19(en-us,MSDN.10).gif

Figure 19. The security wizard will create the default set of membership management tables in a database of your choosing, either local or remote.

Rather than working with a lightweight Access database that won't scale by any stretch you can create membership tables directly in your application's SQL Server database. Now that is what I call an improvement. Maybe grandma won't know the database name and credentials required to complete this step, but your development team surely will. The tables created are canned to match the requirements of the membership and role providers that we just used to implement the above functionality, but you can use an alternate table design and skip this step if you plan to build your own providers.

Identity Management

As you may have gathered from the code we wrote to build some administrative functionality access to the credential store is handled by membership and role providers. The System.Web.Security namespace now includes new SqlMembershipProvider and AccessMembershipProvider components to manage their respective data access requirements against the default credential tables. By default, the<membership>element of the machine.config includes two providers, for SQL Server and Access databases. The<providers>section can be used to add or remove providers, which gives you the ability to remove these default providers at the application level and configure your own. As you can see from the settings below, there are some predefined settings that relate directly to the table structure created by the wizard, including settings for password encryption, password reset and retrieval, the requirement of a secret question and answer, and the uniqueness of e-mail field entries. Each of these rules is enforced by the respective default membership (or, database) provider which gives you the ability to work with the canned table structure in a variety of ways, before resorting to creating your own tables and providers. You can also override the connection string used by each provider.

<membership defaultProvider="AspNetAccessProvider" 
  userIsOnlineTimeWindow="15" >
            <providers>
                <add name="AspNetSqlProvider"
                    type="System.Web.Security.SqlMembershipProvider, 
                      System.Web, Version=1.2.3400.0, Culture=neutral, 
                        PublicKeyToken=b03f5f7f11d50a3a"
                    connectionStringName="LocalSqlServer"
                    enablePasswordRetrieval="false"
                    enablePasswordReset="true"
                    requiresQuestionAndAnswer="false"
                    applicationName="/"
                    requiresUniqueEmail="false"
                    passwordFormat="Hashed"
                    description="Stores and retrieves membership data from
                      the local Microsoft SQL Server database"
                />

                <add name="AspNetAccessProvider"
                    type="System.Web.Security.AccessMembershipProvider, 
                      System.Web, Version=1.2.3400.0, Culture=neutral, 
                        PublicKeyToken=b03f5f7f11d50a3a"
                    connectionStringName="AccessFileName"
                    enablePasswordRetrieval="false"
                    enablePasswordReset="true"
                    requiresQuestionAndAnswer="false"
                    applicationName="/"
                    requiresUniqueEmail="false"
                    passwordFormat="Hashed"
                    description="Stores and retrieves membership data from 
                      the local Microsoft Access database file"
                />
            </providers>
        </membership>

In fact there is also another membership provider component, System.Web.Security.ADMembershipProvider. It performs the same activities mentioned above against an Active Directory store, but this is currently an internal feature.

The<roleManager>section also has configuration settings to control which data store it uses to access related role information. Three are configured by default: SqlRoleProvider, AccessRoleProvider, and WindowsTokenRoleProvider. The job of these components is to handle all role management for users. Again, a default set of tables is created for SQL Server and Access, and the WindowsTokenRoleProvider calls unmanaged code to access roles defined for the operating system's credential store.

        <roleManager
                enabled="false" cacheRolesInCookie="true" 
                  cookieName=".ASPXROLES" cookieTimeout="30"
                cookiePath="/" cookieRequireSSL="false" 
                  cookieSlidingExpiration="true"
                cookieProtection="All" 
                  defaultProvider="AspNetAccessProvider" >
           <providers>
               <add  name="AspNetSqlProvider" 
                 type="System.Web.Security.SqlRoleProvider, System.Web, 
                   Version=1.2.3400.0, Culture=neutral, 
                     PublicKeyToken=b03f5f7f11d50a3a"
                     connectionStringName="LocalSqlServer"
                     applicationName="/"
                     description="Stores and retrieves roles data from the 
                       local Microsoft SQL Server database" />

               <add name="WindowsToken"
                    type="System.Web.Security.WindowsTokenRoleProvider, 
                      System.Web, Version=1.2.3400.0, Culture=neutral, 
                        PublicKeyToken=b03f5f7f11d50a3a"
                    description="Retrieves roles data from the Windows 
                      authenticated token for the request" />


                <add name="AspNetAccessProvider"
                    type="System.Web.Security.AccessRoleProvider, 
                      System.Web, Version=1.2.3400.0, Culture=neutral, 
                        PublicKeyToken=b03f5f7f11d50a3a"
                    connectionStringName="AccessFileName"
                    applicationName="/"
                    description="Stores and retrieves roles data from the 
                      local Microsoft Access database file" />
           </providers>
        </roleManager>

Though this provider model was primarily designed to streamline forms authentication for Web applications, it can work with any authentication scheme to create and manage users and roles, and to execute common activities such as password resets, password encryption, and user validation.

Conclusion

The new components and architectural features in ASP.NET Whidbey will amaze you. What you will really enjoy about these new features is the ease with which you can pull together a complete application combined with the ability to extend those features very easily for an enterprise application that needs to scale. Particularly with respect to security, this new model removes the need to develop poor "demo code" habits using XML configuration files that hold unencrypted credentials and increase file access load on your server. We are always at peril of releasing demo code when deadlines get tight, so why not do it right the first time? The only thing missing now is a telepathic device driver that will translate my thoughts into code, real-time.

About the Author

Michele Leroux Bustamante is an Associate of IDesign Inc., a Microsoft Regional Director, a member of the International .NET Speakers Association (INETA) and a published author. At IDesign, Michele contributes her diverse background to .NET training and high-end corporate consulting. She focuses on the C# language, .NET Framework architecture, ASP.NET, and Web Services; and she also provides guidance to technology executives. Contact her at mlb@idesign.net or visit IDesign: .NET Design and Business Solutions to find out more. Also, visit .NET Dashboard to subscribe to her monthly .NET newsletter.

© Microsoft Corporation. All rights reserved.