Recipe: Implementing Role-Based Security with ASP.NET 2.0 using Windows Authentication and SQL Server



You are building an Intranet expense report application for your organization, and want to enable role-based authentication and authorization capabilities within it.  Specifically, you want to create logical roles called “approvers”, “auditors”, and “administrators” for the application, and grant/deny end-users access to functionality within the application based on whether they are in these roles.

Because your application is an Intranet solution, you want to use Windows Authentication to login the users accessing the application (avoiding them having to manually login).  However, because the roles you want to define are specific to your application, you do not want to define or store them within your network’s Windows Active Directory.  Instead, you want to define and store these roles within a database.  You then want to map Windows user accounts stored within Active Directory to these roles, and grant/deny access within the application based on them.

In addition to using roles to authorize access to individual pages within the application, you want to dynamically filter the links displayed within the site’s menu navigation based on whether users have permissions (or not) to those links.  And lastly, you want to build-in a custom role-management administration UI directly within the expense report application for “expense app administrators” to manage these roles and control who has access to the capabilities of the app:

The below post walks through step-by-step how to implement all of this.  You can download and run the completed sample we’ll build below here.


ASP.NET provides a flexible authentication management system that allows you to easily take advantage of Windows authentication to identify “who” the authenticated user accessing your site is.   You can learn how to enable this and how it works by reviewing my previous Recipe: Enabling Windows Authentication within an Intranet ASP.NET Web Application.

Most web applications typically have at least dozens (if not thousands or millions) of authenticated users accessing them.  Authorizing access to pages or functionality within a site is difficult (if not impossible) when managing it on an individual user account level.  Instead, it is much better for the application developer to define higher level “roles” to map users into, and then grant/deny access or permissions based on these roles.

For example: if we were building an internal expense reporting application, we might want to create three roles for the application: “approvers”, “auditors” and “administrators”.   We would then only allow users in the “approvers” role to access the portion of the site that enables someone to approve employee expenses.  We would only allow users in the “auditors” role to access the portion of the site that generates reports and analysis on employee spending.  And we would only allow the select few users in the “administrators” role to have access to the admin pages of the application.

By coding against roles like “approvers”, “auditors” and “administrators”, as opposed to individual account names, we can have very clean code within our application, and make the application very flexible as we add/remove users to the system and change their permissions over time.  For example, if “Joe” gets promoted to be a manager with expense approver permissions, all that needs to happen for him to get access to the approver portion of the expense app is for the administrator to go in and add him into the “approvers” role – no code changes or configuration changes to the app need to be made.

ASP.NET supports multiple places where user to role mappings can be stored and defined.  When Windows Authentication is enabled, ASP.NET will by default use the Active Directory user/group mappings to support role access permission checks.  This is useful when the permission checks you want to perform are global to your company environment.

For many applications, though, you might want to implement more local role policies – where the roles you define are specific to the application.  In cases like these you either might not want to store these in a central Active Directory store, or your network administrator might not even allow it.   Instead, you might want to store the role definitions and user mappings locally within a database – while still using Windows Authentication to identify and login the users stored within them. 

The below walkthrough demonstrates how to-do this, and builds a complete end-to-end application to illustrate how all the pieces fit together.


Step1: Create a New Web Site

Use Visual Studio or Visual Web Developer (which is free) to create a new ASP.NET 2.0 web site/project.

We'll start by creating a new external CSS stylesheet called “Stylesheet.css”.  We'll use this to style the application consistently.  Add the below CSS rules to it:


