1
votes

I'm using the Forms Auth and ASP Universal Membership Provider in an MVC 3 Site. We're persisting the cookie for user convenience.

FormsAuthentication.SetAuthCookie(model.UserName, true)

When we disable a user in the Membership provider like this:

memUser.IsApproved = model.IsActive;
provider.UpdateUser(memUser);

if they have the cookie they can still get in to the site. This is similar to what is described in this post:http://stackoverflow.com/questions/5825273/how-do-you-cancel-someones-persistent-cookie-if-their-membership-is-no-longer-v

We use Authorize attributes on our controllers, and I know that that is technically more Authorize than Authentication. But the certainly overloap so I'm trying to figure out what is the best MVC way to do a check that the user is not actually disabled? Custom AuthorizeAttribute that checks the user against the membership database? An obvious setting/method I'm missing with Forms auth to invalidate the ticket?

Update:

Here’s basically what I'm going with – we use a custom permission denied page which we we use to better inform user that they don’t have rights vs. they’re not logged in. And I added the IsApproved check. AuthorizeCore gets called when you put the attribute on a Controller or Action and if it returns false HandleUnauthorizedRequest is called.

public class CustomAuthorization : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated || !Membership.GetUser(filterContext.HttpContext.User.Identity.Name).IsApproved)
        {
            filterContext.Result = new HttpUnauthorizedResult();

            // in the case that the user was authenticated, meaning he has a ticket, 
            // but is no longer approved we need to sign him out 
            FormsAuthentication.SignOut();
        }
        else
        {
            var permDeniedRouteVals = new System.Web.Routing.RouteValueDictionary() { { "controller", "MyErrorController" }, { "action", "MyPermissionDeniedAction" }, { "area", null } };
            filterContext.Result = new RedirectToRouteResult(permDeniedRouteVals);
        }
    }

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        // since persisting ticket,
        // adding this check for the case that the user is not active in the database any more
        return base.AuthorizeCore(httpContext) && Membership.GetUser(httpContext.User.Identity.Name).IsApproved; 
    }
}

Usage:

[CustomAuthorization()]
public class MyController
2

2 Answers

1
votes

Well, you're going to have to check the database regardless, the only question is how you want to do that. Yes, you could create a custom authorize attribute, or you could write some code for the OnAuthorize override in ControllerBase, or you could do it in Application_AuthenticateRequest.. lots of ways you could do it, depends on what works best for you.

The best way, of course, would be to not use a persistent ticket if this is an issue for you.

1
votes

I pretty much always use Roles and a RolesProvider, even if there is just one role named "Login" - in part for this issue. This way, your Authorize attributes might look something like this:

[Authorize(Roles="Login")]

Where Login represents a basic 'Role' that all "active" accounts must have to be able to log in at all; Every protected action is protected by this, at minimum.

That way, simply removing the "Login" role effectively disables the user... because, in my Roles Provider, I am checking the logged-in user's roles against the database or server-local equivalent.

In your case, your "Login" role could simply resolve to a check on the IsApproved field on your user model.