When `PostAuthenticateRequest` gets execute?

17,161

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.

Share:
17,161
Jalal
Author by

Jalal

My Blog: https://jalalamini.ir/

Updated on August 07, 2022

Comments

  • Jalal
    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
    Andrew Savinykh over 12 years
    Why 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
    Scott Coates about 12 years
    For this example, it would probably make more sense to replace var identity = new GenericIdentity(authTicket.Name, "Forms"); with var identity = new FormsIdentity(authTicket);
  • Mark
    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
    Murali Murugesan over 11 years
    I 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
    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
    Andrew Hoffman about 9 years
    Would this action filter step on the toes of a custom authorize attribute if both are applied to an action?
  • Andrew Hoffman
    Andrew Hoffman about 9 years
    Also, 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
    Andrew Hoffman about 9 years
    Found some relevant and interesting order of operation info. stackoverflow.com/questions/6561883/…
  • Joao Leme
    Joao Leme about 7 years
    I 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.