As with the ASP, ColdFusion, and PHP server models, you can take advantage of user authentication using ASP.NET. The downside however, is that Dreamweaver doesn't ship with a suite of User Authentication server behaviors as is the case with the other server models. But before you completely skip over this section in frustration, know that user authentication in ASP.NET is not only robust, but easy to implement. Even better, the .NET Framework facilitates many user authentication tasks that the developer would have to add anywayfeatures such as restricting access to pages before the user has logged in and more are automatically added and handled by the .NET Framework. With that said, I'll use the rest of this chapter to walk you through configuring user authentication for the Dorknozzle site by hand.
In the first half of this chapter, we looked at working with user authentication using ASP, ColdFusion, or PHP. Recall that we set up a form, added form objects including a Submit button, and then included a server behavior to facilitate the logging in of the user. This model is also known as forms authentication. Of course, in ASP.NET, you also have the benefit of working with forms authentication. However, forms authentication isn't the only model available when working with ASP.NET. In general, three models exist, including:
Although these are three great authentication methods, we will focus on one: forms authentication.
Creating the Login Web Form
As we did in the first half of this chapter, the first step to authenticating users is to provide some mechanism for collecting their username and password. That data collection mechanism is the login page. However, in ASP.NET, we can't simply go into the Insert, Form submenu and start adding form objects. Instead, we have to build a web form complete with web controls including two TextBox controls and a Button control. When this is done, we can proceed with forms authentication. To build the Login web form, follow these steps:
Now test the results in the browser by pressing F12. As you'll see, the page in the browser resembles a typical login page.
Working with Forms Authentication
Forms authentication is by far the most popular authentication method because of its flexibility to the user. An advantage of forms authentication is that you can store usernames and passwords in virtually any data store, such as the Web.Config file, XML file, a database, or a combination of the three. Regardless of storage mechanism however, the important process here is how the cookie is stored. When a user logs in, their credentials are checked against a data store. If the credentials are valid, an authentication ticket is serialized and stored as an encrypted cookie on the user's computer. If the credentials are invalid, the user is asked to log in again. Even better, after forms authentication is enabled for an entire directory, pages in that directory cannot be accessed without the proper authentication ticket (cookie). Without the proper ticket, an unauthenticated user is automatically redirected to the original page and forced to log in. Figure 29.14 illustrates this point with more detail.
Figure 29.14. When a user logs in, their credentials are checked against a data store. If the credentials are valid, an authentication ticket is serialized and stored as an encrypted cookie on the user's computer. If the credentials are invalid, the user is asked to log in again.
Because we'll be coding the authentication functionality by hand, it's important that you are familiar with three important classes contained in the System.Web.Security namespace (the namespace that contains the classes used when working with ASP.NET web security):
There are actually four classes contained in the System.Web.Security namespace. The FormsAuthenticationModule is irrelevant to our example and is not covered here.
Now that you have a basic idea of the classes you'll be using for your login page, let's see how a basic login page is constructed. You need to take three steps before forms authentication will work in your application:
Because the first step has been taken care of, let's move directly to the second step. This step involves configuring the authentication mode for the application in the Web.Config file. This can be accomplished by opening the Web.Config file (located in the root folder of your project) and adding the following lines of code:
<system.web> <authentication mode="Forms" /> </system.web>
As you can see, we set the authentication mode to Forms because we're working with forms authentication. We could've however, used the values Windows, Passport, or None if we wanted to work with either Windows or Passport authentication or no authentication at all.
Next, set the authorization mode by adding the following bolded lines of code just below the <authentication mode="Forms" /> tag:
<system.web> <authentication mode="Forms" /> <authorization> <deny users="?" /> </authorization> </system.web>
The result of adding this code should resemble Figure 29.15.
Figure 29.15. Set the authentication mode for the application. Also set the authorization section to deny all anonymous users.
The question mark (?) symbol represents all anonymous users. Essentially, our code is saying, "Deny all anonymous users." If a user tries to access a certain page and does not have the appropriate authentication ticket, the user is redirected back to the login page. Save your Web.Config file and close it. That's it! Forms authentication is enabled and configured for your site. Our next step is to perform the actual authentication using one of the following three methods:
Although we certainly discuss all three methods as the chapter unfolds, for now let's demonstrate ASP.NET forms authentication by simply hard-coding a username and password and authenticating based on those values. You can work with this functionality by following these steps:
In the preceding code, two lines make everything happen:
If (username.Text = "zak" And password.Text = "zak") Then FormsAuthentication.RedirectFromLoginPage(username.Text, False)
The first line checks to see whether the values of the two TextBox controls are "zak" and "zak". If they are correct, the next line is read, which calls the RedirectFromLoginPage() method passing in two parameters. The first parameter is the unique name given to the cookie. Because most usernames are unique, it's the perfect name for the cookie. The second parameter is a Boolean value indicating whether a persistent cookie (as opposed to a non-persistent cookie) should be created. Setting this parameter to TRue guarantees that even when a user closes their browser, the cookie will persist and remain present on the user's computer. When the user fires their browser back up, the login page is bypassed because the cookie still exists. Setting this parameter to False guarantees that the cookie is removed as soon as the user closes their browser. Every time the user closes and reopens their browser, they'll have to log in again.
Now that you have an understanding about how the code works, let's test the functionality. Before you do, however, you need to do one last thing. You need to create the default.aspx page so that you can redirect to a valid document after a successful login. I'll assume that, by now, you're armed with enough knowledge from previous chapters in the book to be able to create that page on your own if it's not created already.
When working with ASP.NET forms authentication, the main page that the login.aspx page redirects to must be called default.aspx. For the most part, there's no way around this requirement.
Press the F12 key to launch the login page in the browser. Enter the values zak and zak in the username and password TextBox controls and click the Log In Button control. You'll immediately be redirected to default.aspx.
To demonstrate the true power of ASP.NET forms authentication, close the browser, reopen it, and type in the path http://localhost/DorknozzleASPX/default.aspx. You are immediately redirected to login.aspx. Remember, the ticket doesn't exist on the computer. Because this is the case, you're automatically redirected to the login.aspx page. How cool is that? The functionality that we would've had to add using the other server models (restricting access based on username and password) is automatically included for us in the ASP.NET server model.
If you are having unsuccessful results with the login page, make sure that you have created an application within IIS for the DorknozzleASPX folder. Because authentication in ASP.NET relies on an application-based model, it does not work if the application has not been created. You can set the DorknozzleASPX site to be an application by opening IIS, selecting the Default Web Sites directory, finding the DorknozzleASPX folder, right-clicking and selecting Properties from the context menu, and clicking the Create button to create an application.
The hard-coded login functionality you have just built is the simplest form you can possibly implement. The next sections enable you to further customize the form and credential-storing means.
Configuring Forms Authentication
In the previous section, you learned how to create a basic login page. You also learned how to modify the Web.Config file by enabling the forms authentication mode. In this section, you explore the forms authentication section in the Web.Config file in greater detail.
Aside from the basic authentication mode, the authentication section in the Web.Config file accepts a form element. The form element accepts the following attributes:
An example of your newly modified Web.Config file could look similar to the following:
<configuration> <system.Web> <authentication mode="Forms"> <forms name=".ASPXAUTH" loginUrl="login.aspx" protection="All" timeout="40" path="/" /> </authentication> </system.Web> </configuration>
Configuring Forms Authorization
As is the case with the authentication section of the Web.Config file, the authorization section can be modified to accept or deny certain users in your application. You can make fine-tuned decisions regarding who will and will not be accepted into your application. For instance, the following code denies all anonymous users except for zak.
<configuration> <system.Web> <authorization> <deny users="?" /> <allow users="zak" /> </authorization> </system.Web> </configuration>
Optionally, you could list out multiple users within the same attribute by simply comma-separating the names as follows:
<configuration> <system.Web> <authorization> <deny users="?" /> <allow users="zak,jessica,makenzie,zaven" /> </authorization> </system.Web> </configuration>
Web.Config File Authentication
The great thing about the Web.Config file is that it is flexible enough to allow you to store usernames and passwords in it. The following code added to the forms element of the Web.Config file sets credentials for the user zak:
<configuration> <system.web> <authentication mode="Forms"> <forms> <credentials passwordFormat="Clear"> <user name="zak" password="zak" /> </credentials> </forms> </authentication> <authorization> <deny users="?" /> </authorization> </system.web> </configuration>
You can now modify the code that lies in the head of your login page to validate the usernames and passwords against the Web.Config file. The result resembles the following:
<script runat="Server"> Sub Authenticate(s As Object, e As EventArgs) If FormsAuthentication.Authenticate(username.Text, password.Text) Then FormsAuthentication.RedirectFromLoginPage(username.Text, False) End If End Sub </script>
Although most of the code is similar to what you added a few sections ago, the following line is different:
If FormsAuthentication.Authenticate(username.Text, password.Text) Then
In this case, we use the Authenticate() method of the FormsAuthentication class, passing in the values of the username and password TextBox controls as parameters. The Authenticate() method is interesting in that it automatically performs the validation against the credentials outlined in the <forms> tag in the Web.Config file.
Trying saving both the Web.Config and login.aspx pages and test the results in the browser by pressing the F12 key. You'll notice that the login functionality is transparent. The difference, of course, is how we're storing the username and password. Rather than hard-coding the username and password in the code of the login.aspx page, we're storing it in the Web.Config file, making it more flexible down the line. If we wanted to, we could add as many users as we needed by simply adding a new <user> tag below or above the existing value as follows:
<forms> <credentials passwordFormat="Clear"> <user name="zak" password="zak" /> <user name="jessica" password="jessica" /> <user name="makenzie" password="makenzie" /> <user name="zaven" password="zaven" /> </credentials> </forms>
Arguably the most flexible method of storing usernames and passwords is through the use of a database table. This section discusses the use of a database table to validate user information. Before you begin, make sure that you modify the Web.Config file back to its original state. The result should resemble the following:
<system.web> <authentication mode="Forms" /> <authorization> <deny users="?" /> </authorization> </system.web>
The next step is to retrieve and validate against the employee's usernames and passwords that are stored in the Employees table of the Dorknozzle database. You can add the following code (at the top of the page before the opening <html> tag) to handle the authentication in the login.aspx file:
<%@ Import Namespace="System.Data.OleDb" %> <script runat="Server"> Sub Authenticate(s As Object, e As EventArgs) If (AuthenticateUser(username.Text, password.Text) = True) Then FormsAuthentication.RedirectFromLoginPage(username.Text, False) End If End Sub Function AuthenticateUser(strUsername As String, strPassword As String) As Boolean Dim objConn As New OleDbConnection _ ("Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=C:\Inetpub\wwwroot\DorknozzleASPX\" & _ "Database\Dorknozzle.mdb") Dim objCmd As New OleDbCommand _ ("SELECT Count(*) FROM Employees " & _ "WHERE Username='" & strUsername & _ "' AND Pass='" & strPassword & "'", objConn) objConn.Open() If (objCmd.ExecuteScalar() >= 1 ) Then Return True Else Return False End If objConn.Close() End Function </script>
Let's pick apart the code to help you better understand how the user is authenticated. Consider this first method:
Sub Authenticate(s As Object, e As EventArgs) If (AuthenticateUser(username.Text, password.Text) = True) Then FormsAuthentication.RedirectFromLoginPage(username.Text, False) End If End Sub
This method processes the AuthenticateUser() function, passing in the two parameters for username and password. The return value, which we'll go over in a bit, is checked to make sure that it is either TRue or False. If the return value is true, the RedirectFromLoginPage() method is called. If it's False, credentials are considered invalid and the user is not permitted into the site.
The AuthenticateUser() function performs most of the work. The function accepts two string parameters, strUsername and strPassword, which come in as the two TextBox control values and has a return type of Boolean:
Function AuthenticateUser(strUsername As String, strPassword As String) As Boolean
What this means is that the code within the function, once executed, must return either true or False. Consider the next lines of code:
Dim objConn As New OleDbConnection _ ("Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=C:\Inetpub\wwwroot\DorknozzleASPX\" & _ "Database\Dorknozzle.mdb") Dim objCmd As New OleDbCommand _ ("SELECT COUNT(*) FROM Employees " & _ "WHERE Username='" & strUsername & _ "' AND Pass='" & strPassword & "'", objConn)
This code instantiates the OleDbConnection and OleDbCommand classes, passing in the connection string and SQL statement respectively. Look at the SQL statement carefully. You'll see we're using the SQL COUNT(*) function to perform and return a count of rows returned from the database when I select a specific username and password (outlined in the WHERE clause) from the table. Technically, the value returned will either be 0 or 1.
Next, we open the connection:
Now we use the ExecuteScalar() method of the Command object to return the numeric value of the returned record count. Remember, this value will either be 0 or 1:
If (objCmd.ExecuteScalar() >= 1 ) Then Return True Else Return False End If
As you can see, we compare the returned numeric value using a conditional. Essentially, we're saying, "if the value is greater to or equal to 1, return true (or the user is authenticated). If the value is less than 1, or 0, the user is not authenticated, so return False."
Finally, we close the connection and end the function:
objConn.Close() End Function
Save your work and test the result in the browser by pressing the F12 key. Enter the values ada and ada (or any other username and password combination stored in the Employees table) in the username and password TextBox controls and click Log In. Again, you should be redirected to the default.aspx page.
Custom Error Messages
You can create custom error messages in the login.aspx page by adding label controls to your page and manually setting the Visible property of those labels to true whenever there is an error message. For example, if a login failure occurred, rather than letting the page reload, you could add a simple Else clause to the If statement that processes the login. To add a custom error message to your login page, follow these steps:
This time, when a user enters the wrong credentials, the user receives an error message rather than a simple page reload similar to Figure 29.18.
Figure 29.18. The user is shown an error message if their credentials are invalid.