Search

Using ASP.NET to Make a Secure Site and Move Login Processing Out of the Page

0 views

Configuring Forms Authentication in Your ASP.NET Project

When you start a new ASP.NET Web Forms application, the default authentication mode is Windows. That means the server trusts IIS and the underlying operating system to verify user identities, and all requests that aren’t authenticated are denied automatically. For most web sites that need a custom login page and database‑backed user store, the Windows default isn’t useful. Switching to Forms authentication gives you full control over how and where users log in, while still allowing you to protect specific pages or directories with minimal configuration.

The first step is to open the Web.config file that lives in the root of your application. Inside the <configuration> element you’ll find the <system.web> section. Look for a line that looks like this:

Prompt
<authentication mode="Windows" />
Replace it with the following snippet. The loginUrl attribute points to the page that will handle user sign‑in, and the timeout value controls how long the authentication ticket is valid before the user is forced to log in again. The deny users="?" rule forces all anonymous requests to be redirected to the login page, making the rest of the site effectively secure by default.

Prompt
<system.web></p> <p> <authentication mode="Forms"></p> <p> <forms loginUrl="Login.aspx" timeout="30" /></p> <p> </authentication></p> <p> <authorization></p> <p> <deny users="?" /></p> <p> </authorization></p> <p></system.web>
After you save Web.config, any page that a user attempts to access without being authenticated will automatically redirect to Login.aspx. Once the user logs in successfully, the FormsAuthentication module will set an authentication cookie that the browser presents on subsequent requests. The cookie is encrypted and signed, so tampering is effectively prevented.

In many applications you need a mix of protected and public content. For example, you might want to expose a contact form or a public gallery while keeping the rest of the site behind a login. You can override the default deny rule on a per‑resource basis using the <location> element. Add a block after the <system.web> section, still inside <configuration>:

Prompt
<location path="PublicPage.aspx"></p> <p> <system.web></p> <p> <authorization></p> <p> <allow users="*" /></p> <p> </authorization></p> <p> </system.web></p> <p></location></p> <p><location path="PublicFolder"></p> <p></location>
The first block allows anyone - authenticated or not - to view PublicPage.aspx. The second block grants the same permission to every resource inside PublicFolder. Notice that the path attribute contains no leading slash and no wildcards; it is the simple relative name that ASP.NET uses when matching resources. If you need to protect only a specific directory, make sure that directory contains a Web.config that does not override the global authorization settings, or use the location block as shown above.

One nuance worth noting is that the order of <location> elements matters. The first matching rule wins, so place more specific rules before more general ones. If you later decide to add a new public page, just insert a new location block before the generic deny rule. This keeps the configuration readable and ensures that only the resources you explicitly mark as public remain unprotected.

After the configuration changes are in place, build the project and run it. Navigating to any protected page will bring you to Login.aspx. The redirection logic is handled entirely by ASP.NET, so you don’t need to add code to each page to check authentication status. This small tweak - removing the Windows line and adding the Forms section - enables a consistent, secure baseline for your entire application while still giving you the flexibility to expose individual pages or folders as needed.

Creating a Reusable Login Processor in C#

With authentication turned on, the next logical step is to build the login page itself. You want a clean, maintainable solution that separates user interface code from business logic. ASP.NET Web Forms makes that separation straightforward: put the form controls in the markup, and keep the processing code in a dedicated class or set of classes. This keeps your .aspx files lean and improves testability.

Begin by adding a new Web Form named Login.aspx. In the designer or directly in the markup, add the following controls inside a <form> element that uses runat="server". Use the built‑in TextBox and Button controls rather than raw HTML; the server controls expose convenient properties like Text and events like Click

Prompt
<asp:TextBox ID="txtUser" runat="server" placeholder="Username" CssClass="input" /></p> <p><asp:TextBox ID="txtPassword" runat="server" placeholder="Password" TextMode="Password" CssClass="input" /></p> <p><asp:Button ID="btnLogin" runat="server" Text="Log In" OnClick="btnLogin_Click" CssClass="btn" /></p> <p><asp:Label ID="lblError" runat="server" ForeColor="Red" Visible="false" />
The OnClick attribute wires up the btnLogin_Click event handler in the code‑behind file. Open Login.aspx.cs and implement the handler as follows:

