ASP.NET MVC - How to handle an expired password? - asp.net-mvc

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.

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.

How about creating a custom AuthorizationAttribute and overriding the OnAuthorization method [ Sample code here: 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.

Related

IdentityServer3: Can it be used "side by side" with existing users/authentication?

I'm new to SSO, so hopefully what I'm asking makes sense. So my current setup is a .NET MVC website using OWIN/cookies (app.UseCookieAuthentication()) and a custom user table (not ASP.NET Identity users).
So I'm wondering if I could add IdentityServer3 only for external providers, but leave all my existing user/authentication stuff as is for "local users". So I see that you can implement a custom IUserService to lookup users against your local database, and I think I got that working, but I'd like to even avoid that. And I'd like to avoid themeing the IdentityServer login screen. So something like this:
User hits up page with [Authorize] attribute.
User is redirected to my existing login page (not IdentityServer stuff)
Then my login page would have the external provider button(s) to login with external providers.
Is that possible? Or do you have to run your local users through IdentityServer3 also? I noticed I get an error if you don't provide a IUserService and don't use UseInMemoryUsers() either.
So from following various guides, I have this in my Startup.cs: app.UseIdentityServer(), app.UseCookieAuthentication(), and app.UseOpenIdConnectAuthentication() with Authority set to my IdentityServer endpoint.
Hopefully that made sense, Thanks!
Gonna answer my own question if it helps anyone else. The important piece here is AuthenticationMode in OpenIdConnectAuthenticationOptions. AuthenticationMode.Active is what will redirect the user to your OIDC provider anytime they hit an action with [Authorize].AuthenticationMode.Passive will allow you to use your OIDC provider as an additional authentication method. You want to follow the examples with ExternalLogin() and ExternalLoginCallback() controller actions that issue challenges to the provider and then match the authenticate user with your local user.

Asp.Net MVC Antiforgery validation fails when non-null usernames differ...is that reasonable?

My question is about the MVC Antiforgery system (described here).
Consider a simple app which posts todos to /Todo/Create. The corresponding action method has the ValidateAntiForgeryToken attribute. Consider the following client workflow:
User A logs on and goes to the page to create a todo, but doesn't do it yet.
User B (physically on the same computer) opens a new tab in the same browser, logs out of User A's account, logs in as User B. The browser then gets User B's validation cookie.
Some time later, User A switches back to their original tab and hits 'create' on the todo they were making.
In this scenario, the Antiforgery verification will not pass because the form token was meant for User A, while the validation cookie is for User B.
I'm sure there are valid security reasons for this behavior (e.g. a script on another site that manages to login as malicious user so that the 'todo' data is posted to their account instead), but it doesn't stop the above scenario happening for my legitimate users sometimes.
My questions are:
Is there a 'best practices' way to handle this scenario? Is it usually just a case of showing a custom error, telling them to reload the page and/or login again etc?
Is there any way to know when the out-of-the-box MVC Antiforgery system runs into this error? It seems to only ever throw the same kind of Exception (HttpAntiForgeryException). Would I need to revert to using/modifying their source?
I see two ways of handling it:
Use Javascript callback to the server before hitting a button to detect if the user is still logged in. If not - display him a message. It should be relatively easy to do this. But it requires one additional call, and little bit more time to execute your request.
One solution to avoid callbacks could be using html 5 localStorage (and you can support that on other browsers using modernizr, for example). It is shared between tabs. But I'm not sure if this approach is good. Additional research required.
Catch HttpAntiForgeryException on the server, check if the user is logged in. If the user is not logged in, display him a message.
Usually approach (1) is used. On banking websites they detect with JavaScript when you logged out in other browser tab.

The same asp.net mvc page for LDAP authenticated and anonymous users

I have some intranet asp.net mvc3 app ,which is available for both LDAP authenticated and anonymous users.All of them are using IE. One of the views should be available for both of them, depending on status some columns should be hidden,etc.
I don't want anonymous users to receive popup authentication dialog,but can't see no way of solving problem.If I add Authorize() attribute ,it forces anonymous user to input user/password, if I remove this attribute everyone is treated as anonymous.
How can be this obstacle solved?
In your controller check to see if the user is authenticated and perform actions based off that conclusion.
if (User.Identity.IsAuthenticated){
//Handle Case
}

When does the .NET FormAuthentication ticket get checked and how do I tap into this event?

We are attempting to integrate an ASP.NET MVC site with our client's SSO system using PingFederate. I would like to use the built in FormsAuthentication framework to do this. The way I've gone about it so far is:
Set up my Web.config so that my FormsAuthentication LoginURL goes to my site's "BeginAuthentication" action on a "Security" controller. From this action, I set up some session variables (what URL was being accessed, for example, since Ping won't send this info back to me), and then redirect to our client's login page on an external site (www.client.com/Login for example).
From here, the authentication takes place and a cookie is generated on the same domain as the one that our application is running on which contains the unique identifier of the authenticated user, I've set it up so that once this happens, the Ping server will redirect to my "EndAuthentication" action on my "Security" controller.
In this action, I call my membership class's "ValidateUser" method which takes this unique identifier from the cookie and loads in the user on our application that this ID refers to. I save that logged in user in our Session (Session["LoggedInAs"], for example) and expire the cookie that contains the id of the authenticated user that the SSO system provided for me.
All of this works well. The issue I'm wondering about is what happens after our user has already authenticated and manually goes back to our client's login page (www.client.com/login) and logs in as another user. If they do that, then the flow from #2 above to number 3 happens as normal - but since there already exists an authenticated user on our site, it seems as though the FormsAuthentication system doesn't bother kicking off anything so I don't get a chance to check for the cookie I'm looking for to login as this new user. What I'd like to do is, somewhere in my Global.asax file (probably FormsAuthenticate_OnAuthenticate), check to see if the cookie that the SSO system sends to me exists, and if so, sign out of the application using FormsAuthentication.SignOut().
Another issue that seems to be related is that if I let my Session expire, the FormsAuthentication still seems to think I am authenticated and it lets me access a page even though no currently logged in user exists in my Session, so the page doesn't render correctly. Should I tap into the Session_End event and do FormsAuthentication.SignOut() here as well?
Basically, I want to know when the authentication ticket created by
System.Web.Security.FormsAuthentication.SetAuthCookie(..) gets checked in the flow of a request so that I can determine whether I need to SignOut() and force revalidation or not.
Thanks for any help. Sorry for the length of this message, trying to be as detailed as possible.
Mustafa
Welcome to the small section of Hades that is mixing session with formsauth.
If your needs are as complex as presented, you would get more sleep if you implement a full provider stack to share amongst the participating sites. Easier said than done, I know.
But to address your question:
from http://www.codeproject.com/Articles/39026/Exploring-Web-config-system-web-httpModules.aspx
On the way in....Check ticket and set identity #
app.AuthenticateRequest += System.Web.Security.FormsAuthenticationModule.OnEnter-->OnAuthenticate
On the way out... set the ticket and redirect as necessary
app.EndRequest += System.Web.Security.FormsAuthenticationModule.OnLeave
Reflector is your friend. ;-)
I don't know about a specific event for when the cookie is checked, but you could place the appropriate logic in Application_BeginRequest() and check the user's authentication state there.
Another issue that seems to be related
is that if I let my Session expire,
the FormsAuthentication still seems to
think I am authenticated and it lets
me access a page even though no
currently logged in user exists in my
Session, so the page doesn't render
correctly.
The life of the cookie (how long until ASP.NET feels it needs to ask for a password again) and how you are managing state are unrelated. The ASP.NET authentication is cookie based so that, should a developer want to, he could turn off viewstate, session, use no query strings or hidden fields and authentication still works.
If you want to tie the interval at which you request the password to how you are persisting data, then you will want your session expiration to be roughly the same as the cookie expiration, but they will never quite match up. It would be better to have two policies (one for how fast you throw away a users session data and one for how long you are willing to wait before you need to reask for a password)

