0% found this document useful (0 votes)
110 views

Forms Authentication and Authorization

The document discusses implementing role-based authorization in an ASP.NET web application using Forms authentication. It describes modeling roles, users, and their relationships in database tables. It also explains determining a user's roles by executing a stored procedure during the AuthenticateRequest event and assigning roles to the user principal.

Uploaded by

shivuhc
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
110 views

Forms Authentication and Authorization

The document discusses implementing role-based authorization in an ASP.NET web application using Forms authentication. It describes modeling roles, users, and their relationships in database tables. It also explains determining a user's roles by executing a stored procedure during the AuthenticateRequest event and assigning roles to the user principal.

Uploaded by

shivuhc
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 9

Role-Based Authorization With Forms

Authentication Part 1
Introduction
In Web applications, typically there exist certain parts of the site that only certain individuals, or
groups of individuals, can access. For example, imagine an intranet Web site used to administer the
content on a company's public Internet Web site, where the public Web site lists products sold by the
company. From the administrative site, all company employees might be able to make minor changes
to the products' descriptions, quantity, and other such non-essential information. However, perhaps
only a subset of trusted employees might be able to change the products' prices. And even a smaller
subset of employees would be able to add new products or delete existing products from the database
To handle such a hierarchy of capabilities, a traditional security model to use is to divide users into
roles, and then to assign permissions to various resources on a role-by-role basis. For example, our
fictional company's administrative Web site might be setup so that the President, CEO, and CIO are
made "Administrators" of the product database, and have access to change the product database via
the online interface in any way they desire. The top-level managers might be added to the Price
Changer role, meaning they can change the prices of existing products, while all other company
employees were inserted into the Minor Updates role. Such a security model is typically referred to a
role-based authorization model, as the authorization users enjoy is based upon the role they play
within the system.
In this article we will examine how to utilize role-based authorization in an ASP.NET Web application
using Forms authentication. Specifically, we'll examine the data model needed for implementing roles,
we'll see how to determine what roles a given user belongs to, and then how to restrict (or grant)
access to resources based on a user's roles.
A Quick Security Overview
When thinking or talking about security, there are two fundamental underpinnings which are central to
understanding role-based authorization and security models in general. These two concepts are:

Authentication - authentication is the act of identifying who a user is. In Web applications,
this is typically done by having the user provide some credentials, such as a username and
password.
Authorization - authorization is the act of granting or denying access to a resource based
upon the user attempting to access the resource.

Before we can discuss role-based authorization, we must first think about how we will authenticate our
users. ASP.NET provides three methods for authentication:
1.
2.
3.

Windows Authentication - Useful when working on an intranet, where every user who
needs to be authenticated has a Windows account on the Web server's domain,
Passport Authentication - Uses Microsoft's Passport service, and
Forms Authentication - Prompts the user for a set of credentials (typically username and
password).

This article will focus on using role-based authorization with Forms authentication.
Getting Role-Based Authorization Working - the First Steps
In order to provide role-based authorization, we need some way to model the roles and the users that
participate in these roles. Typically, when using Forms authentication a user is modeled as a row in a

database table. Each user usually contains information pertinent to the Web application, including the
user's credentials (i.e., their username and password).
So, before we can get started at looking at the code necessary for implementing role-based
authorization, we need to provide a data model. Our data model will consist of three related tables:

Users - each record in this table models an individual user. The pertinent fields in this table
include UserID (a unique identifier for each user), Username, and Password.
Groups - each record in this table models a group. The Groups table's pertinent fields are
GroupID and Name. Groups are the classifications to which users can be assigned. For
example, a Web application might have groups like: Administrators, CanEdit, CanDelete,
CanInsert, and so on.

Roles - the Roles table models the many-to-many relationship between Users and Groups.
Specifically, a role maps a user to a group. The Roles table needs only two fields: UserID
and GroupID.

An example database might be as follows:

Roles

Users
UserID Username Password
1

Bob

password1

Scott

password2

Jisun

password3

Sam

password4

John

password5

Groups

UserID GroupID

GroupID

Name

Administrators

CanEdit

CanDelete

CanInsert

