Today I had a question from one of our developers about bridging ASP and ASP.NET applications and how to handle the security. What I always recommend for all .NET web applications is that people use what is built-in. Forms authentication works very well, already has security for things like authentication tickets, and it already handles checking authorization by just adding the <authorization> tag in web.config.
Many ASP applications already have the concept of a userID and Roles. In February I presented a session at VS Live that included some sample code that will easily address setting up forms authentication with roles for ASP.NET. Lets jump right into it in a basic walk-through.
#1 Web.config settings. These basic settings tell the framework that all unknown users should be redirected to the Logon.aspx page. If the users are known (authenticated) then they can browse the other pages in the application.
<authentication mode="Forms">
<forms loginUrl="Logon.aspx" name="myAuthCookie" timeout="60" path= "/"></forms>
</authentication>
<authorization>
<deny users="?" />
<allow users="*" /> <!-- Allow all users -->
</authorization>
#2 Logon.aspx. We could have a username and password input form or any kind of input we want to use to validate the user. Since I am talking about a solution here that makes it easy to share authentication between classic ASP and ASP.NET we will leave the ASPX page itself blank and handle the check in code. My recommendation is that the ASP classic application would instead trust the .NET authentication. For that you might simply set an extra cookie from .NET that the ASP classic could read. It really depends on how your ASP application validates users. You would simply want to have the ASP.NET code duplicate the current ASP mechanism so when the user hits ASP pages they do not know any difference. In other words, the ASP code would still check for a specific cookie or something like that.
#2a Logon.aspx.cs. In the code behind the for the authentication page is where all the real work happens. Lets look at it in detail for the given scenario.
private
void Page_Load(object sender, System.EventArgs e)
{
string userID = "";
// Check the ASP classic authentication cookie array or whatever you use in ASP to know the user is logged in
if (!(Request.Cookies("MyApplication") == null))
{
// Assuming here if authenticated then we also have a userID
userID = Request.Cookies("MyApplication").Values.Get("userID")
}
if (userID == "")
{
// unknown user. Send them back to classic ASP login page
Response.Redirect("login.asp");
}
else
{
// We have a userID from the ASP application. Use it to create an authentication ticket
// Get a pipe delimited string of the groups (Roles) that the user belongs to.
// Typical ASP applications store a list of roles in a database table. Hard coded here for sample.
string groups = "Editors|Registered Users|Newsletters";
// Create a forms authentication ticket that we can stuff the userID and Roles into
FormsAuthenticationTicket authTicket =
new FormsAuthenticationTicket(1,
userID,
DateTime.Now,
DateTime.Now.AddMinutes(60),
false, groups);
// Encrypt the ticket.
String encryptedTicket = FormsAuthentication.Encrypt(authTicket);
// Stuff the ticket into a cookie
HttpCookie authCookie =
new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket);
Response.Cookies.Add(authCookie);
// Return the user to the requested page now that an authentication ticket is created
Response.Redirect(FormsAuthentication.GetRedirectUrl(userID, false));
}
}
#3 Global.asax.cs. This is where the magic of .NET forms authentication really happens. If you are not stuffing the roles into the authentication ticket then much of this whole process can be greatly simplified. Putting the Roles into the ticket will give us an easy way to apply them to the current user on each request. Lets take a look.
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if(null == authCookie)
{
// There is no authentication cookie.
return;
}
FormsAuthenticationTicket authTicket =
null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch(Exception ex)
{
// Log exception details (omitted for simplicity)
return;
}
if (null == authTicket)
{
// Cookie failed to decrypt.
return;
}
// When the ticket was created, the UserData property was assigned a
// pipe delimited string of group names.
String[] groups = authTicket.UserData.Split(new char[]{'|'});
// Create an Identity object
GenericIdentity id = new GenericIdentity(authTicket.Name, "TrustedASPAuthentication");
// This principal will flow throughout the request.
GenericPrincipal principal = new GenericPrincipal(id, groups);
// Attach the new principal object to the current HttpContext object
Context.User = principal;
}
That is really about all there is to it. By using the built-in authentication and authorization tools in ASP.NET and stuffing the roles into the ticket and then the current principal your code can leverage the built-in methods for checking Role membership.
User.IsInRole("Editors") would return true for us since "Editors" is one of the roles I hard coded the user to be in.
I hope that someone finds this brief article helpful as an introduction into using Forms authentication and Role authorization in ASP.NET.