July 11, 2007 - 16:03 UTC - Tags: .NET ASP.NET authentication authorization login
I guess this happens to lots of people because I found a lot of writing about it after searching google, but I didn't find any solution I could fully use. So I created a version that should work in most cases.
The problemYou have created a web application using forms authentication. You have setup a login page (Login.aspx) where the user can log in using his username and password. Once the user is logged in, you display some information and then redirect the user to whatever the returnUrl parameter says. You have also defined Login.aspx in web.config as the login page:
<authentication mode="Forms">
<forms loginUrl="Login.aspx">
</forms>
</authentication>
A user logs in to your site, and tries to access a resource that he/she does not have access to. The ASP.NET framwork redirects the user to Login.aspx to authenticate once again (Login.aspx?returnUrl=some/secure/resource), but Login.aspx sees the user as logged in, and tries to redirect the user back to the returnUrl (some/secure/resource), which again redirects to the login page. We have a loop.
The solutionASP.NET allows you to create HTTP handlers, which are of type System.Web.IHttpHandler. These handlers can handle http-request and send back data or redirect to another URL. HTTP handlers are mapped in web.config. We add an HTTP handler called Login.as
hx to handle our logins:
<location path="Login.ashx">
<system.web>
<httpHandlers>
<add verb="*" path="Login.ashx" type="My.NameSpace.LoginHandler" validate="true" />
</httpHandlers>
</system.web>
</location>
Next we change out login URL to point to the HTTP handler:
<authentication mode="Forms">
<forms loginUrl="Login.ashx">
</forms>
</authentication>
The HTTP handler class is pretty simple. All it has to do is check if the user is logged in or not, and redirect to the proper page. We can also make sure that we access the actual login page over SSL:
namespace My.NameSpace
{
public class LoginHandler : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
context.Response.Redirect("/NotAuthorized.aspx");
}
else
{
string loginUrl = "https://" + context.Request.Url.DnsSafeHost + "/Login.aspx";
if (!string.IsNullOrEmpty(context.Request.Params["returnUrl"]))
{
loginUrl = loginUrl + "?returnUrl=" + HttpUtility.UrlEncode(context.Request.Params["returnUrl"]);
}
context.Response.Redirect(loginUrl);
}
}
public bool IsReusable
{
get { return true; }
}
}
}