IIS 7 and ASP.NET Providers (Membership, Roles, and Profile)

I talked at length about the ASP.NET Provider Model pattern and the ease of implementing several built-in services you can have for free with ASP.NET 2.0 in my previous series of posts.  In this post, I will be discussing the advantages of using the ASP.NET Provider system under IIS 7 found in Windows Vista and Server 2008.  I will also talk about an issue with using this Provider system to try and add authentication to an ASP.NET website that contains non-ASP.NET content, like static HTML pages.  I think I’ll start with the discussion about non-ASP.NET content first.

IIS 7.0 Integrated Pipeline

Since I don’t really understand much about ISAPI filters and such, I’m just going to quote stuff from random pages on the official Microsoft IIS site:

"IIS 6.0 and previous versions allowed the development of .NET application components via the ASP.NET platform. ASP.NET integrated with IIS via an ISAPI extension, and exposed its own application and request processing model. This effectively exposed two separate server pipelines, one for native ISAPI filters and extension components, and another for managed application components. ASP.NET components would execute entirely inside the ASP.NET ISAPI extension bubble and only for requests mapped to ASP.NET in the IIS script map configuration.

"IIS 7.0 integrates the ASP.NET runtime with the core web server, providing a unified request processing pipeline that is exposed to both native and managed components known as modules."

and…

"This allows developers to fully extend the IIS 7.0 server with the richness of ASP.NET 2.0 and the .NET Framework, instead of using the lower level IIS C++ APIs. Existing ASP.NET applications also immediately benefit from tighter integration using existing ASP.NET features like Forms Authentication, Roles, and Output Caching for all content."

Note the last three words there:  "for all content."  When I started this endeavor to implement the ASP.NET Provider system, I didn’t realize that it would really only apply to ASP.NET content and pages.  I didn’t realize on IIS 6 I would have to fiddle with ISAPI filters and content filters to make my implementation work.  And when I tried to doing a mapping (like *.html to the ASP.NET ISAPI DLL), I either didn’t do it right or it just isn’t very reliable and stable; here is a link to the thread I followed in trying to make the appropriate changes in IIS 6.  I will note however that UltiDev’s Cassini Web Server can host such a site with absolutely no extra modification, where even IIS 7 required a little configuration.

By default in IIS 7, an application pool runs using the Integrated mode for the Managed Pipeline configuration.  But there is also the option of Classic mode, which is helpful if you are needing strict compatibility with how you ran a website under IIS 6.  In many cases, however, an existing ASP.NET website runs under Integrated mode without any problems.

Configuring an ASP.NET Website to Run on IIS 7

First, I think I will point you to a great video and document on Microsoft’s official IIS site that explains everything you really need to know.  Then I will emphasize a few of the important points and also a gotcha or two.  Here are the links you should reference:

The video above is also a terrific introduction to IIS 7.  Below is a screenshot of IIS’s UI for configuring a website:

IIS_screenshot

Notice the ASP.NET section and how it’s able to use your Providers to bring back Users, Roles, and Profile data back from the database.  This UI reads from and writes to your Web.config and is really slick in my opinion.

The only real "breaking change" for running your website under IIS 7 is the fact that your Web.config might need a little migration to be compatible with Integrated mode.  Apparently IIS gives you helpful error messages if it detects you haven’t run through the migration yet.  The two most common things that ASP.NET websites have that require this migration are if you have declared httpModules or httpHandlers in your Web.config.  The fix is to either copy or move these sections under system.web to their corresponding system.webServer sections.  If you duplicate the sections, then you should be able run under both Integrated and Classic modes without any problems.  You can even still run on IIS 6 I believe, as IIS 6 will ignore this new section called system.webServer which is IIS 7 specific.  IIS 7 even provides a utility that can perform this migration for you; here is the what you would type in a Command Prompt:

%windir%\system32\inetsrv\APPCMD.EXE migrate config <Application Path>

…where <Application Path> would be something like "Default Web Site/" or "Default Web Site/someApp".  If you manually migrate your Web.config and are still getting migration error messages, here is a snippet you can add right under the system.webServer node to disable the error message:

<validation validateIntegratedModeConfiguration="false" />