Prompt
protected void btnLogin_Click(object sender, EventArgs e)</p> <p>{</p> <p> string username = txtUser.Text.Trim();</p> <p> string password = txtPassword.Text.Trim();</p> <p> bool isValid = AuthService.ValidateUser(username, password);</p> <p> if (isValid)</p> <p> {</p> <p> // Persist the auth cookie and redirect to the originally requested page</p> <p> FormsAuthentication.SetAuthCookie(username, false);</p> <p> string redirectUrl = FormsAuthentication.GetRedirectUrl(username, false);</p> <p> Response.Redirect(redirectUrl);</p> <p> }</p> <p> else</p> <p> {</p> <p> lblError.Text = "Invalid username or password.";</p> <p> lblError.Visible = true;</p> <p> }</p> <p>}
The key piece is AuthService.ValidateUser. Instead of writing raw SQL directly inside the event handler, we delegate responsibility to a static helper class. This keeps the login logic isolated from the UI and allows the same method to be reused by any part of the application that needs to authenticate a user - for instance, a custom login control or a scheduled job that verifies a username and password pair.

Below is a simple implementation of AuthService. It uses ADO.NET to query a Users table that contains columns Username and HashedPassword. In production you would use salted hashing (e.g., PBKDF2 or BCrypt) and parameterized queries to guard against SQL injection, but the skeleton below illustrates the core idea:

Prompt
public static class AuthService</p> <p>{</p> <p> private static readonly string ConnectionString =</p> <p> System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;</p> <p> public static bool ValidateUser(string username, string password)</p> <p> {</p> <p> using (SqlConnection conn = new SqlConnection(ConnectionString))</p> <p> {</p> <p> conn.Open();</p> <p> string sql = "SELECT HashedPassword FROM Users WHERE Username = @username";</p> <p> using (SqlCommand cmd = new SqlCommand(sql, conn))</p> <p> {</p> <p> cmd.Parameters.AddWithValue("@username", username);</p> <p> object result = cmd.ExecuteScalar();</p> <p> if (result == null) return false;</p> <p> string storedHash = result.ToString();</p> <p> // Assume a method VerifyPassword that compares the input password</p> <p> // with the stored hash and returns true if they match.</p> <p> return VerifyPassword(password, storedHash);</p> <p> }</p> <p> }</p> <p> }</p> <p> private static bool VerifyPassword(string password, string storedHash)</p> <p> {</p> <p> // Placeholder: replace with a proper hash comparison</p> <p> return Crypto.VerifyHashedPassword(storedHash, password);</p> <p> }</p> <p>}
Notice that AuthService relies on a connection string named DefaultConnection defined in Web.config. By externalizing the database details you can swap data stores without touching the logic. If you need to add more sophisticated authentication - such as multi‑factor or social login - simply extend AuthService without touching the UI code.

In some scenarios you may want to expose Session or Response objects to the helper. For example, if you need to store a user ID in Session after successful authentication, you can modify ValidateUser to accept those objects as parameters:

Prompt
public static bool ValidateUser(string username, string password, HttpSessionState session, HttpResponse response)</p> <p>{</p> <p> // Authentication logic here</p> <p> if (isAuthenticated)</p> <p> {</p> <p> session["UserID"] = userId;</p> <p> // Optional: set a custom cookie or modify the response headers</p> <p> response.Headers.Add("X-Auth-User", username);</p> <p> return true;</p> <p> }</p> <p> return false;</p> <p>}
Passing the Session and Response objects is safer than trying to access them directly from a static class, because it keeps the class free from a global dependency on HttpContext.Current. The caller owns the context and can decide how to handle the session data, which makes the helper easier to unit test.

Finally, once the user is authenticated you can choose between FormsAuthentication.SetAuthCookie and FormsAuthentication.RedirectFromLoginPage. The former simply issues a cookie; the latter automatically redirects to the page that the user originally requested, which is useful when you want to preserve the navigation flow. In the example above, GetRedirectUrl fetches the correct target from the authentication ticket before redirecting.

With this architecture in place you have a clean separation between markup, event handling, and authentication logic. The login page remains straightforward, the helper class can evolve independently, and you can reuse the same validation routine wherever you need it. This pattern scales gracefully as you add more complex security features - such as password resets, account lockout, or role‑based access - without cluttering your .aspx files.

Suggest a Correction

Found an error or have a suggestion? Let us know and we'll review it.

Share this article

Comments (0)

Please sign in to leave a comment.

No comments yet. Be the first to comment!

Related Articles