# IT:AD:ASP.NET:Common:HowTo:HttpModule:FormsAuthenticationModule #
* [[../|(UP)]]
{{indexmenu>.#2|nsort tsort}}
## Notes ##
### Activation ###
Web Authentication is by default one of:
* `WindowsAuthentication` (using [[IT/AD/ASP.NET/Common/HowTo/WindowsAuthenticationModule/]])
* `FormsAuthentication`
* `Passport` (defunct)
* `None` (*which is a bit of a misnomer, as a more appropriate name would have been `NoneOrCustom`*).
You activate FormsAuthentication in the `web.config` file.
### Setting Authentication Mode in Web.Config
The choice of which authentication method is used is determined in `web.config`.
...
...
### Sequence Diagrams
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.
Participant UserAgent
Participant FormsAuthenticationModule
Participant UrlAuthorisationModule
UserAgent -> FormsAuthenticationModule : Request Protected.aspx
FormsAuthenticationModule -> UrlAuthorisationModule : Anonymous Request
UrlAuthorisationModule --> FormsAuthenticationModule : 401 (Unauthorized)
FormsAuthenticationModule --> UserAgent : 302 (redirect to Login.aspx)
UserAgent -> FormsAuthenticationModule : Request Login.aspx
FormsAuthenticationModule -> UrlAuthorisationModule : Anonymous Request
UrlAuthorisationModule -> LoginPage : Authorized
LoginPage -> UserAgent : Render Resource
UserAgent -> FormsAuthenticationModule : Postback Credentials
FormsAuthenticationModule -> UrlAuthorisationModule : Anonymous Request
UrlAuthorisationModule -> LoginPage : Authorized
LoginPage --> UserAgent : 302 (redirect to Protected.aspx, adding Auth Ticket to Cookie)
UserAgent -> FormsAuthenticationModule : Request Protected.aspx
FormsAuthenticationModule -> FormsAuthenticationModule : Attach Identity to Thread
FormsAuthenticationModule -> UrlAuthorisationModule : Authenticated
UrlAuthorisationModule -> Protected.aspx : Authorized
Protected.aspx -> UserAgent : Render Resource
### All Requests
As per [[IT/AD/ASP.NET/Common/HowTo/HttpModule/]], whereas `HttpHandler`s handle a specific registered route, all requests go through all `HttpModule`s.
### Order of Processing
It's important to notice that as per [[IT/AD/ASP.NET/Common/HowTo/HttpModule/HowTo/Order of Execution]], `FormsAuthenticationModule` processes requests before `UrlAuthorisationModule` and `FileAuthorisationModule`.
### Events Handled
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);
}