body {




.loginname {





.menu {









We'll then add a new file called “Web.SiteMap” in the top level "root" directory of the project.  SiteMap files enable us to specify the site hierarchy and link structure that we want to use to organize pages within our site, and can be used to databind Menus and navigation UI against (treeviews, breadcrumb controls, etc).  Within the Web.SiteMap file add this XML to define the link structure for the site we are going to build:

<?xml version="1.0" encoding="utf-8" ?>


<siteMap xmlns="" >


    <siteMapNode url="default.aspx" title="Home">

        <siteMapNode url="approver.aspx" title="Approver Expenses" />

        <siteMapNode url="audit/auditor.aspx" title="Audit Page" />

        <siteMapNode url="admin/admin.aspx" title="Admin Manager" />





Then we'll create a Master Page template called “Site.Master” at the root of the project.  This will enable us to define an overall consistent layout to use for all pages on the site.  Within it we'll add a Treeview control that is databound to our site navigation hierarchy that we defined above, and which will provide a hierachical menu for navigating the pages in our site.  Define the contents of the Site.Master page like so:

<%@ Master Language="VB" CodeFile="Site.master.vb" Inherits="Site" %>



<head runat="server">

    <title>Expense Report</title>

    <link href="StyleSheet.css" rel="stylesheet" type="text/css" />



    <form id="form1" runat="server">


    <div class="header">


            Expense Report Sample

            <asp:LoginName ID="LoginName1" FormatString="(Welcome {0})" CssClass="loginname" runat="server" />




    <div class="menu">


        <asp:TreeView ID="T1" DataSourceID="SiteMapDataSource1" runat="server" ImageSet="Simple" EnableViewState="False" NodeIndent="10">

            <ParentNodeStyle Font-Bold="False" />

            <HoverNodeStyle Font-Underline="True" ForeColor="#DD5555" />

            <SelectedNodeStyle Font-Underline="True" ForeColor="#DD5555" HorizontalPadding="0px" VerticalPadding="0px" />

            <NodeStyle Font-Names="Verdana" Font-Size="8pt" ForeColor="Black" HorizontalPadding="0px" NodeSpacing="0px" VerticalPadding="0px" />



        <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />       




    <div class="content">


        <asp:contentplaceholder id="MainContent" runat="server">








Note: for convenience sake I’m using the “Simple” auto-format selection option within the VS designer to style the TreeView control above.  One downside is that this is embedding inline styles.  To enable a pure CSS styling solution for the TreeView above I would want to download and use the ASP.NET 2.0 CSS Control Adapter Toolkit. 

After we define the Site.Master template, we’ll create a new page called “Default.aspx” that is based on the master-page and that we will use as the home-page for the site.  Create the page like so:

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">


    <h3>Some typical home-page content would go here...</h3>



And then run the page.  You will see a page like this:

Note that the tree-view’s site navigation links on the left-hand side of the page are databound from the ASP.NET 2.0 Site Navigation System.

Next create two subdirectories in the project called “admin” and “audit” and add pages within these sub-directories called “admin.aspx” and “auditor.aspx” respectively.  Also then create a top-level page called “approver.aspx” in the root directory of the site.  All of these pages should be based on the Site.Master template we defined above.

When you are done with this your project’s solution explorer will look like this:

Now we are ready to enable Windows Authentication and add Role Support to the site.

Step 2: Enable Windows Authentication

To enable Windows Authentication for our expense report web-site above, and force users to always be authenticated when visiting the application, we’ll want to open our web.config file at the root of the project and add this XML to it:

   <authentication mode="Windows" />



      <deny users="?"/>         


To understand better what this does, please take a few moments to review my previous Recipe: Enabling Windows Authentication within an Intranet ASP.NET Web Application.

Once we’ve configured the settings above, every user to the site will automatically be validated and authenticated via our Windows authentication system on the network. 

Step 3: Enable SQL based Role Management

ASP.NET 2.0 ships with built-in role manager providers that work against SQL Express, SQL Server, and Active Directory (which can also be used against ADAM stores).  If you’d prefer to use your own custom database (or the file-system or your own LDAP system), you can also build your own role manager provider and easily add it to the system.  This web-site details how the ASP.NET 2.0 Provider Model works, how you can build your own providers to plug-in, and enables you to download the source code for the built-in providers that ASP.NET ships with.

For the purposes of this sample we are going to use either SQL Express or SQL Server to store our role mappings.  To begin with we’ll want to enable the ASP.NET role-manager in our web.config file.  We’ll do this by adding this section within the <system.web> group:

  <roleManager enabled="true"/>


If you have SQL Express installed on your machine, then you are done.  ASP.NET will automatically provision a new SQL Express database within your app_data folder at runtime that has the appropriate Roles tables configured to persist the role mappings we’ll do.  You don’t need to take any additional steps to configure this.

If you don’t have SQL Express installed, and instead want to use a SQL Server to store the Roles data, you’ll need to create a database within SQL to store the ASP.NET Application Service tables, and update your web.config file to point at the database.  The good news is that this is easy to-do.  If you haven’t done this before, please read my previous Configuring ASP.NET 2.0 Application Services to use SQL Recipe that demonstrates how to do this.  Below is a sample configuration entry that shows how to configure the web.config file to use a SQL database:

  <roleManager enabled="true" defaultProvider="SqlRoleManager">




      <add name="SqlRoleManager"



           applicationName="MyApplication" />




Where the “SqlRoleManagerConnection” connection-string we referenced above is defined within the <connectionStrings> section of our web.config file like so:


    <add name="SqlRoleManagerConnection"

         connectionString="Data Source=localhost;Initial Catalog=BasePageSample;Integrated Security=SSPI;">



Important: If you explicitly declare a new provider reference like I did within the <providers> section above, you need to make sure that you specify the “applicationName” attribute.  Otherwise you’ll run into this problem that I discuss in this blog post here.

And now, when we run our application again, ASP.NET will automatically authenticate the incoming users to the site using Windows Authentication, and use the SQL Database we defined above to retrieve all role mappings.

Step 4: Create Our Roles and Initial User Mappings to Them

Now that we have configured our role provider, we can use the “Roles” API (the System.Web.Security.Roles class) within ASP.NET to create roles, and manage users within them. 

One tip/trick I like to use is to take advantage of the “Application_Start” event handler within Global.asax to setup my Roles if they don’t already exist, and map any initial users into them if necessary.   To-do this, choose File->Add New Item and select the “Global.asax” file item.  Then add this code to your Application_Start event handler:

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)


        If (Roles.RoleExists("Auditors") = False) Then


        End If


        If (Roles.RoleExists("Approvers") = False) Then


        End If


        If (Roles.RoleExists("Admins") = False) Then


            Roles.AddUserToRole("REDMOND\scottgu", "Admins")

        End If


    End Sub

This event handler gets called once every-time the ASP.NET application starts up.  As you can see above, within it I am checking to see whether our three roles exist in the configured Roles database – and if not I create them.  I also then added my Windows account into the “Admins” role that I created last.  This is a good approach you can use to setup the initial admin users for your application.

Now, when we run the application again, it will automatically provision the three new roles into the database and add me into the admin one (note: in the code above I’ve only added myself to the admin role – I’m not yet in the Auditors or Approvers role).

Step 5: Authorizing Access Based On Roles

To verify that our roles were setup correctly, let’s add some authorization rules to grant/deny access to portions of the site based on them.  I can do this using the “authorization” section of our web.config files.

First create new “web.config” files within both the “admin” and “audit” directories:

Then add the below XML content within the web.config file within the “admin” directory:

   <?xml version="1.0"?>







      <allow roles="Admins"/>

      <deny users="*"/>






This tells ASP.NET to allow users within the “Admins” role to access the pages within the directory, but to block everyone else who isn’t in this role.  We’ll then want to add similar content to the web.config file within the “audit” directory (which does the same logical thing as above – except in this case only allowing “Auditors” access).

For the “Approver.aspx” page that is in the root directory of the application we’ll want to-do something a little extra.  Because it isn’t in a sub-directory of its own, we can’t use a global directory rule like we did above.  Instead, we’ll want to restrict the rule to only apply to the “Approver.aspx” page within that directory.  We can do this by specifying a <location path=”url”> directive around it within our root web.config file on the site:

    <location path="Approver.aspx">





          <allow roles="Approvers"/>

          <deny users="*"/>






And now when we run the application again, and try to access the “Audit Page” or “Approver Page” within the site we’ll get this error message:

This happens because when we created the roles in our Application_Start event handler we didn’t add ourselves into the “Approvers” or “Auditors” role – and so ASP.NET is denying us access to those resources (like it should).  When we click the “Admin” link we are able to access it, though, because we belong to the “Admins” role.

Step 6: Implementing Security Trimming for our Menu

One issue you will have noticed when we ran the sample above is that the menu on the left-hand side of the screen is still displaying all of the links when we visit the site – including links to the “Audit” and “Approver” pages that we don’t currently have access to (because we aren’t in those roles). 

Ideally we want to hide those links and only display them to users within the appropriate roles to access them.  This avoids users inadvertently seeing our “Access Denied” error message above.  The good news is that this is easy to implement using a cool ASP.NET 2.0 feature called “Security Trimming”.

To implement security trimming, add this XML section to your web.config file:

      <siteMap defaultProvider="XmlSiteMapProvider" enabled="true">


          <add name="XmlSiteMapProvider"

            description="Default SiteMap provider."

            type="System.Web.XmlSiteMapProvider "


            securityTrimmingEnabled="true" />



This tells ASP.NET to enable security-trimming at the Site Navigation provider.  Once we do this, we can then open up our web.sitemap configuration file again and update the individual nodes within it with “roles” attributes indicating what nodes are visible to incoming users:

<?xml version="1.0" encoding="utf-8" ?>


<siteMap xmlns="" >


    <siteMapNode url="default.aspx" title="Home">

        <siteMapNode url="approver.aspx" title="Approver Expenses" roles="Approvers" />

        <siteMapNode url="audit/auditor.aspx" title="Audit Page" roles="Auditors" />

        <siteMapNode url="admin/admin.aspx" title="Admin Manager" roles="Admins" />




And now when I run the application again, you’ll notice that the “Audit” and “Approver” links are hidden from the TreeView (since I’m still not in those roles):

The “administrator” link is still available, however, because I am a member of the “Admins” role.

Step 6: Building our Admin Page to Manage Role Permissions

The final step we’ll want to take is to build a simple role-manager page that Administrators within our expense reporting application can use to Add/Remove users from roles.  We’ll do this within the “admin.aspx” page underneath our “Admin” sub-directory (which only users within the “Admins” role can access).  Within this page we’ll add a TextBox that administrators can use to lookup a username, and then use a checkbox list to enable administrators to easily add/remove the selected user from roles.  When the admin clicks the “Update” button these changes will be persisted in the role database:

Here is what the .aspx markup for the admin.aspx page looks like to implement this:

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">


    <h3>Role Manager</h3>



        Enter UserName:

        <asp:TextBox ID="TxtUserName" runat="server"></asp:TextBox>

        <asp:Button ID="LookupBtn" runat="server" Text="Search" /> 



    <div class="roleList">

        <asp:CheckBoxList ID="RoleList" runat="server">





        <asp:button ID="UpdateBtn" text="Update" Visible="false" runat="server" />




Notice that I’m using an <asp:checkboxlist> control to provided a list of checkboxes for the roles.  Here is then the complete code-behind file for the page:

Partial Class Admin_Admin

    Inherits System.Web.UI.Page


    Sub PopulateRoleList(ByVal userName As String)




        Dim roleNames() As String

        Dim roleName As String


        roleNames = Roles.GetAllRoles()


        For Each roleName In roleNames


            Dim roleListItem As New ListItem

            roleListItem.Text = roleName

            roleListItem.Selected = Roles.IsUserInRole(userName, roleName)






    End Sub


    Sub UpdateRolesFromList()


        Dim roleListItem As ListItem


        For Each roleListItem In RoleList.Items


            Dim roleName As String = roleListItem.Value

            Dim userName As String = TxtUserName.Text

            Dim enableRole As Boolean = roleListItem.Selected


            If (enableRole = True) And (Roles.IsUserInRole(userName, roleName) = False) Then

                Roles.AddUserToRole(userName, roleName)

            ElseIf (enableRole = False) And (Roles.IsUserInRole(userName, roleName) = True) Then

                Roles.RemoveUserFromRole(userName, roleName)

            End If




    End Sub


    Sub LookupBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LookupBtn.Click


        UpdateBtn.Visible = True

    End Sub


    Sub UpdateBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles UpdateBtn.Click



    End Sub


End Class

Note that the above code does not hardcode the list of roles used within the application.  Instead it dynamically populates the <asp:checklistbox> with the list of all roles currently in the Role Manager.  This means that if we add a new role to our application it will automatically show up in the list (it also means you should be able to copy/paste this page into any application with Roles enabled and take advantage of it).

Note also that if you lookup your own account, and enable either the “auditors” or “approvers” role for it, the Treeview menu for the site will now show those nodes that were previously unavailable to us:

Programmatically Looking Up Roles

In the example above, I’ve been using the <authorization> section within web.config files to control access to page resources based on the role of the user.  This works well for page-level authorization, however sometimes you want to grant everyone access to a page and then perform role-level checks within the page to control capabilities (for example: showing/hiding the edit and delete buttons within GridViews, etc). 

Developers can use the User.IsInRole(rolename) method to easily check to see whether an authenticated user is within a specific role within a page.  For example:

        If User.IsInRole("Administrators") Then

            ' Do something only admins are allowed to-do

        End If

This works great from within ASP.NET pages.  Non-page derived classes don’t have a built-in “User” object.  However, you can always access an ASP.NET Request’s User identity context using code like this:

        Dim User As System.Security.Principal.IPrincipal

        User = System.Web.HttpContext.Current.User


        If User.IsInRole("Administrators") Then

            ' Do something only admins are allowed to-do

        End If

The above code will work in any class within your application (or in a referenced class library assembly).


The above walkthrough demonstrates how you can build an application from scratch using ASP.NET 2.0’s built-in Windows Authentication, Role Management, Master Pages, Site Navigation, Url Authorization, and Security Trimming features.  The combination of these features makes building secure Intranet applications easy.  You can download a completed version of the above sample here.

For additional reading I’d recommend checking out these links:

My ASP.NET 2.0 Security Resource Link Page: This is a good page to bookmark for ASP.NET 2.0 Security information, and contains ton of tutorials and links on ASP.NET security topics.

ASP.NET 2.0 “How Do I” Videos: This is a great series of short 10-15 minute videos that you can watch online to learn ASP.NET concepts.  Included are several videos that show off Security, Roles, Master Pages and Site Navigation.

How To: Use Role Provider in ASP.NET 2.0: This is a great article on MSDN that walks-through how to use the ASP.NET 2.0 Role Features.

Role Providers in ASP.NET: This page from K. Scott Allen contains some additional information on the ASP.NET Role Providers.

ASP.NET Site Navigation Tutorials: This is a great set of tutorials by Scott Mitchell that discuss the ASP.NET Site Navigation features.

Hope this helps,




  • Great article! That's something I was looking for. Thanks Scott!

  • Great article - thanks! Just what I needed to kick-start my development in the right direction as I am a seasoned ASP.NET developer, but starting my first 2.0 project.


  • Scott, you're like a machine - constantly pumping out these great examples. Examples are 100X better than the documentation any day.

    One quick question:

    Membership / Roles is great. But what if I want something more fine-grained. For example: I want user "Bob" (who has role "Customer Support Agent") on my system to only access customer accounts "A1", "B7" and "D9".

    Do I just have to roll-my-own for this level of permission-based security?

  • One other quick (related) question I couldn't find the answer to anywhere...

    When using Forms Auth, is there anyway to set the Authentication Cookie to be IP-specific?

    This would be a great way for us to avoid the issue of cookies stolen from the DB and used on a remote computer (something we've experienced).

  • This is great! I think making an app capable of handling both Intranet (Windows) and Internet (SQL) is a great approach that often make sense for Enterprise type apps. I can see at least 10 apps in my organization can take advantage of this (currently those are mostly using only SQL and intranet users has to remember another login id).

    How about auditing though? How can I determine "LastLogin" status for Windows users and other type of auditing information (which are mostly available when you are using traditional SQL based authentication?)

  • Hi Boris,

    For more finer grained capabilities like that (where you want to restrict access to just specific accounts), you'll probably want to add your own role-specific checking there.

    One approach would be to associate a valid roles list with your customer account row, and then check to make sure that "Bob" is in one of those before proceeding on the operation.

    Hope this helps,


  • Hi Boris,

    Forms Authentication doesn't provide the ability to issue IP specific cookies directly. The main problem with this is that Proxy Server and DHCP can cause IP addresses to change frequently, which can cause issues with valid customers becoming locked out.

    You could, though, add your own IP address checking logic if you wanted to though. Another approach would be to use the private data section of the ASP.NET Forms Authentication ticket and encrypt the user's IP address within it. You could then verify it within the Application_Authenticate request in Global.asax to ensure that it is valid.

    Hope this helps,


  • Hi Impu,

    ASP.NET 2.0 provides a pretty rich health-monitoring API that I believe has auditing support for Windows Authentication (I know it goes for Forms Auth, and I believe also for Windows Auth). You could either use this, or implement your own custom audit log to track user activity (last access time, etc).

    Note that ASP.NET also automatically logs anytime an authorization failure occurs to the Windows Event Log. So if you ever want to see who has been trying to access secure pages they don't have access to, you can always check there as well.

    Hope this helps,


  • Oh, and it's nice to see you've changed browser again for your examples. What's left? Opera, Safari... Lynx? ;)

  • Hi Logic,

    There are a few good whitepapers out there on the starter kits to read about extending and using them. Here is one on the Personal Starter Kit for example:

    If you scroll down on the "Downloads" tab on, you can also find a pointer to ASP.NET V1.1 downloads. Included there are the origional IBuySpy and other starter kits for ASP.NET V1.0 (note: these also still work on ASP.NET 2.0).

    Hope this helps,


  • I agree with a previous poster that you are definatley posting some quality stuff lately...well beyond the average bloggers protocol.

    Great example to btw, loving ASP.NET 2.0!!

  • Scott/Daniel/Boris,

    The brick wall that i've run up against while trying to implement my own Role Provider is that the IsUserInRole method doesn't allow for a "qualifier".

    Say the User tries to access a page of information about platoon Y. There doesn't seem to be a method in the interface that would allow you to check for that Role on that "object".

    While it's trivial to add a method like this to your own Role Provider and call it within your code, you can't use the User.IsUserInRole method and you lose out on alot of the "built in" functionality like the Security Trimming that Scott talks about above.

    Any tips on how to work around this limitation?

  • Hi Daniel,

    There are probably two ways you could handle this sub-permission notation:

    1) Expose a method on your Roles Provider and then access it via the Roles.Provider property.


    2) Look to implement a hierarchical naming scheme with the role-name. For example, you could support:

    User.IsInRole("Soldier") and User.IsInRole("Soldier.PlatoonA")

    or some other notation. Since the Roles system just passes the string to the provider, you could parse and handle this however you want within your Role provider implementation.

    This should also then work within sections in web.config as well as for security trimming.

    Hope this helps,


  • Thank for information for role based security, but I have problem with Internet role based security. How I can to implement more difficult security for my site.

  • Scott, are you saying that you can not use the ASP.NET Web Site Administration Tool if you use Windows Authentication? Can I still use it to manage the web.config files?

  • Great article, thanks.

    is it true that adding this statement can have a negative impact on performance? I've read that by default, the ASP.NET 2.0 role manager doesn’t cache roles data. This means if roles are stored in a database, that’s one more database access per request that you can easily do without.

    For example, pages that use site maps with security trimming enabled, and pages to which access is restricted using role-based URL directives in web.config, causes the role manager to query the roles data store.

    Is it better to configure the role manager to cache roles data in cookies for performance reasons? Like

  • Hi Rainer,

    By default the Role Provider will query on demand for the Roles list -- so if you don't use Roles in a request there won't be any perf impact (once queried the role list is also re-used throughout the rest of the request, so only one query per request).

    The Role System also optionally allows you to use an encrypted HTTP cookie to cache the role-list across requests on the client. If you want to minimize the database hit to lookup roles, you can enable the cookie caching support and set the cache window you want within it (for example: you could set it for a small window like 1 minute or a large one for hours).

    Hope this helps,


  • Hi MtSonic,

    You can use the web administration web tool with Windows Authentication (I know you can use it for more scenarios -- I'm not sure if there are some authentication specific things that don't work).

    One reason I built the custom roles tool I did above, though, is that for application-specific roles you often want to be able to delegate to end-user admins (meaning non-IT Professionals) the ability to easily control who or who does not have capabilities within an application.

    So for example someone in the accounting department would be able to use the expense tool above to grant a manager permission to approve expenses -- using the system admin UI I built instead of using a more generic (and feature-rich) admin tool geared more towards system admins.

    Hope this helps,


  • Just another thing to consider regarding AzMan (Authorization Manager) when looking for finer-grained/complex control. (no intention to detract from Scott's great post)

    Even though you can use AzMan roles in ASP.NET through the AuthorizationStoreRoleProvider, the primary advantage of using its AuthCheck() to determine a user's capabilities against 'Operations', rather than 'Roles', is the flexibility gained by avoiding roles in your code altogether.

    For example, a "Delete Items" capability cannot be removed from a "Item Manager" role without a code change if your code allows it based on checking for the "Item Manager" role.

    In contrast, if your code checks for "Delete Items", that 'Operation' can be added to, or removed from, the "Item Manager" role (via AzMan MMC add-in or code) and not require recompiling or redeploying any of your code.

    Also, "Item Manager" can be renamed, subsumed by another role, or replaced entirely, depending on shifting business requirements. "Delete Items" still stands as a capability you can grant or deny as appropriate.

    (a whiteboard would be great about now, ha)

    There are other features (AD or ADAM integration, BizRules, exclusions, app scopes, 'Task's, aggregation, nesting, delegation, etc.) that make it flexible and robust. But again, these live outside your application which continues to simply check 'Operation's. It's quite clever and supports *a lot* of authorization complexity.

    Having implemented it at work, I have to admit that it comes with a few headaches today. I'm anticipating the SQL based store, managed API, and things like CardSpaces in Vista/Longhorn.

    But, if you reach the point where it feels like you are shoehorning your requirements into roles alone (like it did for me), then it's worth looking at.

  • Scott, found a minor quirk in this. If the roles exists it will not add the defaults to it.

    If (Roles.RoleExists("Admins") = False) Then
    Roles.AddUserToRole("REDMOND\scottgu", "Admins")
    End If

    should be

    If (Roles.RoleExists("Admins") = False) Then
    End If

    If (Roles.IsUserInRole("REDMOND\scottgu", "Admins") = False) Then
    Roles.AddUserToRole("REDMOND\scottgu", "Admins")
    End If

    I think is't worth noting that is also automatically adds a user record when AddUserToRole.

    Thanks for all the great posts!

  • Hi Scott,

    Great article, this situation is exactly what i have in my scenario (multiple identity stores, database/AD/custom, with one app role store). Do you have any tips or best practice guidance on managing this scenario from the 'keeping users in sync' perspective?

    Basically, we have internal users on AD and external ones with their credentials stored in the standard SqlMembership provider db, with both getting their roles stored in a custom application database (with a slightly customised SqlRoleProvider for access). I think that all I need to do is make sure user names and guids are kept in sync across the stores and all will be well, but I have not done this approach before and would value any comments!

  • Hi Scott,

    Totally agree, i definitely want to use different providers so that i keep the membership stores separate, but won't the role store need to have at least the keys of the users (guid and prob user name for convenience) so that i can setup the relationship between the users and the roles (i.e. populate the UsersInRoles table or equivalent)?

    (re-reading my above post it was a bit ambiguous, i actually only meant syncing the data between the membership and role stores, not between the membership ones)


  • I've just started in the world of and these articles are fantastic.(Much easier to learn and understand from examples). just a quick question.
    How does the role provider model deal with app pool recycling? We currently are using this model, with authentication cookies and have an issue where if an app pool recycle occurs, we lose the user role, but they are still logged in. Is this an issue with the IIS settings or with the web app?


  • Hi Peter,

    The role-manager is stateless, so you shouldn't have any isses with app-pool recycling. Are you sure you are loosing the role identity?



  • Hi Scott,
    Fantastic article.
    Can you please shed some light or point me to an article on how do we cater for situations when the roles are dynamic and not known (cannot be hard coded). So you define your application services/objects and map them to relevant roles. The application comes with a standard admin and some other default roles. Admin can define new roles and map the existing services/objects to this new role.
    Your help will be greatly appreciated.

  • Does anyone know how to set the AuthenticateEventArgs (i.e. e.Authenticated) without using an asp:login control?

  • Hi Dan,

    The role provider actually just maps the names of the users as strings -- which means that you don't need to maintain key names for them. This would allow you to have multiple membership providers, but a single role provider, and have the role provider unify the names.

    Hope this helps,


  • Hi Josh,

    You don't need to use the Login controls in order to authenticate a user. Instead, just use the Membership.ValidateUser() method to check whether their username/password is correct, and then use the FormsAuthentication class to issue the http cookie to authenticate them.

    This article shows how to use both:

    Hope this helps,


  • Hi Shahzad,

    In cases like those, you would probably want to maintain another mapping datable that you could then use to lookup (and then probably cache) the role-names that apply to your specific objects, pages or operations.

    You could then use the same approach I did above to retrieve each authenticated user's role status and perform standard role-checks against them (like the sample above).

    Hope this helps,


  • Sorry to take so long to get back to you, we have been very busy at the moment. You are correct, we do not have a loss of authentication when the app pool recycle occurs as I previously thought. I've had another look at our model, basically we are declaring 2 public properties in the (public override bool ValidateUser) method. However, these values are lost when an app pool recycle occurs. How can we preserve the values of these? Is there anyway to add these value to the cookie that the role provider uses?

  • @Scott - I am building a roles based intranet app that will use both the SqlRoleProvider and Groups within Active Directory for authentication. Would you be able to discuss the difference between setting up the WindowsTokenRoleProvider to return that Group information versus using WindowsPrincipal?

  • Hi Shane,

    I believe you can use either approach to perform a role based lookup (specifically: calling User.IsInRole()).

    If you use the WindowsTokenRoleProvider, you'll also then be able to retrieve the full list of groups that the authenticated windows user is in. With the WindowsPrincipal you can't get this full list directly -- instead you can only check to see whether they are in a specific role.

    Hope this helps,


  • Hi Peter,

    You could persist these values in one of two ways:

    1) You could create your own custom cookie and encrypt the values there (.NET includes standard crypto APIs for doing this).

    2) You could add the values to the FormsAuthentication cookie (which has a property for storing custom data on it) if you are using Forms Auth.

    Hope this helps,


  • @Scott - Thanks. Yes, that is very helpful.

  • I am a fairly recent convert to ASP.NET and in my v1.1 tests I added a WindowsAuthentication_OnAuthenticate event handler in Global.asax to create identities with the roles I wanted based on the Windows authenticated user. This seemed to work fine in my tests and even now with .NET v2 is simpler than a custom provider where I want a lightweight role mapping solution without using SQL Server Express. So I was just wondering if there's anything wrong with using this approach in .NET v2?

    Thanks, Simon

  • Scott,

    Once again you have saved me. I followed these instructions and it worked like a charm.
    I appreciate all the efforts in communicating implementations like this.

    I'd like to show this to our whidbey user group here in San Antonio. Would that be alright with you?


  • Hi Roni,

    Definitely feel free to re-use any of my posts anyway you want.

    Hope this helps,


  • Hi Simon,

    You can definitely continue to use the Application_OnAuthenticate event if you want to setup the roles for your request. This is definitely still supported in V2.

    Hope this helps,


  • Hi Scott,
    Great article, I've managed to get this working with one of my applications. One question I have, is it possible to redirect to a custom access denied page when a user without access tries to access the system? Currently I get a 'Enter Network Password' box and then the Access Denied page as above if I hit cancel.

  • Hi Duncan,

    You might have enabled basic or digest authentication within IIS for your web-site. If you limit it to just support NTLM, then I believe you will be have an access denied page if you don't have access. Can you check that?



    P.S. The access denied error message can also then be customized using a custom error page.

  • Scott,

    thanx a lot for this nice tutorial. I've implemented some windows authentication in our intranet solution. However, role information in some way is not persisted. I add users to the admin role using:

    System.Security.Principal.IPrincipal customUser = System.Web.HttpContext.Current.User;
    if (!System.Web.Security.Roles.IsUserInRole(customuser, "Admin")) System.Web.Security.Roles.AddUserToRole(InfaUserName, "Admin");

    Now this works fine while being in the current session. When that specific user logs in, he or she has admin rights. However, when they restart the application (or the browser for that matter) they're not a member of the admin role anymore...

    These are the web.config settings used:

    this kinda leaves me puzzled. Do you have a clue?

  • Hi Nathan,

    That is pretty odd. Can you double check the Roles table within your database to make sure they are getting added?

    Also -- do you first check to see whether the user is in the role before adding them? I believe it will throw an error if you are adding a user into a role they are already in. It might be that your logic is causing an error, and that is why it doesn't work.



  • Hi Scott,

    yes, in my database there are 2 roles, the "Admin" and "User" role...

    I use the following code to add a user to the database:

    System.Security.Principal.IPrincipal customUser = System.Web.HttpContext.Current.User;

    if (!System.Web.Security.Roles.IsUserInRole(customuser, "Admin")) System.Web.Security.Roles.AddUserToRole(customuser, "Admin");

    for the Administrators... And this actually works, the user gets added to the role database.

    In some way, whenever the user "restarts" his or her session they get thrown back to being a regular user...

  • Hi Nathan,

    Can you double check that you have this line in your web.config file:

    I'm wondering if you might not have anonymous authentication disabled - in which case maybe the user is connecting without being logged in which might explain the behavior you are seeing.

    Hope this helps,


  • Hi Scott,

    I've double checked the setting in the web.config and if DO have the lines you mentioned.

    This whole thing really is leaving me clueless. If you want we could transfer this conversation to email. That way I could send you some files...


  • This is really really a great article on Role based authenticaion and authorization in .NET. Before reading this article, I was totally in dilemma that how this actually works, but now I have got a clear idea about it.
    Thanks Scott.

  • Hi Scott, sorry for the delay in following this up. I have 'Integrated Windows Authentication' selected. Do I need to lose this and enable anonymous access? When I try this even users in the relevant role get the access denied page. I'm using Windows 2003.

  • Hi Duncan,

    You should make sure that you have integrated windows authentication selected - since that is what provides the windows authentication feature.

    Hope this helps,


  • Hi Scott,
    Integrated Windows Authentication is what I have selected and when users who dont have access visit the page they're challenged with a Enter Network Password box.

  • Hi Duncan,

    Any chance you could send me email describing the scenario more? I can then loop you in with a few folks who can help.



  • How do you use SQL Membership with Windows Authentication, like you've explained here, but display the member's usernames without the domain attached.
    Let's say I need to display a dropdown with all the users registered with the website, but I want to show it without the domain included in the username.
    E.g. Display 'Scott Guthrie' or at least 'scottgu', instead of 'REDMOND\scottgu'?

    Loved the article, thanks.

  • Hi Daniel,

    To just display the username (no domain), you'd probably want to use the String.Split method. You could split the string into two based on the "\" character and then just display the second part.

    Hope this helps,


  • Is there any simple way of listing the Users in a domain?

  • Hi Gerald,

    If you are using the ActiveDirectoryMembershipProvider I believe you can list all users for the domain.

    Hope this helps,


  • Scott, I noticed that when AddUserToRole is called it will add the user to the User table if the user doesn't exist yet in that table. However, the user is not added to the Membership table. Is this because the NT user is already considered a member via the fact that it's already NT Authentication? What exactly is the purpose of the Users table AND the Membership table? Is the Membership table more for the Forms Authentication model? Thanks in advance.

  • Hi Derek,

    The Membership table is intended for Forms Authentication scenarios, so you won't use it with Windows Authentication.

    Hope this helps,


  • What an article, it saved me a lot of time.
    Scott mate, you are doing great, I enjoy all of your articles and technology updates.


  • Hi Jack,

    For more SQL table control with the Roles API, you might want to take a look at these Simple Providers I just blogged about:

    That can provide you with a little more flexibility over the exact database schema structure used.

    Hope this helps,


  • Hi Scott!

    Great example! Do you have a hint for me: I want to use the Profiles for my Users.

    Is it required to implement AspNetActiveDirectoryMembershipProvider in Web.Config?


  • Hi Undying,

    You don't need to use any specific Membership provider for Profiles. You can use Windows Authentication directly, or use Forms Authentication with this.

    This blog post demonstrates how to use Profile personalization with standard Windows Authentication:

    Hope this helps,


  • Hi Scott
    I am really impressed with your article - as it has been what I have been trying to write (without success) for some time.
    On initally running the exact code, the update button appeared to check the users and their roles rather than update the changes. ie 2 roles (admin &amp; delegate) Bob is in admin only. select delegate also and when updated Bob is still in admin only
    So I changed the code to:
    Protected Sub RoleList_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    &nbsp; &nbsp; &nbsp; &nbsp;Dim roleListItem As ListItem
    &nbsp; &nbsp; &nbsp; &nbsp;For Each roleListItem In RoleList.Items
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Dim roleName As String = roleListItem.Value
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Dim userName As String = TxtUserName.Text
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Dim enableRole As Boolean = roleListItem.Selected
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;If (enableRole = True) And (Roles.IsUserInRole(userName, roleName) = False) Then
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Roles.AddUserToRole(userName, roleName)
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ElseIf (enableRole = False) And (Roles.IsUserInRole(userName, roleName) = True) Then
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Roles.RemoveUserFromRole(userName, roleName)
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;End If
    &nbsp; &nbsp; &nbsp; &nbsp;Next
    &nbsp; &nbsp;End Sub
    But the problem with this is that it either adds or removes from both roles the same ie click on admin and both admin and delegates get added.
    Where have I gone wrong?
    (I have also modified the so instead of searching for the username the list is in a dropdown box - but that shouldnt change anything should it?)
    Irish Kiwi

  • hi Scott

    What if don't want to save the username and password in the database (that seems to happen here) but only the login-id?

    I assume that Windows login and Active Directory Logins have unique login Ids?

    If so, how can I access that?
    (I think Microsoft CRM does that as well)

  • Hi Pascal,

    In my solution above I'm actually not storing the password in the database. The username/password are instead stored and maintained by ActiveDirectory/Windows -- and only the username and role permissions are being stored in the database. So I think this gives you what you are after.



  • Hey Scott,

    Great blog posting. One question that I haven't seen answered anywhere (maybe I missed it), but how do you integrate role-based security with searches?

    It's pretty common that most apps have search capabilities and similar to the site map, you don't want users seeing results outside of their "scope". Are there any best practices on doing that sort of thing? I've done/seen plenty of custom approaches to this, but I'm looking for something that would fit more cleanly into role-based security.

  • Scott,

    I have set up my intranet site to use active directory to authenticate as you described. It works perfectly. The question/problem I have is that I do not want to let everyone in my company into the site, only people in a certain department. I do not know that much about active directory, but I was told that everyone in our active directory is assigned to a department. Is there a way to accomplish this with or such, where the role is gotten from the AD?

  • I am able to enter the Role Manager in the sample app and add, change, and remove roles.

    I am also able to launch the Web Site Administration Tool and see all the roles.

    However, I am not able to then Manage those roles and find users. That is, if I select Create or Manage roles and then Manage and attempt to search for users using a variety of methods, including; selecting All, searching using just the *, entering the username or DOMAIN/username...I come up with empty results every time...No users found for this search. I know I am there for the Admin role, as I can see it in the UserInRoles table, and as mentioned, when I use the sample app to view.

    Any assistance would be greatly appreciated!


  • Hi Mark,

    That is a good question. In theory you should be able to modify your search query based on the role of the person doing the search.

    Hope this helps,


  • Hi Mike,

    If you configure the role manager to use the ActiveDirectoryRoleProvider, then you can definitely grant/deny users based on the roles/groups within your active directory.

    Just use the syntax "DOMAIN\Group" in order to grant/deny people.

    Hope this helps,


  • Hi Ed,

    Have you configured a custom provider in your web.config file? If so, make sure that you have added a directive before the one. Otherwise, you'll be picking up both the provider declarations.

    Hope this helps,


  • Scott, I appreciate your response. You say to configure the role manager to use the ActiveDirectoryRoleProvider. VS is telling me it doesn't exist in 2.0. This is what I put in my web.config:

    I googled ActiveDirectoryRoleProvider and practically nothing came up. What am I doing wrong?

  • Hello,
    Would it be possible for you to post the code snippets you included in the tutorial in C#?


  • Hello,
    When I try to implement the security trimming for the navigation tree, I get the error:

    "Unrecognized configuration section siteMap"

    I inserted the code from the example above verbatim.

    is anyone else seeing this problem, and possibly know how to fix it?

  • Hi Daniel,

    Did you add the section underneath the section in your web.config file? That might be the cause of the problem.



  • Hi scott, your great article is very helpful, but I find it doesn't work very well. When I download your code and replace the required items, I can't see the link "admin manager". Can you help me?

    Frank Yang

  • Hi Scott,

    This is a great article - so much easier to follow that a lot of what's out there. I seem to be running into one problem and I'm hoping you can help. I've followed your instructions to enable Windows authentication and set up a RoleManager with the appropriate connectionstring. I used the aspnet_reqsql utility to create the appropriate tables (aspnet_Users, aspnet_Roles, etc) in an existing SQL database where the rest of the app's data resides. The RoleManager connectionstring points to this database, as does the connectionStrings section in web.config. When I use your code in my Global.asax file, the roles I specify get created in aspnet_Roles in the proper database. However, aspnet_Users is empty, and aspnet_UsersinRoles stays empty despite my attempt to use Roles.AddUserToRole to add users to roles. It seems that I need to populate aspnet_Users ahead of time - is this right, or am I doing something else wrong? If you need me to email you files, let me know.
    Thanks much!

  • Hi Melanie,

    If you are using Windows Authentication, then you shouldn't need to populate the aspnet_Users table - this is only used if forms authentication is used.

    Can you make sure you aren't running into this problem:

    If that doesn't fix it, feel free to email me your web.config file (



  • Hi,

    Great article!

    The issue I've come across is that my sitemap needs to be a bit dynamic. For example, my website is accessed from another one and passes in via the query string a value which I use to determine the content on the site. For example, the variable in the query string might be ID and the values are A, B or C. I store the value in a session variable.

    I have roles:

    For A:
    AdminA, UserA, ReadOnlyA

    For B:
    AdminB, UserB, ReadOnlyB

    For C:
    AdminC, UserC, ReadOnlyC

    So if Joe is in UserC and accesses the page when Session["ID"] is B, Joe doesn't have access to the page.

    So to authorise the user to particular sections of a page, I use the code:

    Session["ID"] = Response.QueryString["ID"]

    If (User.IsInRole("Admin" + Session["ID"].ToString())
    { //allow acess }

    However, I can't do this with the sitemap because a sitemap requires you to enter the authorised roles at the beginning, but I don't know which "role" to allow access to until runtime, when the session variable is set.

    Does this make sense?

    So do I need to dynamically generate my sitemap at runtime? If so, how would I go about doing this?

  • Hi Scott,

    I love all your articles, I've used a lot of them. Thanks!

    I'm using the authentication method you outlined in this article, everything is working great. My question is, is there a way to keep track of which users are online? Something like the Membership.GetNumberOfUsersOnline() method. I would like to get a list of their identities as well. Is there any way to do that? I've done a lot of searching but no luck so far.

    Thanks, Walt

  • Hi Walt,

    Unfortunately with Windows Authentication there isn't a super easy way to keep track of which users are currently online. You could try issues an HTTPCookie and then update a timestamp in a database periodically which might be one approach.

    Hope this helps,


  • Hi Scott
    I have the same problem as Duncan - getting a Enter Network Password box prior to an Access Denied page. Have you found any resolution?

  • Hi Steve,

    Unfortunately I don't think there is a way around this absent of turning off basic/digest authentication to supress the pop-up.



  • That is the problem! Basic & digest authentication are off, but I am still getting the pop-up.

  • Hi Alistair,

    The error you are receiving is because the worker process account that your web server is running under doesn't have security privledges to connect to the remote SQL server.

    This article covers how to connect to a SQL server from ASP.NET using Windows auth in the connection string:

    This article covers how to connect to a SQL server from ASP.NET using SQL auth in the connection string:

    Hope this helps,


  • Hi Scott, further to my recent query I notice that I definitately require the end user to be granted db access permissions, and the very minimum database permissions granted to the IIS application pool account (that my app runs in), required to make it all 'work', appear to be that the pool account needs to be added to the following db roles


    Is there anyway of avoiding this ? ie - If an impersonated user has been granted dbo access to the application db within sql server, is there any way to allow only the impersonated user to access these tables and associated sp's etc to enable the .Net authorization functionality WITHOUT having to set the application pool identity to have any permissions in the application db whatsoever ?

  • Hi Alistair,

    I believe you'll need to grant the process access - although if you send me email I can loop you in with someone who will know for sure.



  • One of the best articles I have seen on the web.

  • Hi Scott,
    This is a great article I am lookig for.
    but I have one question can we do the role managment with orcale.

  • Hi Rockey,

    You could create a new Role Provider that works against an Oracle database to accomplish this.

    This blog post points to a simple SQL provider that you could use to port to SQL:

    Hope this helps,


  • For anyone thats interested, it turns out that the application pool account definately does need to be added to the following roles




    - when using windows authentication, when a nominated user is to act as an administrator (for adding/deleting users and setting roles for the application)

    My thanks to your article, and the expert help you put me in touch with :)


  • This is absolutely fantastic. It shows us how to upgrade and standardize the Classic ASP techniques we've developed over the years. It also vindicates those of us who have faced non-supportive IT departments that don't understand why this dual-natured AD authentication/Web App authorization technique is necessary.

  • Hi

    Can't find any reference about limiting access to a resource based on a group name with a space in it e.g.

    Works fine with non-spaced group names.


  • Hi Scott, I am having the same authenication issues. In the Admin folder I set

    and in the root folder I set the custom error for 401 to redirect to a custom error page. But the default access denied page is displayed. I am also using forms authentication to manage the users. Is this the source of the problem. I tried commenting out the membership part in the webconfig and still the same problem.

  • Hi Martin,

    That is a good question - can you send me an email with it, and I'll loop you in with someone on my team who probably knows the answer.



  • Hi Scott,

    Man i just wanted to say thanks for all your hard work and dedication put on to this site. your work helps us mortals carry on with our developer lives... thank u sooooo much man ur like my hero!! gosh im so happy hahaha

    Thanks again!

Comments have been disabled for this content.