Also, I want to make note of something I couldn’t get to work that they showed in the tutorial video.  At about the 13:50 mark in the video, they go into the Modules section of the UI.  These are the modules of the IIS pipeline for both managed .NET code and unmanaged code.  In order to make their video file hidden behind ASP.NET Forms Authentication, they change a property on two authentication modules to allow these modules to execute on non-managed content as well.  However, this did not work for me for some reason when I was trying to secure HTML pages behind Forms Authentication.  What did work for me (and may have been a drastic solution) was to put the following attribute on the modules node under system.webServer:

<modules runAllManagedModulesForAllRequests="true" />

I believe the issue in my case was their was another few managed modules that I needed to clear that checkbox for in the UI.  But I didn’t take the time to find out which ones I was missing and opted for the drastic solution.  I will also say there is a way to configure these managed modules through your Web.config so you don’t have to have your IT Admin configure properties manually in the UI.  In fact, it seems that much of IIS 7 configuration for your website can be done this way.

A Few Error Messages (and Their Solutions)

If you get error messages while using the IIS Manager user interface like this…

"This feature cannot be used because the default provider type could not be determined to check whether it is a trusted provider.

"You can use this feature only when the default provider is a trusted provider.  If you are a server administrator, you can make a provider a trusted provider by adding the provider type to the trusted providers list in the Administration.config file.  The provider has to be strongly typed and added to the GAC (Global Assembly Cache)."

…then you should visit the following article for detailed information about resolving the error message:

Getting rid of this error message is really only for convenience in letting the IIS Manager UI be able to query your database using your providers and give you a basic view of your Membership, Roles, and Profile fields.  Also, if you are trying to find your Administration.config file, it is likely located under the C:\Windows\System32\inetsrv\config\ folder.

The other error message I received was not in the IIS Manager UI, but rather when you attempt to view your website in a browser (after you think you got all the configuration bugs out!).  A BadImageFormatException is thrown and the error details say something like this:

Could not load file or assembly ‘AssemlyNameHere’ or one of its dependencies.  An attempt was made to load a program with an incorrect format.

The likely cause of this error is that the assembly referenced in the error message was compiled only for 32-bit machines and you are running a 64-bit operating system.  With Windows Server 2008 x64, the application pools in IIS have an Advanced Setting called Enable 32-Bit Applications, that is disabled by default.  Enable this setting and you’re probably good to go.  See the following link for more detailed information and some nice screenshots:

Additional Reading

I just barely noticed that 4GuysFromRolla.com recently posted an article about the exact same stuff I was attempting to explain.  To read additional details about IIS 7’s Integrated Pipeline and ASP.NET Forms Authentication with static content, check out the following link:

Conclusion

I hope you’ve enjoyed this series on the ASP.NET Provider System.  My desire is that you’ve found these posts helpful in getting you started with implementing such a versatile and customizable model for adding common membership and authorization functionality to your new and existing websites.  There are quite a few advantages in hosting your site on IIS 7 in Windows Server 2008, as I’ve outlined in this specific post.  Hopefully these discussions about errors and gotchas will also help you get going in no time!

Comments

Anonymous
Thanks. Awesome job.
Tony
Prefect article and thanks for the video links. It helped me understand this whole integrated vs classic iis7 mode and was a refresher on forms authenication.
Radek
Thanks. This post helped me resolved serious issue with IIS7

ASP.NET Profile and Web Application Projects

I talked at length about the ASP.NET Provider Model pattern and the ease of implementing several built-in services you can have for free with ASP.NET 2.0 in my previous series of posts.  In this post, I will be discussing a few issues I ran into using the Profile system with the Web Application project template instead of the Web Site project template as found in Visual Studio’s list of default project templates.

The crux of the issue is that Visual Studio doesn’t automatically generate the Profile proxy class object when using the Web Application project template, like it does with the Web Site project template.  This proxy class can be used to obtain access to strongly-typed properties representing the custom Profile fields you specified in your Web.config.  This is a very convenient way to access a user’s Profile data, both for authenticated and for anonymous users.

Let’s discuss the issue a little further and then I will present a workaround that I found and have successfully implemented in my own project.

