Search code examples
asp.net-mvc-2authorizeasp.net-authorizationauthorize-attribute

MVC2 AuthorizeAttribute - What's Going On Here?


Background

I have an MVC2 project with forms authentication enabled in the project level web.config file:

<authentication mode="Forms">
    <forms loginUrl="~/Account/LogOn"
           defaultUrl="~/Magic"
           name="MagicAuthCookie"
           slidingExpiration="false"                    
           timeout="10" />
    </authentication>

I have a custom MembershipProvider declared there as well:

<membership defaultProvider="MagicMembershipProvider">
    <providers>
        <clear/>
        <add name="MagicMembershipProvider"
             type="Magic.Web.Areas.Accounts.Providers.MagicMembershipProvider, 
                   Magic.Web"
             applicationName="MagicalMysteryProject"
             connectionStringName="Test_User_and_RolesConnectionString"/>
    </providers>
</membership>

I have an Index Action that is decorated with the [Authorize] attribute:

[Authorize]
public ActionResult Index()
{
    MagicViewModel avm = new MagicViewModel { MagicDate = DateTime.Now.ToString("MM-dd-yyyy") };
    return View(avm);
}

Feelin' The Flow

When I debug the project, I initially get redirected to the ~/Account/LogOn controller/action as specified in the web.config:

    public ActionResult LogOn(string returnUrl)
    {
        if (ModelState.IsValid && MembershipService.ValidateUser(User.Identity.Name, "")){
            *... do some stuff here ...*
        }
    }

NOTE: At this point the "User" property of the controller is Null

After checking ModelState, it goes to my MembershipService, aka AccountModels.ValidateUser(username, password):

    public bool ValidateUser(string userName, string password)
    {
        return _provider.ValidateUser(userName, password);
    }

Which goes to MagicMembershipProvider.ValidateUser(username, password):

public override bool ValidateUser(string username, string password)
{
    return base.ValidateUser(username, password);
}

Which finally goes to the BaseMembershipProvider.ValidateUser(username, password):

public override bool ValidateUser(string username, string password)
{
    string magicUserName = 
        HttpContext.Current.Request.ServerVariables["MAGIC_USER_NAME"];
    return (!magicUserName == "");
}

The server variable is set early on by a custom isapi.dll that intercepts all calls and does authentication. If there is a value there, the user is considered authentic. This method returns a simple Boolean. NO WHERE ELSE IN MY CODE is this variable referenced or passed.

This boolean is passed back up the execution tree (for lack of a better word) to the original LogOn(string returnUrl) method that started it all.

The Kicker

Now, at this point, somehow magically the User property is no longer null, and the User.Identity.Name property is set to the MagicUserName from the Server Variable. I did not do this.

What Happened?

This behavior is not what I want, but I don't know where/how to stop it because I never told the framework to do this. I assume it's somewhere in the default AuthorizeAttribute implementation, but how does it know to use this random server variable that I checked?

Any illumination will be greatly appreciated.


Solution

  • That's how Forms authentication works by default and has nothing to do with ASP.NET MVC. When you create an authentication cookie (FormsAuthentication.SetAuthCookie) to indicate that the user successfully signed this cookie will be used by the forms authentication module (the IPrincipal is set inside the FormsAuthenticationModule.OnAuthenticate method) to populate the User property on subsequent requests. You have the possibility to write a custom Authorize attribute and set the User property to whatever you like. This technique is often used to create custom principals.