When `PostAuthenticateRequest` gets execute?
Solution 1
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
...
}
Solution 2
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.
Comments
-
Jalal almost 2 years
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? -
Andrew Savinykh over 12 yearsWhy are you recommending applying filters on all controllers, when it seems much cleaner to have the change in a single place on the event handler? What is the gain?
-
Scott Coates about 12 yearsFor this example, it would probably make more sense to replace var identity = new GenericIdentity(authTicket.Name, "Forms"); with var identity = new FormsIdentity(authTicket);
-
Mark about 12 years@zespri The PostAuthenticateRequest event may be called many times per page. Using a custom authorize attribute ensures your code will only be called once per request.
-
Murali Murugesan over 11 yearsI tried PostAuthenticate and Windows Authentication approach combined and got the issue stackoverflow.com/questions/14439497/… . Now i am planning to take your advise and implement. I hope it will not make any issue
-
SherleyDev about 10 years@Darin Dimitrov Should I also set Thread.CurrentPrincipal as you did for HttpContext.User? (i.e. System.Threading.Thread.CurrentPrincipal = principal;)
-
Andrew Hoffman about 9 yearsWould this action filter step on the toes of a custom authorize attribute if both are applied to an action?
-
Andrew Hoffman about 9 yearsAlso, would there be a way to ensure that this attribute executed before an authorize attribute? Ideally we'd only load the cookie at one spot, before any attribute executes.
-
Andrew Hoffman about 9 yearsFound some relevant and interesting order of operation info. stackoverflow.com/questions/6561883/…
-
Joao Leme about 7 yearsI just updated changing to AuthenticationFilter to get his first, since after upgrading to MVC5 .net 4.5 the previous code on ActionFilter was not working.