VS Project Templates:  Web Site vs. Web Application

It appears that Microsoft has tried to differentiate these two project templates as much as possible in Visual Studio 2008.  In 2005, you would go to the New Project dialog window and you would see these two Web project templates side-by-side, wondering what difference it would make if you chose one over the other.  However, in Visual Studio 2008, it appears they reworked this whole dialog and menu system for starting a new project so that you would never see these two project templates side-by-side.  In 2008, you have more than one option in the File menu for starting a project; those options are File | New | Project and File | New | Web Site.  The first option is the one that will get you to the ASP.NET Web Application template, while the second takes you to the ASP.NET Web Site project template.

Now as for the differences between these two templates, I’ll outline a few of the main differences and give a little bit of history of how these two templates came to coexist.  The history in a nutshell is that Visual Studio 2005 came out with the Web Site project template to replace the old, but proven project configuration used by ASP.NET 1.x.  In the opinion of many, the old template better encouraged Object-Oriented design practices.  The new template had no project file for explicitly delineating what files where included in the project and namespacing was quite cumbersome, as the only painless design was to let everything pile up into one big default namespace.  A few other changes involved taking ASP.NET back to compiling on the fly at the web server and also drastically changing the organization of UI template and code-behind files.  It appears the intention was to closely mimic the configuration and deployment practices of popular agile languages like PHP and Ruby on Rails.

In the Service Pack 1 of Visual Studio 2005, Microsoft decided to resurrect the older project template under the name of Web Application.  There is evidence to suggest that Microsoft considers this latter project template an "enterprise" project type, as compared to the casual Web Site project template.  With the advent of Visual Studio 2008, it is apparent that Microsoft is committed to supporting both project types for at least a little while.  For a more detailed discussion of the history and differences of these two project templates, I suggest the following blog post by Stephen M. Redd:

Web Profile Builder by Joe Wrobel

The ability to retrieve and update your custom Profile fields at design time via strongly-typed properties on the Profile proxy class is extremely convenient.  Unfortunately, you get no such compiler and intellisense love if you are using the Web Application project template for your site.

In trying to find a workaround, I came across a blog post by Joe Wrobel introducing a project he worked on to solve this issue.  It’s called the Web Profile Builder (here’s a link to it’s project page on MSDN Code Gallery) and it was based on a project called the Web Profile Generator, which was a Visual Studio 2005 Add-In project found on CodePlex.  The latter project seems to have not been worked on in over a year and a half, nor does it seem to be compatible with Visual Studio 2008.  Hence, Joe feeling the need to start up on his own project, with a few improvements on the side of course.

In Joe’s introductory post to the project, he mentions he changed the Visual Studio integration from being a plug-in to being an MSBuild Build Task.  The method for incorporating the Build Task into your project is to add a line into your project file (the one with file extension .csproj).  In case you weren’t aware, Visual Studio project files are just MSBuild build scripts.  For details about where to put this line in the file (and about a message Visual Studio pops up when it detects you’ve mucked with the project file), be sure to check out Joe’s post on the Web Profile Builder project.

There are a couple of other things I need to point out about using the Build Task that weren’t entirely clear to me beforehand.  The build task actually generates a C# code file that is intended to be included into your project; once it’s generated, you need to go to the Solution Explorer and explicitly add it as a file in your project (one way is through the Add | Existing Item menu option).  One of the main things that helped me decide to use this solution and how to configure it was reading two blog posts from Anthony Grace; the second post below has a great excerpt apparently from the Web Profile Builder documentation that explains the process I outlined above in a bit more detail:

Also, it is not necessary to do the steps in the Extended Usage section of Joe’s blog post.  They are optional configuration points in the Web.config where you control the className, nameSpace, directory, and fileName of the auto-generated code file.  Most of the defaults are fine, but in my case I wanted the generated file to end up in an App_Code directory instead of in the root of the project folder.  If you decide to add in this custom configuration, you need to be aware that you must add the following XML namespace to the configuration root element of your Web.config:

xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"

