views:

482

answers:

2

What's the best way to handle an expired password in an ASP.NET MVC application?

Let me explain - ASP.NET MVC is obviously set up (both in the barebones app the NerdDinner example) to handle the following scenarios:

  • Register new users
  • Allow them to change their password
  • Log in using a valid account/password

What it doesn't have is a really good way to do the following:

  • Force the user to change their password if it is expired

The ASP.NET MVC way of thinking points to the idea of having the user go to a separate URL/view to perform the password changes.

The problem with this idea is that I don't want people to be able to go to this URL if they're not logged in, and I don't want them to be able to go anywhere else in the site with an expired password.

In the past the way I've handled this is to have the user not leave the login page and have an ASP.NET panel show itself with the "oh hey you need to change your password" bit, and hide the rest of the page. At this point the user is not logged on yet, so they won't be authenticated and can't go anywhere until they change their password.

But ASP.NET MVC makes this difficult. If I do like above and have everything on the login page then I have to have a very cumbersome Login() action in order to handle all of the possible posted values. If I have it post to another action/view then I run the risk of either having to log in the user or have the change password page be not protected by authentication (since, unlike the "change password" bit you get provided with, I don't want them to be authenticated when they see the page).

I can envision a few scenarios wherein you would set something in ViewData to indicate the password is expired and insist on redirecting the user to the "Change Password" page, but I'm not sure if that's a safe thing to do.

+2  A: 

I would consider using a custom (extending the existing) AuthorizeFilter that sets the ActionResult on the AuthorizationContext to redirect to your change password action if the user is authenticated but the password is expired. This would allow them to login normally, but restrict them to only that action if their password is expired. I use a similar approach in one of my apps that redirects a person to an event enrollment page if they are registered with the site but haven't signed up for an event yet (it's a charity event management app).

You might even be able to implement it as a separate filter and still use the existing one for authorization.

 [Authorize]
 [RequiresUnexpiredPassword]
 public class MyController : Controller
 {
     ...
 }

Of course, you'd have to make sure the ChangePassword action is allowed to proceed without being redirected by the filter.

tvanfosson
I think it's not a good idea to create RequiresUnexpiredPassword attribute because you will have to apply this attribute to ALL the controllers [Assuming that author doesn't want user to browse the application with expired password]. It's better to modify the behavior of the Authorize attribute itself.
SolutionYogi
You have to remember to decorate the methods/controllers with the Authorize attribute as well. As I said, you could either extend the authorize filter OR have a separate attribute. A separate attribute would be in keeping with the single responsibility principle and also be flexible. In this case, however, I'd probably go with my first suggestion -- which is also your suggestion minus the exception -- and use a custom authorize filter.
tvanfosson
Yes, but Authorize attribute is already in place so that's a moot point.
SolutionYogi
I implemented this solution - full details on how to do it here: http://zootfroot.blogspot.com/2010/08/aspnet-mvc-2-force-password-change.html
James McCormack
+2  A: 

How about creating a custom AuthorizationAttribute and overriding the OnAuthorization method [ Sample code here: http://stackoverflow.com/questions/554094/asp-net-mvc-adding-to-the-authorize-attribute ] .

In that method, you can check if the password has expired, throw PasswordExpiredException. Catch this exception in Base Controller and redirect the user to 'Change Password' action.

SolutionYogi
Why throw an exception when you can just set the AuthorizeContext result property and do the redirection?
tvanfosson