it:ad:asp.net:common:howto:httpmodule:formsauthenticationmodule:home

IT:AD:ASP.NET:Common:HowTo:HttpModule:FormsAuthenticationModule

Web Authentication is by default one of:

You activate FormsAuthentication in the web.config file.

The choice of which authentication method is used is determined in web.config.

<configuration>
  <system.web>
    ...
    <authentication mode="Forms">
      <!-- Notes:
             Url is relative, to a page, or handler (if bouncing out to an SSO)
             cookieless should always be set to UseCookies 
             Timeout value is in minutes -->
      <forms cookieless="UseCookies" 
                 loginUrl="/demo/login.ashx" 
                 name="DemoServiceProvider" 
                 timeout="20"/>
    </authentication>
    ...
</configuration>

FormsAuthentication works via a combination of Redirects, Cookies, Identity, Principal, and AnonymousIdentity.

The following sequence diagram will make part of what's going on clearer.

UserAgentUserAgentFormsAuthenticationModuleFormsAuthenticationModuleUrlAuthorisationModuleUrlAuthorisationModuleLoginPageLoginPageProtected.aspxProtected.aspxRequest Protected.aspxAnonymous Request401 (Unauthorized)302 (redirect to Login.aspx)Request Login.aspxAnonymous RequestAuthorizedRender ResourcePostback CredentialsAnonymous RequestAuthorized302 (redirect to Protected.aspx, adding Auth Ticket to Cookie)Request Protected.aspxAttach Identity to ThreadAuthenticatedAuthorizedRender Resource

As per IT:AD:ASP.NET:Common:HowTo:HttpModule, whereas HttpHandlers handle a specific registered route, all requests go through all HttpModules.

It's important to notice that as per Order of Execution, FormsAuthenticationModule processes requests before UrlAuthorisationModule and FileAuthorisationModule.

The Module handles the following two events only if the AuthenticationMode == Forms, and not Windows, etc

(I sort of feel that it should allow for both).

    public void Init(HttpApplication app)
    {
      //pseudocode:
      //exit now if AuthenticationConfig.Mode != AuthenticationMode.Forms;
      
      //otherwise, wire up the event handlers
      app.AuthenticateRequest += new EventHandler(this.OnEnter);
      app.EndRequest += new EventHandler(this.OnLeave);
    }

Concentrating on the AuthenticationRequest event handler first, this is what happens:


    private void OnEnter(object source, EventArgs eventArgs)
    {
      HttpContext context = ((HttpApplication) source).Context;


      //VERY IMPORTANT: 
      //If *any* earlier Module has already created a User at this point, 
      //do NOT change, and get out early:
      if (e.Context.User != null) {return;}
      ...
      // Pseudo code steps:
      // =============
      // Get `FormsAuthenticationTicket` from Cookie (using FormsAuthentication.FormsCookieName):
      // If null/Expired exit;
      // Renew ticket (adjust sliding expiration)
      // Create new FormsIdentity from ticket
      // Create GenericPrincipal from FormsIdentity
      // set e.Context.User
      // create new ticket, encrypt, remove existing, reattach

      //Not sure about this part just yet and no more time tonight:
      if (AuthenticationConfig.AccessingLoginPage(context, FormsAuthentication.LoginUrl))
      {
        context.SetSkipAuthorizationNoDemand(true, false);
        cookielessHelper.RedirectWithDetectionIfRequired((string) null, FormsAuthentication.CookieMode);
      }
      if (context.SkipAuthorization)
        return;
      context.SetSkipAuthorizationNoDemand(AssemblyResourceLoader.IsValidWebResourceRequest(context), false);
    }

OnLeave

    private void OnLeave(object source, EventArgs eventArgs)
    {
    //Get HttpContext
    //If we are not handling a 401 raised by UrlAuthorisationModule or else
    //no need to do anything else (the Response ticket is already set)
      if (context.Response.StatusCode != 401 || context.Response.SuppressFormsAuthenticationRedirect)
        return;
        
     //If looping back, don't redo this.        
      if (!this._fOnEnterCalled)
        return;
      this._fOnEnterCalled = false;

      string rawUrl = context.Request.RawUrl;
      if (rawUrl.IndexOf("?" + FormsAuthentication.ReturnUrlVar + "=", StringComparison.Ordinal) != -1 || rawUrl.IndexOf("&" + FormsAuthentication.ReturnUrlVar + "=", StringComparison.Ordinal) != -1)
        return;
      string strUrl = (string) null;
      if (!string.IsNullOrEmpty(FormsAuthentication.LoginUrl))
        strUrl = AuthenticationConfig.GetCompleteLoginUrl(context, FormsAuthentication.LoginUrl);
      if (strUrl == null || strUrl.Length <= 0)
        throw new HttpException(SR.GetString("Auth_Invalid_Login_Url"));

      CookielessHelperClass cookielessHelper = context.CookielessHelper;
      string str;
      if (strUrl.IndexOf('?') >= 0)
        str = FormsAuthentication.RemoveQueryStringVariableFromUrl(strUrl, FormsAuthentication.ReturnUrlVar) + "&" + FormsAuthentication.ReturnUrlVar + "=" + HttpUtility.UrlEncode(rawUrl, context.Request.ContentEncoding);
      else
        str = strUrl + "?" + FormsAuthentication.ReturnUrlVar + "=" + HttpUtility.UrlEncode(rawUrl, context.Request.ContentEncoding);
      int num = rawUrl.IndexOf('?');
      if (num >= 0 && num < rawUrl.Length - 1)
        str = str + "&" + rawUrl.Substring(num + 1);
      cookielessHelper.SetCookieValue('F', (string) null);
      cookielessHelper.RedirectWithDetectionIfRequired(str, FormsAuthentication.CookieMode);
      context.Response.Redirect(str, false);
    }

  • /home/skysigal/public_html/data/pages/it/ad/asp.net/common/howto/httpmodule/formsauthenticationmodule/home.txt
  • Last modified: 2023/11/04 03:00
  • by 127.0.0.1