Finally, it should be noted that if the website you are building is under source control, you will likely want to always keep this auto-generated code file checked out in order to prevent the file from having its Read-Only attribute set when it’s checked in.  If you don’t have it checked out, the build will throw out a warning and the generated code file will not be regenerated if changes in your Web.config need to be propagated to it.

Conclusion

With all of this in place, you should be able to use the Profile proxy class object in your ASPX code-behind files, just as I gave examples of in my previous post about the ASP.NET Profile system.  Although in my case, I mostly used the static method GetProfile from the auto-generated WebProfile class (the name the Visual Studio Build Task selects by default).

As I mentioned when I first started the ASP.NET Provider Series of posts, my next blog post will be about some advantages of using this Provider system on IIS 7 and a few issues when your ASP.NET website contains non-ASP.NET content:

Comments

Martena Gaines
Web sites are dynamically compiled and source is distributed with them. Web Applications can be compiled into a single DLL and deployed. You cannot use the app_code folder in WAP projects, but you can with WSP.

ASP.NET Providers - Profile

This blog post is part of a series about the ASP.NET Providers (namely Membership, Role Management, and Profile). The introductory post in the series can be found at the following link, which introduces the Provider Model pattern and gives a personal example of an implementation I have been working on:

This post will deal with adding Profile properties to your user accounts (and apparently this can also be used with anonymous users as well).  However, I will suggest using a slightly different Profile Provider than what comes in ASP.NET by default.  This will show how easy it is to plug in a different implementation in this very flexible Provider Model.

SqlProfileProvider vs. SqlTableProfileProvider

The SqlProfileProvider is the built-in Profile Provider that ASP.NET comes with by default.  It allows you to define custom fields that make up profiling data you would want to collect and store about your users.  This could be demographics, preferences, or any other useful details.  This specific implementation is designed in a way where no matter what fields you define, your database schema will be compatible with any upgrade Microsoft could send down to patch the Provider system or ASP.NET in general.  The way this is accomplished is by storing a blob of all the field names in one table column and a blob of all the field values in another column; it’s similar to the concept of a dictionary with key/value pairs.  The difficult part is that these blobs can contain clear-text, XML, and/or binary, depending on how the source data type serializes by default.  Unfortunately, this highly flexible solution is quite unusable if you need a clear view of the Profile data for reporting purposes or stored procedure needs.

When trying to find an already implemented custom provider that would allow to store each Profile field in its own table column, I came across a blog post from Scott Guthrie introducing such an implementation done by two Microsoft employees.  The name of this provider is the SqlTableProfileProvider, and it can be found at the following link:

Table Profile Provider Samples

At the above location, you will find an MSI download that will install a sample project containing a few source code files you can include in your project; you will also find a download link for a "white paper" Word document that explains the usage of the SqlTableProfileProvider and the SqlStoredProcedureProfileProvider custom providers.  The nice thing about both of these providers is that their use in code and their configuration in the Web.config is extremely similar to how the original SqlProfileProvider is used and configured.

Setting up the SqlTableProfileProvider

As was done with the Membership and Role Management Providers, here is the XML fragment that would go in your Web.config:

<configuration>
    <connectionStrings>
        <add name="MyDB" connectionString="..." />
    </connectionStrings>
    <system.web>
        ... authentication & authorization settings ...
        <profile enabled="true"
                 defaultProvider="ConfiguredProfileProvider">
            <providers>
                <add name="ConfiguredProfileProvider"
                     type="Microsoft.Samples.SqlTableProfileProvider"
                     connectionStringName="MyDB"
                     table="custom_Profile"
                     applicationName="SampleWebSite" />
            </providers>
            ... properties section found in the next XML snippet ...
        </profile>
    </system.web>
</configuration>

WARNING: According to Microsoft, you should never use the default provider settings and should always add a new, configured provider; in addition you must be sure to specify the applicationName attribute! See Scott Guthrie’s blog post on this topic for more details. Apparently, you can also use a value of "/" for the applicationName and it will use the root application name.