In this sample database there are five users and four groups. Bob is in the Administrator role, Scott is
in the CanEdit and CanDelete roles, Jisun is in the CanInsert role, Sam is in the CanDelete and
CanInsert roles, and John is not in any roles.
Determining What Roles a User Is In
The .NET Framework contains an HttpApplication class that represents an ASP.NET Web
application. This class has a number of events that can fire during various times of the Web
application's lifecycle. The invent we're interested in is the AuthenticateRequest event, which fires
each time when the ASP.NET security module has established the identity of a user. We can write an
event handler for this event in the Global.asax file. In this event handler we need to determine
what roles this user belongs to.
The following shows the code for the AuthenticateRequest event handler:

Sub Application_AuthenticateRequest(sender As Object, e As EventArgs)


If Request.IsAuthenticated Then
'Determine this user's roles
Dim reader As SqlDataReader = _
SqlHelper.ExecuteReader(connection string, _
CommandType.StoredProcedure, "rolesForUser", _
New SqlParameter("@Username", User.Identity.Name))
' Create an array of role names

Dim roleList As New ArrayList


Do While reader.Read()
roleList.Add(reader("Name"))
Loop
'Convert the roleList ArrayList to a String array
Dim roleListArray As String() = roleList.ToArray(GetType(String))
'Add the roles to the User Principal
HttpContext.Current.User = _
New GenericPrincipal(User.Identity, roleListArray)
End If
End Sub
Let's examine this code sample line-by-line. The first line checks to see if the user is an authenticated
user. That is, if we are dealing with an anonymous Web user, there's no point in hitting the database to
determine what roles the anonymous user belongs to. By the data model I presented earlier, only
users that exist in the database can be assigned roles.
If the user has been authenticated, then the Request.IsAuthenticated property will return True,
and the code inside the If statement will execute. Next, the code uses the Microsoft Data Access
Application Block to execute the stored procedure titled rolesForUser, passing in the authenticated
user's name as the value for the @Username parameter. (If you are unfamiliar with the MS Data
Access Application Block, it is a free assembly from Microsoft that can be used to encapsulate a lot of
the extra complexity involved in making database calls.
The results of the stored procedure - whose syntax we'll examine shortly - are then inserted into an
ArrayList. The ArrayList is then converted into a String array using the ToArray() method. Finally,
the current user's User context is reassigned to a new GenericPrincipal instance with the user's
existing Identity and the set of roles. (Don't worry if you do not yet understand the syntax here or
the purpose of the code - we'll discuss this last line of code in more detail in the next section.)
The rolesForUser stored procedure is fairly straightforward - it simply takes in a single parameter
(the username to search on) and then returns the list of roles the user belongs to.

CREATE PROCEDURE rolesForUser


(
@Username
varchar(50)
)
AS
SELECT G.Name
FROM Roles R
INNER JOIN Groups G ON
R.GroupID = G.GroupID
INNER JOIN Users U ON
R.UserID = U.UserID AND U.Username = @Username
In Part 2 we'll examine the code for this event handler in more detail, specifically examining the
HttpContext class and Principals, Roles, Groups, and Users!

Role-Based Authorization With Forms


Authentication, Part 2
In Part 1 we examined a data model to provide role-based authorization. We also saw how in order to
assign roles to an authenticated user, we needed to create an event handler for the Application's
AuthenticateRequest event. In this part we'll continue our examination of this event handler.
The HttpContext Class
The HttpContext class encapsulates HTTP-specific information about an HTTP request. This includes
response/request headers, server variables, session variables, user information, and so on. Of
particular interest is a property called Current, which returns an instance of the HttpContext class
that represents the current HTTP request.
Another important property in the HttpContext class is User, which contains security information
about the user who made the Web request. The User property returns a type the implements the
IPrincipal interface, which is our topic of discussion in the following section...
Principals, Roles, and Identities, Oh My!
If you've done any administering of Windows you will be familiar with the concept of Users and
Groups. Basically, to access a secured network you must have a user account and that account would
be assigned to one or many groups. In .NET these are referred to as Identities and Roles respectively,
and they are contained within a Principal object. To understand how role based authorization works
you need to have an understanding of how these three elements are tied together, and how you can
programmatically access their values. Let's look at each element separately.
Identities
Identities represent users, and as such, have properties that allow you to obtain information (such as
the username) about that user. The classes for working with Identities reside in the
System.Security.Principal namespace. This namespace contains two classes:
GenericIdentity and WindowsIdentity through which you can determine the properties of a
user; and one interface: IIdentity that you can use to create custom Identities that can extend the
base Identity type to suit needs that are specific to your application.
The IIdentity interface contains three properties, the two more interesting ones for this article
being Name and IsAuthenticated. This means you can access the name of the authenticated user
using User.Identity.Name. (Note that with Forms authentication, the value of
User.Identity.Name is the same value as specified in the name parameter of the
FormsAuthentication.RedirectFromLoginPage() method.)
Roles
Roles are simply a comma-delimited String of role names that are added to the Principal to associate
the current user with one or more roles.
Principals
A Principal contains information about the identity and role(s) that the current user is associated with.
It is through the Principal that you are able to check the role membership of the current user. In many
ways a Principal is the glue that binds identities, roles, and the various other pieces of information that
fully describe that Principal to the application.
A Principal is encapsulated by classes found in the System.Security.Principal namespace. This
namespace contains two Classes: GenericPrincipal and WindowsPrincipal through which you
can determine the properties of a principal; and one interface IPrincipal that you can use to define
your own custom Principals.

The .NET runtime uses the Principal that is attached to the current thread to gain information about
the identity and roles of a user when handling requests that require authorization. To
programmatically assign your own principal settings you simply create an instance of the Principal
class passing in an identity object and a comma delimited string of roles for that identity. The
constructor for the Principal object looks like this:

Public Sub New( _


ByVal identity As IIdentity, _
ByVal roles() As String _
)
Putting It All Together
Now that we've had our discussion on Principals, Roles, HttpContext, and Identities, hopefully the
last line of code in the AuthenticateRequest event handler looks a little more sensible -

HttpContext.Current.User = New GenericPrincipal(User.Identity, roleListArray).


Here, we are assigning to the current HTTP request's User principal a new GenericPrincipal build
up from the user's existing principal plus the set of roles obtained from the database.
Role-Based Authorization
There are a number of ways to perform role-based authorization, once you have specified the user's
roles. One of the more common approaches is to use the <authorization> element in the
Web.config file. With this element, you can restrict or grant access to the Web application based on
roles. For example, imagine that you wanted to allow only those playing the Administrator or CanEdit
roles to a particular Web application or directory in a Web application. To do so, you would simply need
to add the following line in your directory's Web.config file:

<authorization>
<allow roles="Administrator,CanEdit" />
<deny users="*" />
</authorization>
This allows those who are in the Administrator or CanEdit roles, while denying all other users.
Membership in roles can also be checked programmatically.
Membership into roles can also be done programmatically using the IsInRole() method of the User
object. For example, you might have an ASP.NET Web page that you wanted to allow everyone to
access, but the amount of details displayed depended upon the user's role. Here is some pseudocode
that implements such functionality:

If User.IsInRole("Administrator") then
' Display sensitive material
ElseIf User.IsInRole("ModerateInfo") then
' Display moderately sensitive material
Else
' Display only bland material
End If
Conclusion
As we saw in this article, implementing role-base authorization in a Forms authenticated environment
requires that we determine the roles a user belongs to in the Application's AuthenticationRequest
event's event handler. Once we have assigned the roles to a user's principal, we can grant or deny
access to various resources either through the <authorization> element in the Web.config file, or
by programmatic means via the IsInRole() method.

If this topic interests you and you plan on implementing it on your Web site, I would first encourage
you to read with a keen eye an excellent publication from Microsoft: Building Secure ASP.NET
Applications: Authentication, Authorization, and Secure Communication. This "best practices" paper
discusses in great detail the IIS and ASP.NET security models, and various techniques for
authentication and authorization. Also worth checking out is How To: Create GenericPrincipal Objects
with Forms Authentication. This article looks at using role-based authorization with Forms
authentication, but instead of hitting the database at each AuthenticationRequest event firing,
this other approach stores the authentication information in an encrypted cookie.

Dissecting Forms Authentication


Introduction
If you've ever used ASP.NET to create a website that requires that users login to view certain pages or
to have access to particular features, then you've no doubt examined ASP.NET's forms-based
authentication scheme. ASP.NET's forms-based authentication allows you to quickly and easily build a
website that authenticates users through a forms-based approach. Namely, to identify themselves, a
user will enter their credentials in a Web Form. (Credentials could be anything, really, but for websites
they are typically just a username and password. However, some websites require more involved
credentials, such as a username, password, and PIN.) After a user enters their credentials, they are
"logged in" to the site.
With this article we will not be looking at how to implement forms-based authentication; rather, we will
peel back the layers of forms-based authentication and examine what's really happening when a user
is "authenticated." If you've ever wondered how, exactly, a user's authentication status is remembered
as they visit various pages on the site, or how ASP.NET protects against nefarious users from
circumventing the authentication process and "faking" a successful login without valid credentials,
read on to learn more!
The Forms-Based Authentication Workflow
Before we begin our dissection of ASP.NET's forms-based authentication, let's take a brief moment to
examine the forms-based authentication workflow from both the end user's perspective and the page
developer's perspective. While reading the end user and page developer point of views be sure to keep
in mind that authentication is the process of identifying a user, nothing more, nothing less. So formsbased authentication is simply identifying a visitor and doing so using a forms-based mechanism.
(That is, the user enters their identifying credentials through a form on a web page.)
From the end user's point of view, when visiting a site they are typically allowed to login by entering
their name and password. Alternatively, if they attempt to visit a page that they need to be
authenticated in order to access, they'll automatically be sent to the login page. At the login page, our
end user will enter her credentials - typically a username and password - and click a button. Assuming
their credentials are valid, they'll then be "logged on" to the site. Once our end user is logged on, she
can visit any number of pages on the site and be remembered - that is, she need not re-enter her
credentials unless she's closed her browser or explicitly logged out. (Some financial sites or sites that
are commonly visited through public terminals will automatically log out a user after a period of
inactivity.)
From our view as the page developer, implementing forms-based authentication requires setting up
the Web.config so that in the <authentication> setting the <forms> element is used. This
<forms> element contains information as to the URL of the login page along with information on the
user's authentication ticket (more on this later). For a complete listing of properties that can be set
through the <forms> element refer to the technical documentation. In addition to the Web.config
settings, we'll also need to create a login page. This login page needs to prompt the user for their
credentials and, on postback, verify if the credentials are valid. If the credentials are indeed valid, we
"log on" the user by calling the FormsAuthentication.SetAuthCookie() method or the
FormsAuthentication.RedirectFromLoginPage() method. (The SetAuthCookie() method is

what actually logs on the user to the site. if your site is using authorization and a user attempts to
visit a page they are not authorized to visit, they'll be automatically sent to the login page. Once
they've provided their credentials, calling RedirectFromLoginPage() will log them in by calling
SetAuthCookie(); it then redirects the user to the page they were attempting to visit before being
automatically rerouted to the login page.)
This workflow is one both end users and most page developers are familiar with. What it fails to
answer, however, is how, exactly, is a user "logged in?" As a user bumps from page to page in the site,
how does the site remember that the user is, indeed, "logged in?".
Remembering That a User is Logged In
There are a variety of techniques for maintaining state on a website - querystring parameters, POST
parameters, cookies, session variables, and the web server's cache are the most common techniques.
With forms-based authentication we clearly need some state management in that the user's "logged
on" status must be remembered across multiple page visits. We don't want to force the user to have
to re-login every time she visits a new page on our site.
So how can we remember this data? One approach, and an approach used commonly in the days of
classic ASP, would be to use a session variable that would indicate whether or not a user had
successfully logged in. While this approach definitely works, one downside is that this login data
cannot be persisted across the length of a session. That is, with session variables the state is lost once
a user's session expires, which happens when they close their browser or are inactive for a duration
greater than the site's session timeout. But many sites that utilize forms-based authentication allow
the end user to check a 'Remember Me' checkbox that allows the user to login just once at their home
computer and not need to re-enter their login credentials, even days later after having shut down their
computer. In fact, you can configure forms-based authentication to work in this manner. Both the
SetAuthCookie() and RedirectFromLoginPage() have as their second parameter a Boolean that
indicates whether or not the user's "logged in" state should persist even after the user's session has
ended.
Clearly session variables are not at play with forms-based authentication; instead, cookies are used,
which might have been obvious enough given the fact that the method to login a user
(SetAuthCookie()) has the word 'Cookie' right in it! Cookies are small text files saved on the
client's computer that are sent to the corresponding website with each request. Ok, so at this point we
know we will be using cookies to store authentication information, but this still leaves us with two
questions:
1.
2.