Is the Authorize attribute in ASP .NET MVC used for Authentication as well as Authorization?

I'm reading up on ASP .NET MVC, and I just got to a section talking about the Authorize attribute. It's saying that the Authorize attribute is used to check that a user is authenticated against a Controller. Is this true? I know that the attribute is designed to be used for authorization purposes, but is it also a best practice to use this attribute for authentication?
If not, what is the best practice for verifying (not performing) authentication?
If so, why is it done this way? Am I missing something?
Authorize attribute can be used to check to see whether the user is logged in. It can also be used to check if the user is a member of a specific role and has a specific name.
It essentially does the same thing handled by <authorization> section in web.config when using Web forms.
It doesn't specify the authentication method. It's handled by <authentication> section in web.config just like Web forms.
EDIT (clarification about authentication and authorization):
Authentication is identity verification. That is, you check to see who the user is. This can be performed by checking a user name and password, checking your Windows authentication token, scanning retina, voice identification or whatever else.
Authorization is the act of limiting access to a specific resource to users that satisfy a certain criteria. To be able to authorize a user to a resource, you should know the rights the user have. To check that, you should know who the user is in the first place. So the user have to be authenticated.
Essentially an empty [Authorize] attribute does authorization, not authentication. It doesn't check who you are. It just checks if the one who you verified to be does have access to the resource or not. However, its authorization criteria is "anyone successfully authenticated." You can specify a different criteria. So, indeed it's doing authorization, not authentication.
Authorize does indeed check that the user is authenticated, otherwise it would not be able to determine the roles for the user or which user (other than the anonymous one) the current user is. That is, in order to be authorized, if anonymous access is not allowed, you have to be authenticated first. Below is the relevant snippet from the AuthorizeCore method in the RTM version (from http://www.codeplex.com/aspnet).
// This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method.
protected virtual bool AuthorizeCore(HttpContextBase httpContext) {
if (httpContext == null) {
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated) {
return false;
}
...
If AuthorizeCore returns false in OnAuthorization, then the AuthorizationContext.Result is set to a new HttpUnauthorizedResult which will result in the user being redirected to the login page (in FormsAuthentication) or an error.
EDIT: After reading your comments to other answers, I would say that you ARE missing the point. Technically it is only doing authorization. One level of authorization, the minimum, is that you need to be authenticated to perform an action. You get this by not specifying any users or roles for the Authorize attribute. Any user or role is allowed, as long as it is authenticated. By specifying users and/or roles that act as filters you narrow down the scope of the action and the user needs not only be authenticated (so you can check the name/role membership), but also qualify based on the filter.
Authentication and Authorization are two different concerns.
Authentication verifies that the user is who he says he is, almost always done in most web apps by verifying that he/she has some knowledge (like a password) that only he/she should know.
Authorization verifies that an authenticated user has the permissions to do something. Only administrators can access admin pages for instance.
Since we can get the roles of a person only once logged in, it is possible to use the Authorize attribute to test for authentication.
Take a look at this blog post and see how the author implements both a custom Authorize and Authentication attribute:
Securing your controller actions
You'll see that the Authorize attribute has to check for authentication, since only authenticated users can have a role.

Resources