You’ll notice in the type attribute that we are not referencing the default SqlProfileProvider that ships with ASP.NET.  What you need to do is get this new class included or referenced in your solution so that this namespace reference can successfully resolve.  I opted to just include the code files in my solution as is; I chose to place them in my App_Code folder.  The code files you want to grab are likely in the default location that the MSI installed to, which is Program Files/Microsoft/ASP.NET 2.0 Table Profile Provider Samples.  The two files you need are SqlStoredProcedureProfileProvider.cs and SqlTableProfileProvider.cs, both of which are found in the App_Code folder of the sample project.  You need to copy both code files to your solution, as it appears that the SqlTableProfileProvider depends on some logic in the SqlStoredProcedureProfileProvider for some reason.

Also note the table attribute above (which is not present in the built-in SqlProfileProvider), which tells the provider which table in your database will be storing the custom Profile fields.  As for the "properties section" referenced near the end of the above snippet, this is where you specify the custom Profile fields and their mappings to column names in the specified database table.  The advantage of specifying this information in the Web.config is that you will be able to get programmatic access to these fields as strongly-typed properties on a proxy class named Profile in your code-behind.  Here is a sample properties configuration:

<properties>
    <add name="FirstName"
         type="string"
         defaultValue="[null]"
         customProviderData="FirstName;nvarchar" />
    <add name="Age"
         type="int"
         customProviderData="Age;int" />
    <add name="AccountExpiration"
         type="DateTime"
         defaultValue="[null]"
         customProviderData="AccountExpiration;datetime" />
    <add name="CookieID"
         type="System.Guid"
         defaultValue="[null]"
         customProviderData="CookieID;uniqueidentifier" />
</properties>

The Profile system provides a configuration point for each Profile field called customProviderData.  It appears the default SqlProfileProvider doesn’t even look at this additional attribute, but rather reserves this configuration point for use by custom implemented Profile providers.  The SqlTableProfileProvider utilizes this extra metadata field as a place to put mapping information for a column name and ADO.NET database type.  If you have questions about which ADO.NET and .NET types to use/specify in this configuration, there is helpful information in the accompanying white paper found on the download site.  Also found in that document is a great discussion you may want to read about the defaultValue attribute, what it configures, and how it might differ for value types (such as integer).

Creating the Database Table

Now that you have the code files in your solution and have configured the Profile fields, you need to create the custom table in the database that this provider will query and update.  Here is a sample SQL definition for our custom profile table:

create table dbo.custom_Profile
(
    UserId               uniqueidentifier not null Primary Key,
    FirstName            nvarchar(50) null,
    Age                  int null,
    AccountExpiration    datetime null,
    CookieID             uniqueidentifier null,
    LastUpdatedDate      datetime not null
)
go

Note that the SqlTableProfileProvider requires two extra columns in addition to your custom defined Profile properties, namely UserId and LastUpdatedDate.  One other thing to be aware of is that if you use the Membership API to delete a user programmatically, this will remove all entries related to that user in the database except from your custom Profile table.  I believe this might be because the Profile system can apparently be used independently of the Membership system, even allowing you to store Profile data on anonymous, unauthenticated users.  I think the same caution would go for any utility that presumably uses this Membership API under the covers, such as the ASP.NET Website Administration Tool launched from Visual Studio that I referred to earlier in this series with a post titled ASP.NET Providers - Getting Started.  Up to this point, I have resorted to manually clearing out Profile table rows myself that are linked to the membership account being removed from the database.

Also, if your web site is using a Windows user or system account for the identity of the worker process, you’ll want to make sure that set of credentials has SELECT, INSERT, UPDATE, and DELETE permissions granted to it for this new custom database table you created.  There are also some EXECUTE, SELECT, and UPDATE permissions required on a few of the already-existing stored procedures that the Provider system uses.  Again, check the accompanying white paper on the download site for a discussion about these procedure and for some SQL statements to help you automate the granting of these permissions.

The Profile Proxy Class

If you are using the Web Site project template in Visual Studio, then you are good to go with using the Profile proxy class that can be used anywhere in your code to retrieve and update Profile fields on the current user.  The availability of this Profile class is independent of whether you are using the built-in ASP.NET SqlProfileProvider or the custom SqlTableProfileProvider that I’ve been showcasing thus far.  However, if you are using the Web Application project template, this proxy object is not auto-generated for you by Visual Studio.  For a detailed discussion of the problem and a working solution, see my next post which is entitled ASP.NET Profile and Web Application Projects.