What state do we need to maintain in this cookie, and


How can this state be saved so that it can't be read by nefarious users and can't be spoofed
(that is, we don't want to allow someone to fake us out and be able to simulate the state that
indicates that they're logged in).

When determining what information we need to persist across page visits, we're really asking two
questions: the first is, what information must we have to ensure that a user is, indeed, authenticated;
the second is, what information do we need to correctly identify that, yes, this is user X? A naive
approach to answering these two questions is, "Well, I'll simply store the user's credentials in the
cookie." On each page request we could take the user's credentials passed down in the cookie and
determine if they are valid, just like we did on the login page. Additionally, with their credentials handy
we certainly can identify the user - we have their username and password, after all!
When storing authentication information in a cookie, you have to be careful because this cookie data is
passed over the wire in plain-text (unless your site uses SSL). Additionally, the cookie's contents
reside as an unencrypted text file on the end user's computer. If you stored a user's credentials in a
cookie, anyone who could access this computer or was sniffing the network traffic could easily peer
into the cookie and learn the user's username and password.

Ok, so if we don't want to store the user's credentials in a cookie, then what will we store? At
minimum we need to store the logged in user's username, so we can identify who this authenticated
user is. But if we just store this authenticated user's username in plain-text, the site can be easily
compromised by a hacker. The problem with this plain-text approach is that a nefarious user can
"bake" his own cookies - after all, the cookie's contents is a file on the client computer. So what's to
stop a hacker from creating his own cookie that has any ol' person's username in it? Once our hacker
has done this, they can log in as anyone they like!
Clearly we need a better approach, one that can include the user's username but cannot be
compromised or spoofed by hackers.
An Authentication Ticket - Encrypted and Verified for Your Convenience
ASP.NET's forms-based authentication answers the two questions posed earlier by using an
authentication ticket. An authentication ticket is like a paper stub that provides your authentication
information. This ticket is persisted across page requests by storing it in a cookie. Answering the first
question - what state is maintained to identify the authenticated user - the authentication ticket,
which is modeled by the FormsAuthenticationTicket class, has properties that spell out when the
ticket expires, if the ticket is persistent, the authenticated user's username, the date/time the
authentication was issued, and a place for customized data that you, as the page developer, can
optionally insert.
Answering the second question - how is this information protected from inspection or spoofing ASP.NET, by default, both encrypts and validates the authentication ticket. That is, the contents of the
authentication ticket cookie are encrypted when the cookie is written to the client and, each time its
sent to the server, is decrypted back and inspected. The authentication ticket is also validated by
default. Validation is accomplished by a MAC, or Machine Authentication Check. This is accomplished
by computing a digest hash of the concatenation of the contents of the authentication ticket and
tacking on a secret key that is known only to the web server. This hashed value is then appended to
the authentication ticket. Upon receiving the authentication ticket from the client the hash can be
recomputed and compared against the hash sent by the client. If these two hashes match up, the
authentication ticket data has not been modified by any client or in transit; if they do not, something's
gone awry and the authentication ticket is considered invalid. This MAC protects both against a
nefarious user modifying the cookie or that user "baking" their own cookie.

A Bit About Hashes


A hash function is a one-way function that takes some input and quickly computes a digest
of the input. Given the digest and the hash function it is impossible to determine the initial
value plugged into the hash function. For example, consider the hash function h where
h(x) = right-most digit of x. With this example, h(10) = 0, h(45) = 5, and h(14245) = 5.
Even knowing h and given the output of h(x) you can't determine the value of x.
Hashes are commonly used as digital signatures, providing a mechanism to ensure that
the contents of some message haven't been modified by some rogue agent or in transit.
Hashes can also be used to add an extra level of security when storing passwords.
You can customize the level of protection for the authentication ticket. The default is to apply both
encryption and validation, but you can use neither of these techniques (dangerous!), just encryption,
or just validation. Additionally, you can customize what encryption algorithm is used. Additionally, if
you are hosting in a web farm scenario you'll need to explicitly specify the secret key used by the web
server when encrypting the data and when applying the MAC, because you'll want all servers in the
farm to use the same keys. All of these settings can be configured through the <machineKey>
element.
Conclusion
In this article we looked at ASP.NET's forms-based authentication in greater detail. Specifically, we saw
how forms-authentication "remembers" that a user is logged in across visits to the website's pages -

namely, it uses an authentication ticket stored in a cookie. Working with cookies in this manner can
introduce a number of security concerns if not done correctly. To address these issues, the ASP.NET
authentication ticket is, by default, both encrypted and digitally signed to ensure that the contents
haven't been tampered with. Hopefully this article has provided a bit more information on the internals
of forms-based authentication!

You might also like