I'm using ASP.NET Roles with a special role "Must Change Password". If a user has not changed their password for more than 90 days, they are automatically added to this role. This happens during the user login process. Authorization rules then deny that role access to all of the application except the "change password" page.
Generally this works great, but there is a problem when the role cache cookie is used to cache roles. What happens is during the login process, the password last changed date is checked, and if > 90 days, the user is added to the "Must Change Password" role. In the same page request, I subsequently call Roles.IsUserInRole("Must Change Password") to decide whether to redirect the user to the Change Password page or not. This is where it falls down - it seems that with the role cache cookie enabled, Roles.IsUserInRole("Must Change Password") doesn't realise that I have changed role mappings for this user, and returns false. However, on the next page request, Roles.IsUserInRole("Must Change Password") returns true.
This behaviour is fixed by setting cacheRolesInCookie="false", but that seems a high price to pay. Is there another way to fix this problem?
Another , IMHO more elegant, solution is to cast HttpContext.User to RolePrincipal and call SetDirty method after adding a new role to the user (read more on RolePrincipal.SetDirty).
The next call of IsInRole or GetRolesForUser methods should trigger request to your default RoleProvider.
Since you said that the problem exists in the same request, how about also setting an item in the HttpContext.Current.Items collection to indicate that the user must change their password, and check both the cookie and the HttpContext.Current.Items collection later on in the code?
Actually, I've found the problem - it is not a problem with caching roles in cookies, but rather a problem with Roles.IsUserInRole(). If I use the overload Roles.IsUserInRole(username, role) then it works fine, with or without roles cached in a cookie.
Related
In MVC5 asp.net - Is there a way to allow user login only if the user has a particular claim in the user database? I'd like to assign a "CanLogin" claim to users in my user database that are allowed to log in at any given time. By removing the "CanLogin" claim, I could effectively lock the users out of the system until further notice.
This would have to work for a first time login as well as cookie login at a later stage if the user has checked "remember my login".
Using authentication filter, you can check the identity.claims property to validate whatever claims are present in the context.
The claims must be added during the login process
Then you can check whether a particular user is enabled or not.
However, if the user database is self maintained, you can just set a disabled flag and then reject the login request, instead of returning such a claim.
The claims are used for Authorization to a particular functionality rather than Authentication to an app. A valid user will have certain claims which can tell what all the user is permitted to do.
I have a simple requirement of not allowing a user to log into multiple browsers at a time. Same browser is fine.
What is the simplest thing which can be done?
Technlogy : .net 4.0 and sql sever 2008 R2
See my advices below:
Store LastActivityDate for each user. If you are using asp.net SqlMembershipProvider - this field exists there, if you use another authentication mechanisms - probably you need to create it and update with each request of certain user.
Add an additional boolean field LoggedIn for each user. This field will be set to true when user does login. If you are using asp.net SqlMembershipProvider you can store its value in Comment field.
When user closes the browser send request to server to 'logout' user, which means set LoggedIn field to false. Use window.onbeforeunload javascript event for that.
On user login you should check LoggedIn field for the user, if it is false - you simply process the operation. If not - you should check LastActivityDate value, and if it older than a timeout you will define (lets say 3 minutes) process the operation. If not - reject it and show error message. This additinal check is required because we cannot guarantee that window.onbeforeunload is always executed.
The final step would be a javascript which consequentially calls a server action in timeout which updates LastActivityDate. This script should be defined on each page which is accessible for logged in user.
I hope the approach is clear.
I'm using the DropCreateDatabaseAlways Initializer so my database gets destroyed every time I start the application (at least I hope so). The funny thing is I still see myself as logged in. I get pass the Authorize attribute and can do dangerous stuff. This is probably because of leftover cookies from previous testing.
Registration/login part of my app is the MVC 4 Internet Application template untouched. Shouldn't ASP.NET check the cookie values against users saved in the DB? WebSecurity.IsAuthenticated returns true and WebSecurity.CurrentUserName returns the name.
The only thing working as expected is WebSecurity.CurrentUserId which returns -1. I'm a newbie so I can only guess this is because UserId isn't stored in the cookie and must be retrieved from the database.
Am I right? If so, does it mean I should always use WebSecurity.CurrentUserId to determine whether a user is logged in? WebSecurity.IsAuthenticated and User.Identity.IsAuthenticated seem pretty useless in that case. I can delete a user's account and he or she remains unaffected. What should be done differently if I am wrong?
If you want to realiably check whether a user has not been deleted, you just have to consult the database.
Note that users and administrators work concurrently. This means that a user can be deleted just a second after he has been authenticated. A cookie can be then even one or two seconds old (!) and the user could probably have been just deleted.
In a most pessimistic scenario, a user types a valid username and password and is succesfully logged in and get the "sorry, your account has been deleted" just one request later (because the account has really just been deleted).
There will be a small window where if a user is deleted and they are still logged in that they can still access the site. Since most actions require a validate user id, you can simply throw an excpetion and log the user out.
Normally the database does not get blown away on each build, so I'm guessing this is not a use case SimpleMembership was coded for. You can of course check for this. I'll make another assumption that you are not closing your browser when you rebuild the site and deploy the new database. In a real world scenario these things just don't happen. The database is never blown away and the user id is never lost.
Generally once you've logged in the user, the user is not authenticated anytime after that (unless they have logged out, or the session has expired). That's the point of login. The authentication cookie is an indication that the authentication happened and was successful. The assumption going forward is the user has access to your site and is not reauthenticated.
As long as the authentication session remains open (i.e. browser now closed), the session cookie remains active and MVC assumes that the user is still valid.
Remove the cookie by using FormsAuthentication.LogOff() if the user is authenticated (User.Identity.IsAuthenticated == true) and there's no valid user in UserTable (WebSecurity.CurrentUserId == -1).
I had the same issue, but solved it by specifying the required role(s) in the Authorize attribute. As soon as you do this, it start getting to the database and fails with the "user does not exist" error, which is what you want.
[Authorize(Roles = "Customer")]
public class DashboardController : Controller
In normal case adding Session.Abandon(); to to your LogOff action in AccountController would do the job of clearing session:
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
Session.Abandon();
return RedirectToAction("Index", "Home");
}
So I think you can try adding Session.Abandon(); in init code, where you use DropCreateDatabaseAlways to clear session each time.
The ticket issuing time is usually encrypted within the authentication cookie then you can use it to require re-authentication to sensitive areas (inbox/billing etc.) if more than X time passed since login.
If you insist on invalidating all current auth tickets upon application changes (i.e database/configuration) you can change this settings in your web.config:
<machineKey validationKey="..." decryptionKey="" />
I want to redirect people according to their role in the asp.net membership provider. I have some code in 'OnLoggedIn' event on the login page. Here is what I tried so far, and nothing doesn't work:
The code below returns false on the .IsAuthenticated step, it says the user is not authenticated. On which step at the login page the user is authenticated, I thought the 'OnLoggedIn' event is the right place to do this.
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
}
}
}
2nd thing I tried was to get all the roles for the user, but it doesn't return anything, returns empty array of strings, and I checked the database that the role is assigned to that specific user. Here is how I am trying:
string[] userRole = Roles.GetRolesForUser(LoginUser.UserName);
So, my question is how I can get the role on the login page, do I need to check that in some other event or on some other way.
Thanks for your help in advance, Laziale
The first situation won't work because it returns the value of the Request as it came in. Logging in with FormsAuthentication basically just sets a cookie in the Response so that the next Request gets the right User.
The second situation should work though, assuming that LoginUser is the name of your login control.
if you have some predefined set of rules like (SuperAdmin,Admin,User,....) than
you can use a switch case
and according to the authentication level
you can classify/redirect the user accordingly with respective URL
Greg is totally correct. So the question is why the roles are not returned? There could be several reasons depending on how you created the roles on your rolesprovider settings in web.config.
So make sure you roles provider is pointing to the same database you are see the roles.
Also check you are not using different role provider to create roles but accessing it via different one. This can happen if you have more than one roles provider in web.config.
Also check if it isn't the applicationName issue.
If that doesn't help post your web.config roles setting as well as info on how you are creating roles.
When is the LastActivityDate supposed to be updated? When I click on any other pages in my web application as an authenticated user, the LastActivityDate does not get updated.
I would imagine it should be updated when a user clicks on any page, whether it be to do with membership (change password, GetUser(), etc) or not. But the examples that I have seen, seem only to update it on ValidateUser().
http://msdn.microsoft.com/en-us/library/system.web.security.membershipuser.lastactivitydate.aspx
Under Remarks, it says to do it under ValidateUser method.
When do you update the LastActivityDate? Just inside ValidateUser()?
Thanks
Update: Clarified question.
No, for it to get updated on each page request would require your pages to update it manually, but that would be a bad idea.
The LastActivityDate refers to last activity within the domain of the membership provider i.e. authentication token request, change password etc.
If you want to track page request per user that would be another domain, as is typically implemented as a log file of all page request per user.