Use of this Profile class couldn’t be easier and is exactly as you would expect it to be.  Here is a code snippet that both retrieves and updates strongly-typed Profile properties (exactly as they were defined in the Web.config) using this proxy object:

// Set Profile property
Profile.FirstName = txtBoxFirstName.Text;

// Retrieve Profile property
int age = Profile.Age;

Read On!

This crash course of the ASP.NET Provider system is pretty much done I guess you could say, as far as a tutorial goes that is.  I still have another two posts on the radar that will be coming over the next few days regarding issues and workarounds that I encountered.  One post will deal with generating this Profile proxy class under the Visual Studio Web Application project template.  The other post will be about the advantages of using this system under IIS 7 found in Windows Vista and Server 2008; it will also include some issues with using this system if some of your content is static HTML instead of ASPX pages.  Here are the links to these two posts:

It has been a very pleasant experience overall to work with the ASP.NET Provider system because of it’s highly customizable and flexible design.  If your Membership, Role Management, and Profile needs are fairly common and typical, this framework is a piece of cake to incorporate into your product (including your existing ones, no matter how out-of-date they are).  Hope you’ve enjoyed the series!

Comments

mamali
perfect, thank you very much…
Bryan
I've been using the profile system with good results, but in the last couple of weeks, I must have made some change because now I'm getting the error: "Column name 'UserID' appears more than once in the result column list." I can see the SQL command that's generated in SqlTableProfileProvider.vb, and it indeed contains two columns with name UserID. The problem is, I'm unable to find the source of the second UserID column. What can I do to trace and resolve the problem?
Esteban Araya
@Shalan & Mike:

I'd just store the photo on a file server, and then just store a UNC path to the photo on the custom profile table.

That way, all you'd have to do is: read the path, load the photo & display it.

Hope this helps!
Mike Murray
Ya know, I'm not really sure. But I am wondering if I've got something that might lead to an answer. I'm looking at the examples in my blog post and notice the Guid field. In order for that data type to work, it had to be specified as System.Guid, I'm assuming because it's not a primitive type. Perhaps something similar has to be done, like System.Array or System.Array<Byte>…not really sure. The other thing I can think of is maybe using the primitive version of "byte" instead of "Byte".

Just a few random guesses. Let us know if you find the resolution and post back here. Thanks!
shalan
Hi Mike. Excellent article! I’ve been using TableProvider for some time now, but recently I had a first case where the user wanted to incorporated photos as part of their profile.

I have tried to accomplish but to no avail. Its setup as a varbinary field in the database, and a Byte[] datatype in the web.config, but I keep getting an error saying that it could not load type “Byte[]”. Have you tried this? Do you have any suggestions?

Thanx!
Mike Murray
Very good question.

In my particular use case, the need to delete a user account from the system would be so rare that I am content to leave it a manual process. And since I’ll be the one managing it for a while, I’m content with this solution for the time being.

I will give this question some thought, however, and I will post back if come up with an idea or two…that is if someone else doesn’t beat me to it here in the comments.

Please do let us know if you come up with something yourself. Thanks, and good luck.
Anonymous
Has anybody came up with a way to automatically delete custom profile table along with the rest of the membership information?

ASP.NET Providers - Role Management

This blog post is part of a series about the ASP.NET Providers (namely Membership, Role Management, and Profile). The introductory post in the series can be found at the following link, which introduces the Provider Model pattern and gives a personal example of an implementation I have been working on:

This post will deal with adding Role Management to your website.

Role Management

Creating roles and assigning users accounts to them is a great way to group users together under similar permissions and abilities.  The ASP.NET Roles Management service allows you to restrict page viewing (or even just certain regions of a page) based on roles you define; you can even apply roles security to specific methods or classes using attributes, which I will show an example of a little later.

In order to add role management to your site, add an XML fragment like the one below; the process is much the same as it was with the Membership Provider:

