Customizing Membership Role Providers and Login Control in C# .NET

modified

Introduction

Sooner or later many web sites realize they need to allow users to login to their C# ASP .NET web applications. This can be as a result of web application functionality, feature restrictions, or simply to gauge web site traffic better by keeping track of users in the .NET application. The good news is that adding login functionality to your C# ASP .NET web application is easier than ever. By taking advantage of the built-in ASP .NET login control, membership providers, and role providers, you can quickly take care of authenticating, authorizing, and logging users in to your .NET web application.

In this article, we’ll step through the process of using the built-in ASP .NET login control and wiring it up to a custom membership provider and a custom role provider class. We’ll walk through configuring the C# ASP .NET web.config file to specify roles and access within the web application. To add some spice, we’ll also include a way to automatically sign users into your web application, who have previously logged in, by implementing an auto-sign-in feature. Ready to get started?

The Lonely Login Control

It previous versions of C# ASP .NET, developers had to roll out their own login control, consisting of a username and password field, login button, and associated code for validating the user, creating an authentication ticket cookie, setting up the cookie expiry, and handling the security ticket encryption. However, with versions 2.0 and up of ASP .NET, we can drag & drop all of this functionality into the .NET web application by using the ASP .NET Login Control.

You may be surprised at just how easy it is to implement login functionality within your ASP .NET web application with the Login Control. To start, simply drag & drop the Login Control from the toolbar onto your Login.aspx web form. The control code will appear as follows:

1
<asp:Login ID="ctlLogin" runat="server" RememberMeSet="True" destinationPageUrl="~/User/" onloggedin="ctlLogin_LoggedIn">

Notice in the above tag, we’ve defined a Destination Page URL, which specifies the page that the user should be directed to after logging in. While this is a handy shortcut, there will likely be instances where users should be taken to different landing pages depending on the type of user logging in (ie. their role). We’ll get to this a bit later in the article. Also notice, we’ve defined an OnLoggedIn event handler, which allows us to handle the event which is fired after the user has completed logging in.

Now that the Login Control is available, we need to wire up the code to actually check the username and password. Also called authenticating, we’ll need to create a Membership Provider class which contains the code for the Login control to use.

Members Only with a Custom MembershipProvider

The custom membership provider is a class which inherits from the ASP .NET MembershipProvider base class. The base class contains all of the required methods for handling the validation of a user. While you’re free to implement all of the methods in the base class, you really only need to implement one of them, which makes the Login control even easier to use.

