
Introduction
This article demonstrates how to use Form Authentication in ASP.NET. I have written a set of classes and a small web application that uses these classes as an example. The small application features 4 forms (pages) that allow you to do the following functions: Add new user, assign roles to users, remove roles from users and manage roles. Although the classes I've written provide quite enough functions that are ready to use, for the demonstration purpose, I have limited the fields in the User class. That means users can provide some basic fields when registering for a new account: Full Name, Email, Password, Biography. You can add more fields later if you want, it's quite easy.
The Classes Overview
There are 4 classes: User, Role, SitePrincipal and SiteIdentity. I would like to overview the classes' methods and properties here:
The User class
User() |
Default parameter less constructor to create a new user |
User(int userID) |
This constructor gets a userID and looks up the user details from the database |
User(string email) |
This constructor gets an email and looks up the user details from the database |
GetUsers() |
This method returns a DataSet of all the users available in the database |
GetRoles() |
This method returns a DataSet of roles assigned to the current user |
GetUserRoles(int userID) |
This static method grabs the userID and returns a roles ArrayList assigned to that user |
AddToRole(int roleID) |
This method assigns a role to the current user |
RemoveFromRole(int roleID) |
This method removes current user from the role that has been passed by the roleID. |
Add() |
Adds a new user to the database |
Update() |
Updates current user information |
Delete() |
Deletes current user |
UserID |
Gets/Sets user's id number |
FullName |
Gets/Sets user's full name |
Email |
Gets/Sets user's email |
Password |
Gets/Sets user's password |
Biography |
Gets/Sets user's biography |
DateAdded |
Gets/Sets user's registering date |
The Role class
Role() |
Default parameter less constructor to create a new role |
Role(int roleID) |
This constructor gets a roleID and looks up the role details from the database |
GetRoles() |
This method returns a DataSet of all roles available in the database |
Add() |
Adds a new role to the database |
Update() |
Updates current role information |
Delete() |
Deletes current role |
RoleID |
Gets/Sets role ID number |
RoleName |
Gets/Sets role name |
The SitePrincipal class (implements the IIPrincipal Interface)
SitePrincipal(int userID) |
This constructor gets a userID and looks up details from the database |
SitePrincipal(string email) |
This constructor gets an email and looks up details from the database |
IsInRole() |
(IIPrincipal.IsInRole()) Indicates whether a current principal is in a specific role |
ValidateLogin() |
Adds a new user to the database |
Identity |
(IIPrincipal.Identity) Gets/Sets the identity of the current principal |
Roles |
Gets the roles of the current principal |
The SiteIdentity class (implements the IIdentity Interface)
SiteIdentity(int userID) |
This constructor gets a userID and looks up the user details from the database |
SiteIdentity(string email) |
This constructor gets an email and looks up the user details from the database |
AuthenticationType |
(IIdentity.AuthenticationType) Always returns "Custom Authentication" |
IsAuthenticated |
(IIdentity.IsAuthenticated) Always returns true |
Name |
(IIdentity.Name) Gets the name of the current user |
Email |
Gets the email of the current user |
Password |
Gets the password of the current user |
UserID |
Gets the user ID number of the current user |
Enabling Forms Authentication
To enable ASP.NET Forms Authentication, your application web.config file must contain the following information:
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="RolesBasedAthentication"
path="/"
loginUrl="/Login.aspx"
protection="All"
timeout="30">
</forms>
</authentication>
</system.web>
</configuration>
The authentication mode is set to Forms, this enables the Forms Authentication for the entire application. The value of the name attribute is the name of the browser cookie, the default value is .ASPXAUTH but you should provide a unique name if you are configuring multiple applications on the same server. The loginUrl is the URL to your login page. The timeout is the amount of time in minutes before a cookie expires, this attribute does not apply to persistent cookies. The protection attribute: is the way your cookie data is protected, ALL means that your cookie data will be encrypted and validated. Other values that you can set are: None, Encryption, Validation.
When Forms Authentication is enabled, each time a user requests a page, the form will attempt to look up for a cookie in the user's browser. If one is found, the user identity was kept in the cookie represented in the FormsIdentity class. This class contains the following information about the authenticated user:
AthenticationType - returns the value Forms
IsAthenticated - returns a boolean value indicating where the user was authenticated
Name - Indicates the name of an authenticated user
Because the FormsIdentity contains only the Name of the user and sometimes you need more than that, that's why I have written the SiteIdentity which implements the IIdentity interface to contain more information about the authenticated user.
Creating the Login Page
For creating the login page, you simply need 2 textboxes to let the user input the email address and password, named Email and Password, respectively. You may need 1 check box to ask if the user wants us to set a persistent cookie, and finally one submit button with OnClick event which is handled as follows:
private void Submit_Click(object sender, System.EventArgs e)
{
SitePrincipal newUser =
SitePrincipal.ValidateLogin(Email.Text, Password.Text);
if (newUser == null)
{
ErrorMessage.Text = "Login failed for " + Email.Text;
ErrorMessage.Visible = true;
}
else
{
Context.User = newUser;
FormsAuthentication.SetAuthCookie( Email.Text, true );
Response.Redirect("Default.aspx");
}
}
The code above is straightforward, first we call SitePrincipal.ValidateLogin() which looks up the database and check if the user has entered the correct email and password and returns the new instance of SitePrincipal object. If the new object is null that means the user has not entered a correct email or password, otherwise we assign the current user with the new object. Then set the cookie and redirect the user to the main page.
Authenticating User On Every Request
Whenever user requests a page, the ASP.NET Forms Authentication will automatically pick up our cookie. But we haven't replaced the current context user with our own, so we should create a pagebase class as base class and replace the current context user with our own so that every page that is derived from this pagebase will have our own SitePrincipal instance as context user. When the SitePrincipal is instantiated, it will automatically search for roles that match the current user and assign to the user's roles. The code below creates a pagebase class and replaces the current context with our own:
public class PageBase: System.Web.UI.Page
{
public PageBase()
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
this.Load += new System.EventHandler(this.PageBase_Load);
}
private void PageBase_Load(object sender, System.EventArgs e)
{
if (Context.User.Identity.IsAuthenticated)
{
if (!(Context.User is SitePrincipal))
{
SitePrincipal newUser =
new SitePrincipal( Context.User.Identity.Name );
Context.User = newUser;
}
}
}
}
So now every page should derive this bass class instead of deriving the System.Web.UI.Page. So if you want to get the current name or email address or user ID of the authenticated user, you can do like this:
if (Context.User.Identity.IsAuthenticated)
{
string name = ((SiteIdentity)Context.User.Identity).FullName;
string email = ((SiteIdentity)Context.User.Identity).Email;
string password = ((SiteIdentity)Context.User.Identity).Password;
string userID = ((SiteIdentity)Context.User.Identity).UserID;
}
Or if you can check if the current user is in a specific role as following:
if (Context.User.Identity.IsAuthenticated)
{
if (!((SitePrincipal)Context.User).IsInRole("Site Admin"))
Response.Redirect("Login.aspx");
}
The Demo Application
All the code above is the only base for using my classes to turn your application into a roles-based authentication system. How ever I have written a small demo web application that uses these classes as an example with quite enough functions like: insert/update/delete roles, assign user to roles and remove user from roles. In order to get the application up and running, you need to have SQL Sever, since I'm not using Access as a database management system.
You can download the demo application and all the source code for the classes from the links at the top of this page and follow these steps to get the application up and running:
- Copy the RolesBasedAthentication.Web folder to the wwwroot directory.
- Share the RolesBasedAthentication.Web folder by right clicking and choose Properties and then open the Web Sharing tab and choose Share this folder.
- Create a new database and name it RolesBasedAuthentication.
- Run the script in the database.sql using Query Analyzer to create tables and stored procedures for the new database.
When running the application, log on with account: admin@site.com and password: admin to have full access. Hope you find this small application helpful.
|
|
 |
 | Pls help me Stelios80 | 7:05 19 Sep '06 |
|
 |
Unable to cast object of type 'System.Security.Principal.GenericPrincipal' to type 'music.SitePrincipal'. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidCastException: Unable to cast object of type 'System.Security.Principal.GenericPrincipal' to type 'music.SitePrincipal'.
Source Error:
Line 29: // if user is not in the Site Admin role, Line 30: // he/she will be redirected to the login page Line 31: if (!((SitePrincipal)Context.User).IsInRole("admin")) Line 32: Response.Redirect("Login.aspx"); Line 33: }
Stelios80
|
|
|
|
 |
 | Error While Editing Email klopik | 5:32 23 Nov '05 |
|
 |
I've got this error while trying to edit email of current context user No error while changing everything else. Can you help me ??
Server Error in '/RolesBasedAthentication.Web' Application. --------------------------------------------------------------------------------
There is no row at position 0. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IndexOutOfRangeException: There is no row at position 0.
Source Error:
Line 66: connection.Close(); Line 67: Line 68: LoadDetails(dataSet.Tables["Users"].Rows[0]); Line 69: dataSet.Dispose(); Line 70: }
Source File: e:\developing\dotnetdeveloping\authentication\rolesbasedathentication\user.cs Line: 68
Stack Trace:
[IndexOutOfRangeException: There is no row at position 0.] System.Data.DataRowCollection.get_Item(Int32 index) +63 RolesBasedAthentication.User..ctor(String email) in e:\developing\dotnetdeveloping\authentication\rolesbasedathentication\user.cs:68 RolesBasedAthentication.SiteIdentity..ctor(String email) in E:\Developing\DotNetDeveloping\Authentication\RolesBasedAthentication\SiteIdentity.cs:16 RolesBasedAthentication.SitePrincipal..ctor(String email) in E:\Developing\DotNetDeveloping\Authentication\RolesBasedAthentication\SitePrincipal.cs:16 RolesBasedAthentication.Web.PageBase.PageBase_Load(Object sender, EventArgs e) in E:\Inetpub\wwwroot\RolesBasedAthentication.Web\PageBase.cs:27 System.EventHandler.Invoke(Object sender, EventArgs e) +0 System.Web.UI.Control.OnLoad(EventArgs e) +67 System.Web.UI.Control.LoadRecursive() +35 System.Web.UI.Page.ProcessRequestMain() +731
-------------------------------------------------------------------------------- Version Information: Microsoft .NET Framework Version:1.1.4322.573; ASP.NET Version:1.1.4322.573
|
|
|
|
 |
 | FormAuthentication Ticket mex mex | 8:04 11 Nov '05 |