<configuration>
    <connectionStrings>
        <add name="MyDB" connectionString="..." />
    </connectionStrings>
    <system.web>
        ... authentication & authorization settings ...
        <roleManager enabled="true"
                     defaultProvider="ConfiguredRoleProvider">
            <providers>
                <add name="ConfiguredRoleProvider"
                     type="System.Web.Security.SqlRoleProvider"
                     connectionStringName="MyDB"
                     applicationName="SampleWebSite" />
            </providers>
        </roleManager>
    </system.web>
</configuration>

WARNING: According to Microsoft, you should never use the default provider settings and should always add a new, configured provider; in addition you must be sure to specify the applicationName attribute! See Scott Guthrie’s blog post on this topic for more details. Apparently, you can also use a value of "/" for the applicationName and it will use the root application name.

Role names can be used in the Web.config in the same place we used the user class wildcard characters (namely, * for all users and ? for anonymous users); the following sample fragment would go under the system.web node:

<authorization>
    <deny users="BannedUsers,?" />
    <allow users="PremiumUsers" />
</authorization>

Role names can be defined by using the ASP.NET Website Administration Tool launched from Visual Studio that I referred to earlier in this series with a post titled ASP.NET Providers - Getting Started.  You can also use this utility to assign users to one or more roles.  Roles can be programmatically assigned and verified in code as well, using the provided Roles .NET API.

Attribute-based Role Management

In order to put authorization rules on classes or methods, you use the attribute named PrincipalPermission.  Here is a sample that contains a method only accessible by a user with the username of administrator AND in the role group Admin:

[PrincipalPermission(SecurityAction.Demand, Name="administrator", Role="Admin")]
public void SomeMethod(...)
{
    ...
}

The OR version of the above logic (meaning the user account being verified need only satisfy one of these authorization rules) would be represented like this:

[PrincipalPermission(SecurityAction.Demand, Name="administrator")]
[PrincipalPermission(SecurityAction.Demand, Role="Admin")]
public void SomeOtherMethod(...)
{
    ...
}

Here is an interesting sample that restricts a page to authenticated users (those not visiting anonymously); you would put the attribute on the code-behind of an ASPX page, like this example for SomePage.aspx:

using System.Security.Permissions;

