Published:
01 Apr 2008
|
Abstract
Using Role based forms authentication, we can restrict users of the site to accessing certain resource if they are not part of a particular role. In this article, Satheesh demonstrates how to build sites with this type of authentication. He provides a short overview of various Login Controls and Providers and then discusses a scenario with detailed explanation of various aspects of the sample application with screenshots and source code. |
|
by Satheesh Babu
Feedback
|
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days):
65614/
89
|
|
|
Introduction |
Any ASP.NET site we develop will always have an
authentication module unless and until there is nothing private on the site or
something we explicitly know as being unnecessary. For instance, a simple
public content site will not require an authentication module. Depending on the
nature of the application we can decide on the type of authentication we are
going to provide for our site. Meaning, if it is going to be an intranet web
application, we can very well utilize the windows authentication by making use
of the availability of user information in the AD, thus preventing a separate
user information storage private to our application. Still, role mapping to the
users has to be stored privately for the application. If our application is an
internet application then the best choice is to use Forms Authentication. This
article will explore the implementation forms authentication with roles in ASP.NET
2.0.
From 1.x days, the implementation of forms authentication in
ASP.NET is not that complicated. But the actual drawbacks are in those days ASP.NET
itself does not have that many controls that aid in implementing forms
authentication easily. Also, if we have role based forms authentication then we
should go for our custom code for validating a resource access. With the
introduction of 2.0 we have a handful of controls that help in implementing
role based forms authentication very easily with the primary support of Providers in ASP.NET 2.0.
|
Who Process the FormsAuthentication? |
Forms authentication is processed by a HTTP module called
FormsAuthenticationModule which takes part in regular ASP.NET page processing.
|
Login Controls and Providers |
As I said earlier, ASP.NET 2.0 is packed with set of new
controls called Login controls through which we can implement forms
authentication with less effort. This section will take us through how best to
use these Login controls in our projects. By default, Login controls will use
the default membership provider and role provider which will have their own
database schema. So, it is not advisable to use this controls as it is because
enterprise applications itself will have its own database schema and
architecture. The solution for this problem is to develop our own custom provider
or our own custom code that solves our application specific requirements.
However, we can still use some of the login controls, like LoginView, LoginName
and LoginStatus, in our applications. In this article I will explain our
subject matter with a custom implemented login form without using any login
control on it. Also, we will have a look on how to use some of the login
controls effectively with role based forms authentication. Moving forward, I
will explain a sample scenario where we can use role based forms authentication
and will use the same scenario throughout this article for easy understanding.
|
Scenario |
Consider we are creating a content management system where
users can login and post their contents. The posted contents will be moderated
by the administrator of the site. Meaning, Administrator can either approve or
reject the content posted by the user. The posted content can be viewed by
anyone once it is approved by the admin. In this scenario we can have 2 roles
defined, namely Admin and Publishers. The sample application attached with this
article uses the same scenario explained in this article.
|
Application Structure |
Our application will have two folders called Admin and
Publisher as shown in the figure below (Figure 1 – Sample Application
Solution). Admin folder will have all the ASPX pages that are put together to
form the Admin module while the Publisher folder will have all the ASPX pages
for Publisher module.
Figure 1 – Sample Application Solution
|
Configuring Forms Authentication |
As we all know, this is done in Web.config by changing the
value of <authentication> tag.
Listing 1
<authentication mode="Forms">
<forms loginUrl="Login.aspx"
name=".ASPXAUTH"
protection="All">
</forms>
</authentication>
The default timeout for forms authentication is 30 minutes.
It indicates that 30 minutes of inactivity on the application will cause
timeout expiration and the user will be prompted to the login page. Any hit to
the site after login will reset this clock to 30 minutes again starting from
that time. If we want to override this setting then we can include this in the
above <forms> tag. See MSDN for a full list of attributes that can be
specified in this tag.
After configuring forms authentication we need to configure the
authorization part of the Web.Config.
Listing 2
<authorization>
<allow users="*"/>
</authorization>
The above setting says it will allow all the users because
the posted content should be viewed by anyone as I said in the Scenario
section. The next section will explain the configuration settings to restrict
users accessing the pages in Admin and Publisher folders.
|
Controlling Access to Roles |
This is done by <Location> tag in Web.Config file.
Listing 3
<location path="ADMIN">
<system.web>
<authorization>
<allow roles="ADMIN"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
<location path="Publishers">
<system.web>
<authorization>
<allow roles="PUBLISHER,ADMIN"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
The above setting will restrict users trying to access the
Admin section and Publisher section until they are part of that role and allow
users who are already part of the roles.
|
Constructing Login Form |
Construct a login form that has a textbox for entering user
ID and password, and a button for login with an optional Remember me checkbox.
On Login button click do the following steps.
1.
Create Forms Authentication ticket.
Listing 4 – FormsAuthentication ticket syntax
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
int version,
string userName,
DateTime CreationTime,
DateTime Expiration,
bool IsPersistent,
string UserData,
string CookiePath);
2.
User’s role information can be specified in UserData in the above
argument list.
3.
Encrypt the above created ticket through following method in
FormsAuthentication class.
Listing 5 - Encrypt Ticket
string Encrypt(FormsAuthenticationTicket ticket);
4.
It returns a string containing an encrypted forms-authentication ticket
suitable for use in an HTTP cookie.
5.
Create the cookie with the encrypted.
6.
Add the created cookie to the response object.
7.
The below code Listing 6 shows the implementation of the above steps.
Listing 6 - Login Event
protected void btnLogin_Click(object sender, EventArgs e)
{
User _user = new User();
DBOperations dbo = new DBOperations();
_user = dbo.CheckUser(txtUserid.Text);
if (_user != null)
{
if (_user.Password == txtPassword.Text)
{
FormsAuthenticationTicket Authticket = new FormsAuthenticationTicket(1,
txtUserid.Text, DateTime.Now, DateTime.Now.AddMinutes(30),
chkRememberMe.Checked, _user.Role, FormsAuthentication.FormsCookiePath);
string hash = FormsAuthentication. Encrypt(Authticket);
HttpCookie Authcookie = new HttpCookie
(FormsAuthentication.FormsCookieName, hash);
if (Authticket.IsPersistent)
Authcookie.Expires = Authticket.Expiration;
Response.Cookies.Add(Authcookie);
string returnUrl = Request.QueryString["ReturnUrl"];
if (returnUrl == null)
returnUrl = "/";
Response.Redirect(returnUrl);
}
else
{
lblMessage.Text = "Password does'nt match.";
}
}
else
{
lblMessage.Text = "User not exists.";
}
}
Since the user information is stored as encrypted value in
the cookie we need to construct the decrypted version of our credentials for
every request and assign it to the Context object. This is done to make the
user information available on the pages. The FormsAuthentication module will
decrypt the forms authentication ticket in the cookie and make it available
through the property HttpContext.Current.User.Identity. A new GenericPrincipal
object should be constructed and assigned to the User property of Context
object. This has to be done in Application_AuthenticateRequest event in
Global.asax file. By default, there will be no Global.asax file added to our
solution if you use visual studio 2005 so we need to add it explicitly through
"Add new Item."
Listing 7 - Application Authenticate Event
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
string userInfo = ticket.UserData;
string[]roles = userInfo.Split(',');
HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
}
We need to import System.Security.Principal namespace to the
Global.asax file for the above code to work.
Listing 8 - Import Namespace
<%@ Import Namespace="System.Security.Principal" %>
|
Adding Remember Me Option |
In the above code the line that creates FormsAuthentication
ticket has an argument for specifying cookie as a persistent or non persistent
cookie. This attribute is the one that is used for "Remember Me"
option in Login pages. Include a Checkbox beneath the password textbox in the
login page and assign the Checked property of the
checkbox as the value for the above said argument.
We have completed creating a simple site with role based
forms authentication. Now when we run our application, users with admin role
can access the resources in admin section publisher section. A user with a publisher
role cannot access admin resources while he can access the Publisher section.
In the next section we will see how to use the LoginView
control to show and hide a group of controls on a page.
|
Using LoginView Control with Roles |
It can be used to show or hide a group of control or content
based on the user login status.
Below is the syntax of using it.
Listing 9 - LoginView Control
<asp:LoginView ID="LoginView1" runat="server">
<LoggedInTemplate>
</LoggedInTemplate>
<AnonymousTemplate>
</AnonymousTemplate>
</asp:LoginView>
Things inside <LoggedInTemplate> will be exposed only
if the user is already logged in where the things in <AnonymousTemplate>
will be visible even if the user is not logged in.
For example, if we want to display or hide controls based on
the user's role then this control is a champion of doing it with very little
effort. Consider we have an error page that reports the error to the users. If
we want to hide the technical error message to all the users and show the
technical error message only for the users with ADMIN role then we can use
LoginView control to achieve this requirement.
Refer to the below code for the implementation.
Listing 10 - LoginView Control With Roles
<asp:LoginView ID="loginview" runat="server">
<RoleGroups>
<asp:RoleGroup Roles="ADMIN">
<ContentTemplate>
<b>Technical Information:</b><%= TechnicalErrorMsg %>
</ContentTemplate>
</asp:RoleGroup>
</RoleGroups>
</asp:LoginView>
The above code will display the technical error messages
only to the users with ADMIN Role.
We can also achieve the above with the following code.
Listing 11 - Role Chech In Code
if (User.IsInRole("ADMIN"))
lblMessage.Text = TechnicalErrorMsg;
The next sections will describe some of the common problems
that we will come across when we use FormsAuthentication and the workarounds to
those problems.
|
Redirecting user to NotAuthorized page |
When a user who does not belong to admin role is already
logged in and has tried accessing the admin section, unfortunately he will be
again forwarded to the Login page. Refer to Figure 2.
Figure 2 – NotAuthorized Problem
Instead, if a "Not authorized" page is displayed,
it will be more appropriate for this scenario.
We can achieve this by redirecting the user to NotAuthorized
page by including the below code in Page_Load event of Login page.
Listing 12 - Not Authorized Page Redirection
if (User.Identity.IsAuthenticated && Request.QueryString["ReturnUrl"] != null)
{
Response.Redirect("NotAuthorized.aspx");
}
The above code checks whether the user is authenticated and
ReturnUrl is not null through which we can confirm that the user is trying to
access a resource which he does not have access after authenticating.
|
FormsAuthentication.SignOut() doesnot reflect in LoginStatus
and LoginName |
Suppose, if we have a password reset feature in our ASP.NET site
then the user should be forced to logged out once he chooses the new password, and
clicked save. He should be asked to login again and verify the new password.
When a user is already logged in, he can reach the form
PasswordReset.aspx and he can choose the new password. When the user types in
the new password and clicks save, the user is logged off by calling SignOut()
method of FormsAuthentication object. Refer to the code below.
Listing 13 - FormsAuthentication Signout Problem
int res = userDAO.ResetPassword(User.Identity.Name, txtOldPassword.Text,
txtConfirmPassword.Text);
lblMessage.Text = SUCCESSMSG;
FormsAuthentication.SignOut();
The actual problem is here. After the password reset is
successful the above code will log out the user as planned. But the LoginStatus
and LoginName controls on the page will still show the status of the user as
logged in as seen in Figure 3.
Figure 3 – Signout Problem
When the form is refreshed or for the subsequent server trip
the status of the user that is displayed on the form will be successfully
changed to logged out. The cause for this problem is even after the
FormsAuthentication.SignOut(); line execution the context information of the
current request will still hold the logged in user information. On executing
the line Context.User.Identity.Name, the signout code will still give the
logged in user information and thus LoginStatus control will not reflect the
logout change. From the next request Context.User.Identity.Name will have no
value in it which will make the login control to behave correctly.
The work around for the above problem will be making the
User attribute of Context object to null.
The final code is:
Listing 14 - FormsAuthentication Signout Problem
Resolution
int res = userDAO.ResetPassword(User.Identity.Name, txtOldPassword.Text,
txtConfirmPassword.Text);
lblMessage.Text = SUCCESSMSG;
FormsAuthentication.SignOut();
Context.User = null;
The other way of solving this problem is by redirecting the
user to the login page after changing the password. But you need to have some
logic to notify the user that the password is successfully changed.
|
How to use the source code attached with this article |
Unzip the code, open it with Visual Studio 2005 and hit F5
to run. The code uses database attached in APP_Data, so you need to change any
setting in Web.Config. The database has already 2 user ID's created, test1 for
ADMIN role and test2 for Publisher role, passwords are the same as user ID.
Download the code to understand it better.
|
Downloads |
|
Reference |
|
Conclusion |
We have learned implementation of role based forms
authentication in ASP.NET 2.0 with a real time example. This article also provides
some of the tips to use the login controls efficiently in role based forms
authentication. Download the code packed with this article and see it in
action.
Happy Coding!!!
|
|
|
User Comments
Title:
it works at once
Name:
snopbear
Date:
2008-08-26 9:32:07 AM
Comment:
When I log in with test1 or any of them, I get redirected to a HTTP Error 404 - Not Found page. Any ideas?
|
Title:
Question
Name:
Jeff
Date:
2008-06-26 9:53:38 AM
Comment:
When you log into the site with a user, go to their homepage, then click logout, hit the BACK button enough, it will get you into the home page without credentials.
Once you hit refresh it kicks you out and you have to login again.
This happens in IE6. It seems to work in Firefox right.
|
Title:
Something is wrong
Name:
Mike
Date:
2008-06-25 10:16:43 AM
Comment:
When I log in with test1 or any of them, I get redirected to a HTTP Error 404 - Not Found page. Any ideas?
|
Title:
permissions and roles
Name:
tariq
Date:
2008-05-12 5:39:03 AM
Comment:
Hi,
I need to grant users with permissions depending on their roles. I have heard about Visual Guard .Net http://www.visual-guard.com/EN. before I start testing, do you have any feedback about this tool?
thank you
|
Title:
Thnx
Name:
Dhaval Patel
Date:
2008-05-02 11:04:11 AM
Comment:
thanks buddy... it helped understanding it better.
|
|
|
|