To begin, simply create a new class that inherits from MembershipProvider. Right-click on the base class and click Implement Abstract Members to create the stubs for the overridable functions. It may look like a lot of code that gets generated, but in the end you’ll only need to define the ValidateUser() function to get moving along, as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyMembershipProvider : MembershipProvider
{
    public override bool ValidateUser(string username, string password)
    {
        if ((username == "john" || username == "jane") && password == "doe")
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Notice in the above code, we’ve defined the ValidateUser() function, which is a base method of the MembershipProvider class. In this example, we’ve created an extremely simple validation routine, that simply consists of checking if the username is john or jane and that the password is doe. Obviously, in your actual .NET web application, you’ll probably check against an MSSQL or MySQL database to run a query to validate the username and password. This code would run inside the ValidateUser method.

It’s important to note that the Login control will automatically call the custom membership provider’s ValidateUser() method when the user clicks the Login button. If you were to create your own custom login controls, you would need to manually call this custom membership provider to access the ValidateUser() function.

Wiring the Custom MembershipProvider to the Web.Config

Although you’ve implemented a custom membership provider in your C# ASP .NET web application, you still need to tell the Login control about it. You can do this by specifying the membership provider in your web.config. This allows the Login control to know which membership provider you want to use (if you have multiple ones) and links the provider to your application.

To start, be sure you have an authentication tag declared in your web.config. It’s a good idea to include a distinct cookie timeout value as well.

1
2
3
  <authentication mode="Forms">
   <forms loginUrl="Login.aspx" name="MY_AUTH" slidingExpiration="true" timeout="30" />
  </authentication>

Notice in the above web.config excerpt, we’ve declared the authentication method as Forms, since we’ll be handling the login in our web application. We’ve also defined a cookie timeout value of 30 minutes with a sliding expiration. The sliding expiration resets the timeout each time the user logs back into the application. Depending on your web application, you may want a much longer cookie timeout value.

We now need to define the custom membership provider tag to let the login control know which membership provider to use.

1
2
3
4
5
6
  <membership defaultProvider="MyMembershipProvider">
   <providers>
    <clear/>
    <add name="MyMembershipProvider" type="WebApplication1.Providers.MyMembershipProvider, WebApplication1"/>
   </providers>
  </membership>

In the above web.config excerpt, we’ve defined a membership provider tag. Notice that the tag specifies a default provider. If your application will contain different ways of validating a user (such as debug or release), you can indicate which one is active by using the defaultProvider property. We’ve then defined the list of membership providers, which is similar to defining database connection strings. In this example, we only have 1 custom membership provider named MyMembershipProvider, as defined in the original class code above. Note for the type property, the value is the full namespace and classname, followed by a comma, and then the DLL library name.

At this point, you should be able to login to the ASP .NET web application by providing a valid login as specified in the custom membership provider’s ValidateUser() method.

To really get the security churning in our application, we now need to define some roles for our users by implementing a custom role provider.

Who’s Who in the Custom Role Provider

The role provider tells the C# ASP .NET web application which users have specific roles and allows us to implement an authorization process (not to be confused with authenticating). By authorizing a user, we can specify which features in the .NET web application he may have access to.

We start the role authorization process by specifying in the web.config the list of roles that can access a page or folder in the .NET web application. In general, it’s a good idea to separate files within folders in the web application, based upon role. For example, if you have two user roles, “User” and “Administrator”, you would have two folders containing the various associated pages. This allows you to create two web.config files inside those folders to dictate authorization.

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0"?>
<configuration>
  <appSettings/>
  <connectionStrings/>
  <system.web>
    <authorization>
      <allow roles="User" />
      <deny users="*" />
    </authorization>
  </system.web>
</configuration>

The above web.config would be placed in the “User” folder. Notice that we deny access for all users, except those in the “User” role. We’ll be setting the specific role for the user after they login by using the custom role provider. You can follow the same process to create an “Admin” folder and place the following web.config in the folder:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0"?>
<configuration>
  <appSettings/>
  <connectionStrings/>
  <system.web>
    <authorization>
      <allow roles="Administrator" />
      <deny users="*" />
    </authorization>
  </system.web>
</configuration>

Notice that we’ve only specified a single role to allow in each area. You could specify multiple roles by using a comma-delimited list in the allow roles property. In our ASP .NET web application example, administrators can have access to the Admin area and the User area. However, instead of adding a comma-delimited list of User,Administrator to the User area’s web.config, we’ll add two roles to Admin users of User and Administrator. This means that an administrator user is a member of the Users group and the Administrators group, allowing access to both sections of the web application.

We’ll now move on to creating the role provider and assigning the roles to users, which glues all of this together.

Creating the Custom Role Provider

The custom role provider is a class which inherits from RoleProvider, and implements the various functions required to handle user roles. The login control will actually utilize the custom role provider to assign the roles. You can then authorize the roles using the web.config or programtically by accessing the static Roles class or User.IsInRole throughout your ASP .NET web application.

The custom role provider is implemented almost exactly the same as the custom membership provider. You only need to implement a few of the overriden methods, as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MyRoleProvider : RoleProvider
{
    public override string[] GetAllRoles()
    {
        return new string[] { "User", "Administrator" };
    }

    public override string[] GetRolesForUser(string username)
    {
        if (username == "john")
        {
            return new string[] { "User" };
        }
        else if (username == "jane")
        {
            return new string[] { "Administrator", "User" };
        }
        else
        {
            return new string[] { };
        }
    }

    public override bool IsUserInRole(string username, string roleName)
    {
        string[] roles = GetRolesForUser(username);
        return (roles.ToList().IndexOf(roleName) > -1);
    }

    public override bool RoleExists(string roleName)
    {
        string[] roles = GetAllRoles();
        return (roles.ToList().IndexOf(roleName) > -1);
    }
}

Notice in the above code, we’ve added some very basic role code. Obviously, you would want to store the role information about your users in a more central location, such as an MSSQL or MySQL database. In this example, we’ve simply defined two roles: User and Administrator, as defined in the GetAllRoles() functions.

The real meat is found in the GetRolesForUser() function, which specifies the roles that a particular user should have. This function is the core behind the User.IsInRole() function and central to authorizing with the web.config settings. Notice in the above example how we determine the roles based upon the username. You would probably perform a database query with the username to fetch the user roles, however we’ve just included them in the code itself.

The remaining functions IsUserInRole and RoleExists simply take advantage of the other two defined functions, so they come practically for free.

We’re almost finished. We just need to add one more item to the web application’s web.config file in order to link the custom role provider to our login control.

Wiring the Custom RoleProvider to the Web.Config

In the same manner as adding the custom membership provider code to the web.config, we need to do the same for the custom role provider. This allows us to tell the C# ASP.NET web application which role provider to use. You can include the following code directly after the tag specifying the membership provider.

1
2
3
4
5
6
<roleManager defaultProvider="MyRoleProvider" enabled="true" cacheRolesInCookie="true" cookieName="MyRoles" cookieTimeout="30" cookiePath="/" cookieRequireSSL="false" cookieSlidingExpiration="true" cookieProtection="All">
 <providers>
  <clear/>
  <add name="MyRoleProvider" type="WebApplication1.Providers.MyRoleProvider, WebApplication1" applicationName="WebApplication1" writeExceptionsToEventLog="false"/>
 </providers>
</roleManager>

Notice that the role manager web.config code looks very similar to the membership provider code. We can include multiple role providers and we specify the default one by name. There are a few extra properties that we can define, such as the cookie timeout value (which should mirror the value you set for your forms authentication cookie).

With the role provider complete, you can now login to your C# ASP .NET web application with a fully functional authentication and authorization system all connected to the Login control. You can freely declare roles in the web.config within folders to restrict access and programtically call the User.IsInRole() function to determine roles. For example, inside your User folder (which contains the pages that only Users can access, as defined in the web.config inside this folder), we can call Roles.GetRolesForUser() to obtain the list of roles assigned to the logged-in user.

Remembering the User with Auto-Sign-In

With our security system in place, some developers may feel the user has to go through too many clicks to login to the C# ASP .NET web application each time. We can help alleviate this issue by implementing an easy auto-sign-in feature to automatically login the user each time he accesses the site’s login page.

Since we’re using the ASP .NET login control, automatically signing the user in is as simple as including the following code in the Page_Load() event of your Login.aspx page:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void Page_Load(object sender, EventArgs e)
{
// Auto sign-in.
if (User.Identity.IsAuthenticated)
{
ctlLogin.UserName = User.Identity.Name;
ctlLogin_LoggedIn(sender, e);
}
}

protected void ctlLogin_LoggedIn(object sender, EventArgs e)
{
Response.Redirect("~/User/");
}

Note, we simply check if the user accessing the page is already authenticated, meaning that an authentication cookie already exists from a previous time. If they are, we set the username property for the login control and call the LoggedIn() event to redirect the user to their home page. It’s important to note that you will need to define a sufficiently long cookie timeout value for your forms authentication and role provider in the web.config file. Otherwise, the cookie may timeout by the time the user returns to the web application, and the username would be lost.

Notice in the body of our LoggedIn() event for the ASP .NET login control, we simply redirect the user to their designated landing page after logging in. Depending on the number of users and roles in your C# ASP .NET web application, you may need to direct users to different landing pages after logging in, based upon their role. You can do this directly in the code or create a more central method for managing this feature.

With this final code, you’re user-login system is ready to go.

Conclusion

Authenticating and authorizing users is a neccessary part of many C# ASP .NET web applications in order to provide the ability for users to login to the web application and to protect areas of the application from outsiders. By utilizing the built-in ASP .NET login control and providing a custom membership provider and role provider, we can quickly integrate a login and security system within our web application with ease.

About the Author

This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.

Share