[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
public partial class SomePage : System.Web.UI.Page
{
    ...
}

In all of these cases, if the authorization criteria is not met, a SecurityException is thrown.  You would want to catch that exception and perform alternate logic or display a more user-friendly error message.

Read On!

Continue onward by reading the next blog post in this series found at the following link:

ASP.NET Providers - Membership

This blog post is part of a series about the ASP.NET Providers (namely Membership, Role Management, and Profile). The introductory post in the series can be found at the following link, which introduces the Provider Model pattern and gives a personal example of an implementation I have been working on:

This post will deal with adding the Membership functionality to your website.

Membership Provider Configuration

Now that your DB is set up, there are a few snippets that need to be added to your Web.config. It involves adding a connection string and adding a new membership provider, as in the sample below:

<configuration>
<connectionStrings>
<add name="MyDB" connectionString="..." />
</connectionStrings>
<system.web>
... authentication & authorization settings ...

<membership defaultProvider="ConfiguredMembershipProvider">
<providers>
<add name="ConfiguredMembershipProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="MyDB"
applicationName="SampleWebSite"
minRequiredPasswordLength="5"
minRequiredNonalphanumericCharacters="0" />
</providers>
</membership>
</system.web>
</configuration>

WARNING: According to Microsoft, you should never use the default provider settings and should always add a new, configured provider; in addition you must be sure to specify the applicationName attribute! See Scott Guthrie’s blog post on this topic for more details. Apparently, you can also use a value of "/" for the applicationName and it will use the root application name.

The last two attributes of the add node are only a couple of several membership features you can configure. For a complete list of attributes you can configure, see <add> Element for Providers for Membership.

Securing Site Content by Requiring Authentication

With 5 more lines of configuration added to your Web.config, you can have the whole site secured and accessible only by user account authentication. Even better, you can make that 6 lines added to the Web.config, add a Login.aspx page, and drop in a Login control from the Toolbox and you will get auto-redirect to the login page if someone tries to hit any page you’ve now secured. The framework is even smart enough to return you to that desired page after successful login; it accomplishes this by redirecting you to the login page but by adding a ReturnUrl query string attribute to know where to go after authentication.

Here are the additional lines for your Web.config (put them under the system.web node):

<authorization>
<deny users="?" />
</authorization>

<authentication mode="Forms">
<forms defaultUrl="index.html" loginUrl="Login.aspx" />
</authentication>

NOTE: You will likely already have an authentication node in your Web.config, but it’s mode may be set to the default "Windows" value. To use the Membership Provider for authentication, you must have this value set to "Forms."

The optional 6th line I spoke of earlier is the forms node under the authentication node. The defaultUrl attribute of this line is used to tell the framework where to go after successful login if there is no ReturnUrl specified in the query string. The loginUrl attribute is used to specify the page the user is redirected to if they are attempting to hit a page that requires authentication.

The deny node is using the question mark (?) wildcard character, which is used for anonymous users (or those that haven’t been authenticated yet); the asterisk (*) wildcard is used to signify all users browsing the site. Since we are dealing with the Web.config at the root of the site, this deny rule will apply to the whole site and it’s subdirectories. You can either put a Web.config in each subfolder that requires an overriding or more restrictive set of allow and deny rules, or you can actually specify location elements in your Web.config, as is shown in the following example (basically you could have several of these location elements, each with their own authorization rules):

<configuration>
<location path="Login.aspx">
<system.web>
<authorization>
<allow users="?" />
</authorization>
</system.web>
</location>
</configuration>

As far as I can tell, this sample configuration above isn’t necessary if you have specified your login page in the loginUrl attribute of the authentication/forms node like we did in the previous code snippet. But this sample is at least illustrative of the location-based authorization rules.

Personally, I decided to create a folder named Public in my site and I give it its own Web.config that allows all users (including anonymous ones) access to the containing web pages. This folder currently contains the login, user account registration, account activation (a link included in the email sent upon registration), and password retrieval pages, all of which would need unrestricted access for anonymous users.

Test Your Membership Provider

With all of this now in place, you should be able to use the ASP.NET Website Administration Tool launched from Visual Studio to create a few new user accounts. I mentioned how to launch this utility earlier in this series with a post titled ASP.NET Providers - Getting Started. Then you can double-check your membership system is working by logging in with your newly created credentials. Also, you can now create a user account registration page (utilizing the CreateUserWizard control) and a password retrieval/reset page (using the PasswordRecovery control). All of the controls I have named throughout this post are highly skinable and configurable via numerous attributes and templates; they also contain flexible task workflows that you can override or plug into by registering to any of several event listeners.

One other thing that needs to be done if you want to enable the email notification capabilities is to set the SMTP settings in your Web.config.  Here is a snippet you would put right above the system.web node:

<system.net>
    <mailSettings>
        <smtp from="service@website.com">
            <network host="mail.domain.com" port="25"
                     userName="..." password="..." />
        </smtp>
    </mailSettings>
</system.net>

Membership API and MembershipInfo Class

There is also a Membership API and an accompanying MembershipInfo class that you can call in order to retrieve and update user info from the database if you wanted to create your own custom controls or page logic. For instance, the following is code that I adapted from Part 11 of the tutorial I mentioned earlier in the introductory post of this series; this code activates an account when a user clicks the link emailed to them during registration:

public partial class Verify : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//Make sure valid querystring value was passed
if (string.IsNullOrEmpty(Request.QueryString["ID"]) ||
!Regex.IsMatch(Request.QueryString["ID"],
@"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}"))
{
InformationLabel.Text = "An invalid ID value was passed " +
"in through the query string.";
}
else
{
//ID exists and is kosher, approve user
//Get the ID sent in on query string
Guid userId = new Guid(Request.QueryString["ID"]);

//Get user object
MembershipUser userInfo = Membership.GetUser(userId);

if (userInfo == null)
{
//Could not find user!
InformationLabel.Text = "The user account could not " +
"be found in the membership database.";
}
else
{
//User is valid, approve them
userInfo.IsApproved = true;
Membership.UpdateUser(userInfo);

//Display a message
InformationLabel.Text = "Your account has been " +
"verified and you can now log into the site.";
}
}
}
}

Read On!

Continue onward by reading the next blog post in this series found at the following link: