When `PostAuthenticateRequest` gets execute?
This is my Global.asax.cs
file:
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
...
}
protected void Application_Start()
{
this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
}
// This method never called by requests...
protected void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
var identity = new GenericIdentity(authTicket.Name, "Forms");
var principal = new GenericPrincipal(identity, new string[] { });
Context.User = principal;
}
}
}
When PostAuthenticateRequest
gets execute?
According to the documentation:
Occurs when a security module has established the identity of the user.
...
The PostAuthenticateRequest event is raised after the AuthenticateRequest event has occurred. Functionality that subscribes to the PostAuthenticateRequest event can access any data that is processed by the PostAuthenticateRequest.
And here's the ASP.NET Page Life Cycle.
But because your question is tagged with ASP.NET MVC I would strongly recommend you performing this into a custom [Authorize]
attribute instead of using this event. Example:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
var authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
var identity = new GenericIdentity(authTicket.Name, "Forms");
var principal = new GenericPrincipal(identity, new string[] { });
httpContext.User = principal;
}
}
return isAuthorized;
}
}
Now decorate your controllers/actions with the [MyAuthorize]
attribute:
[MyAuthorize]
public ActionResult Foo()
{
// if you got here the User property will be the custom
// principal you injected in the authorize attribute
...
}
If you place your code on PostAuthenticateRequest you may get hit many times per request as every resource such as images and style sheets referenced on your page will trigger this event as they are treated as separate requests.
If you go with @Darin's answer, the AuthorizeAttribute won't render the action when isAuthorized returns false, but people may need it to be rendered anyway, even if its a public page (unrestricted access) you may want to show a "Display Name" saved on the userData part of the authTicket.
For that, I recommend loading the authCookie on an ActionFilterAttribute (AuthenticationFilter):
public class LoadCustomAuthTicket : ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
if (!filterContext.Principal.Identity.IsAuthenticated)
return;
HttpCookie authCookie = filterContext.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null)
return;
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
var identity = new GenericIdentity(authTicket.Name, "Forms");
var principal = new GenericPrincipal(identity, new string[] { });
// Make sure the Principal's are in sync. see: https://www.hanselman.com/blog/SystemThreadingThreadCurrentPrincipalVsSystemWebHttpContextCurrentUserOrWhyFormsAuthenticationCanBeSubtle.aspx
filterContext.Principal = filterContext.HttpContext.User = System.Threading.Thread.CurrentPrincipal = principal;
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
//This method is responsible for validating the current principal and permitting the execution of the current action/request.
//Here you should validate if the current principle is valid / permitted to invoke the current action. (However I would place this logic to an authorization filter)
//filterContext.Result = new RedirectToRouteResult("CustomErrorPage",null);
}
}
And on global.asax.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LoadCustomAuthTicket());
}
That way you also won't have to populate all your actions with the attribute.