|
 |
Thank you for the great example. However, being somewhat of a newbie...I was wondering if you could briefly explain why you haven't implemented the FormAuthentication Ticket? I may be overlooking something....could you shed some light on the subject? Is it needed?
Thanks again for the great code example.
Chhers
|
|
|
|
 |
|
 |
see that was a good question. maybe the ticket is where the cool and useful properties and attributes are hiding. lets see!
|
|
|
|
 |
 | How about this? jay@gatewaywebsys.com | 7:01 15 Sep '05 |
|
 |
http://www.pluralsight.com/articlecontent/efficientRoleBasedAuthentication.pdf
|
|
|
|
 |
 | Plz help me out nishil@rediffmail.com | 0:14 30 Aug '05 |
|
 |
When i login with id:admin@site.com and password:admin...it redirects me to the default page.On default page i see Welcome,guest.Why so?...i tried debugging.in header.ascx page..in load event.. if(Context.User.Identity.IsAuthenticated)..the value here is false...and it goes in the "else" part and logs me as guest. im logged in as admin..and i dont see the roles of admin.mail me at nishil@rediffmail.com
|
|
|
|
 |
 | IsAuthenticated ??? Bahadir ARSLAN | 13:02 30 Jun '05 |
|
 |
This property always returns true; so why we check it?
public bool IsAuthenticated { get { // assumption: all instances of a SiteIdentity have already // been authenticated. return true; } }
is there something that i forget to do?
Bahadir ARSLAN www.maxiasp.net
|
|
|
|
 |
|
 |
As for me i want much more details. just a true or false is not enough for me! i want details man details, properties and attributes!
|
|
|
|
 |
 | System.InvalidCastException swandown | 7:01 29 Oct '04 |
|
 |
Hi, I've tried to incorporate your RolesBasedAthentication classes into my application. I basically recreated Role.cs/SiteIdentity.cs/SitePrincipal.cs/User.cs in the root of my project. My login page page calls the ValidateLogin method of the SitePrincipal.cs and authenticates fine but when I try to get the current name or email address or user ID of the authenticated user to display in my header.cs I get the following :
System.InvalidCastException: Specified cast is not valid
here is some of the code from my header.cs :
if (Context.User.Identity.IsAuthenticated) { lbl = new Label(); lbl.Text = " Welcome " + ((SiteIdentity)Context.User.Identity).Name; ... ... }
Could you help? Mark
|
|
|
|
 |
|
 |
I'm getting the same error.
Were you able to figure anything out?
.brit
|
|
|
|
 |
|
 |
You can not cast a object there is Inherits from a sub interface!
You can cast from A to D, but it might throw an InvalidCastException. You would have to check every time to see if it is able to cast from A to D. You can use the as operator to do so. It will attempt to perform a cast to a type, and then return null if the cast is not possible:
// Assume a has an instance of D in it, but the variable is of type a. D d = a as D;
// If d is not null then the cast was successful. if (d != null) { // Use d here. }
More precis:
SitePrincipal d = Context.User as SitePrincipal;
if(d == null) { SiteIdentity sid = new SiteIdentity(Context.User.Identity.Name); d = new SitePrincipal(sid.Email); }
|
|
|
|
 |
 | timeout elnife | 22:06 28 Sep '04 |
|
 |
when we place mode in authentication tag equal to forms,then we determine a timeout for it.if we want that some of forms will be shown for any one whenever they want,how can we do this?(i mean some of forms do not have timeout) thank u
elnife
|
|
|
|
 |
 | Code is wrong zarkon | 13:56 24 Sep '04 |
|
 |
Not only is this work apparently largely plagarized, but it does not do what it claims. It appears to work, but only because it is hitting the database to lookup the user on every page. By the time the page base class is reached its too late to retrieve the SitePrincipal object from Context.User; its always just GenericPrincipal at that point and the database must be hit all over again. You have to get and set SitePrincipal in "Application_AcquireRequestState" as one reader has already suggested.
|
|
|
|
 |
|
 |
What do you want for free? And, without any stated or implied warrantee? Pay an Expert and get an Expert. This code in here is supposed to stimulate you to have ideas and move in a viable direction. Its not supposed to be a quick solution for nothing. This is an area for senior developers.
|
|
|
|
 |
 | Missing Functionality and not optimised for scalability Simon Knox | 18:22 6 May '04 |
|
 |
Thanks for your article, I have some suggestions.
Passing back a DataSet containing a list of roles seems a bit of an overkill, a | separated string or an array of strings would be sufficient.
With Forms Authentication it would be better to use FormsAuthentication.GetRedirectUrl() after the Login so that the user is sent back to the page they came from.
What about using an encrypted FormsAuthenticationTicket? When using Forms Authentication you should use a FormsIdentity so that you can store the FormsAuthenticationTicket. Forms Authentication also uses a GenericPrincipal. FormsIdentity can not be extended however GenericPrincipal can.
Your method uses Context.User.Identity.YourProperty. If you extend GenericPrincipal with the properties you need then they can be accessed via Context.User.YourProperty. This is also much simpler as you only have to extend GenericPrincipal.
|
|
|
|
 |
 | MS-Access Version guga032 | 2:31 3 May '04 |
|
 |
I had converted the application to MS-Access, but the update action doesn´t work and didn´t give any error message. I am tinking that is a problem of the fact of extending iidentity makes windows to think that user is not able to update. Am I correct?
|
|
|
|
 |
|
 |
I discovered the error. I had success on converting to ms-access, the problem occurs in the way os setting parameters of stored procedures, I change the way and it worked very well, I am using this mettod in various aplications
|
|
|
|
 |
 | Set up restricted pages - roles paulpinder | 1:39 24 Feb '04 |
|
 |
I have understood the example of the form authentication to the point that only the user within the database can be authenticated to the certain page.
I would like to know how you setup a role restriction that will only allow for example "site admin" to view / redirected to admin.aspx and "tech admin" to be redirected to tech.aspx when loggin on.
I want to be able to restrict certain users from certain web pages.
How and where do i put the code for this. I think its something to do with the following code.
if (Context.User.Identity.IsAuthenticated) { if (!((SitePrincipal)Context.User).IsInRole("Site Admin")) response.redirect("loin.aspx"); }
where would the above code go? should there be anything stored in the global.ascx ?
thanks.
|
|
|
|
 |
|
 |
You should place the code in the Page_Load method. When the page loads it will redirect to the login page if the user doesn't have the specific role.
Zeke.
Waiting to kill Bill
|
|
|
|
 |
|
 |
so the below code: should be put on everypage within the the page_load ?
if (Context.User.Identity.IsAuthenticated) { if (!((SitePrincipal)Context.User).IsInRole("Site Admin")) response.redirect("loin.aspx"); }
If no then where and how do i set it up, i tried putting the above code in and i get the following error message :
(104): The type or namespace name 'SitePrincipal' could not be found (are you missing a using directive or an assembly reference?)
Paul.
|
|
|
|
 |
|
 |
it looks to me like just a simple way to login to a form. i wish it were more, and we'll see if i can make a silk purse out of a sow's ear. however until then i suggest you give up your desire to kill something because this is science man science and requires a patient hypothesis and conclusion and none of your hot sports killer instincts and if you dont like solving puzzles you are in the wrong place
|
|
|
|
 |
 | Can you please help me here. Orlando Bloom | 18:19 11 Dec '03 |
|
 |
I tried to create a project patterned to what you did but I tried to use vb and the lines you gave that can output the the information about the current user seems to give an error that says "System.InvalidCastException: Specified cast is not valid".
Yourr code: if (Context.User.Identity.IsAuthenticated) { string name = ((SiteIdentity)Context.User.Identity).FullName; string email = ((SiteIdentity)Context.User.Identity).Email; string password = ((SiteIdentity)Context.User.Identity).Password; string userID = ((SiteIdentity)Context.User.Identity).UserID; }
My translation:
If Context.User.Identity.IsAuthenticated Then lLog.Text = CType(Context.User.Identity, zekeapp.rmmIdentity).Login() lPass.Text = CType(Context.User.Identity, zekeapp.rmmIdentity).Password() lFull.Text = CType(Context.User.Identity, zekeapp.rmmIdentity).Name() End If
Can you tell me what seems to be the problem.
|
|
|
|
 |
|
 |
OK, if you said I stole that code that's fine. But there's a few things I want to explain:
I did not copy and paste to my code, I made the code easier so beginners can understand, if you haven't read the code then do not say I'm stealing it. Besides I also spent my time to write an article about it I didn't just submit the code. Ok so I learned it through some books. My way of writing code is affected alot by that book (ASP.NET Programming) because that's the first one I read and the only one. So I code any website they're all similiar. Everyone learns coding from else where and of course their coding style will be affected.
Zeke.
Waiting to kill Bill
|
|
|
|
 |
|
 |
BTW So sorry that I just forgot to give credits. Someone told me this before also the bug you pointed out someone else did tell me, I wanted to fix resubmitting but I've just been very busy. I will fix them soon. Thanks for pointing it out. But hey if you haven't read the original code how do you know that my code is that similar??? I believe most ASP.NET beginners will find my my code alot easier to understand because the when I read that book I struggled so hard to understand it and I know how to write it the easiest way.
Zeke.
Waiting to kill Bill
|
|
|
|
 |
|
 |
Its impossible to steal this code. object orientated code is just a bunch of calls to class libraries. you cant steal anything. its just how things are done. it has to look something like this no matter where you are! so lets try to relax with the idiology since this is a science only. if you saw this code or any code someplace before thats because it belongs in all those places.
|
|
|
|
 |
|
|
Last Updated 23 May 2003 |
Advertise |
Privacy |
Terms of Use |
Copyright ©
CodeProject, 1999-2010