Why doesn't ValidateUser return more? - asp.net-membership

I'm using the standard .NET membership provider, and thought I'd see if someone could shine a light on it.
Calling ValidateUser return either true or false. Now since the method accepts a username and password, one would reason that the return result would reflect an invalid username or password. Yet if we delve into it further, we find it is also checking IsLockedOut and IsApproved.
public override bool ValidateUser (string username, string password)
{
MembershipUser user = GetUser (username, false);
/* if the user is locked out, return false immediately */
if (user.IsLockedOut)
return false;
/* if the user is not yet approved, return false */
if (!user.IsApproved)
return false;
......
In my application, I would like to make use of IsApproved for my own means. Simply rolling my own provider won't work because I'm still constrained to a bool result. Creating a user gives us all the information we need, so why not ValidateUser? Am I missing something?

I expect that what you're seeing is a security decision - by restricting the information returned, they're not providing information to malicious parties.
Imagine that you're Cyril Cracker, trying to break into a website.
Scenario #1:
You try entering username "Admin" with password "Password" and the system tells you no-go. The only information you have is that Admin/Password isn't a valid combination.
Scenario #2:
You try entering username "Admin" with password "Password" and the system tells you that no user by that name is known. You can keep trying different usernames until you find one that is known.
Scenario #3: You try entering username "Admin" with password "Password" and the system tells you that no the password is invalid. Suddenly, you know that "Admin" is a valid user. You've learnt something useful, and all you need to keep guessing is the password.
Scenario #4: You try entering username "Admin" with password "Password" and the system tells you that the account is blocked. Now, you know a valid username and password, and that the account is blocked. You can come back later and try again.
Systems that spill the beans on what's valid, and what's not, are known as chatty systems, and they're considered insecure for good reason, as they're easier to crack.
Hope this is helpful.

Related

parse-server: How to differentiate between signUp and regular ParseUser.save in User class BeforeSave?

This is an issue where parse-server behaves differently from Parse.com.
I have anonymous users in my app, and when using ParseUser.signUp (Android SDK) there's an important difference between parse-server and Parse.com. Parse.com would first check for username conflicts, and if there is one, the beforeSave function wouldn't even be called. This allowed me to make some assumptions in the beforeSave code.
Now in parse-server, the beforeSave is always called, and only after I response.success() does it fail to save it.
The problem is that there is some code that shouldn't happen if the internal signUp fails (e.g. duplicate username) but happens anyway since I assume that if beforeSave is called, the username is unique. My solution was to do the check myself via a ParseQuery on the username, but now there's another issue - how do I differentiate between an anonymous user and a new user?
In my app, every new user is automatically saved as an anonymous user. For an anonymous user, there's some things you can't do (change username for example). Now after a while, when he wants to sign up, he enters a username, and in the beforeSave I can't tell if he is trying to change only the username, or is he signing up? If he is trying to sign up, I should allow him to set the username, but if he is just trying to change his username, then I'm suppose to reject the change.
So to summarize: How can I tell in the User class beforeSave if signUp was called or a regular save?
Can you try using the .existed() function in your beforeSave?
Parse.Cloud.afterSave(Parse.User, function(request) {
if(!(request.object.existed())){
//Not sure what anonymous user would return here...
}
});
I couldn't find anything obvious in the documentation, but after analysing the request object of the beforeSave hook, I found a solution:
Verifying if the authData object contains the anonymous key tells us if an anonymous user is saved. Once signed up, authData is undefined:
var authData = request.object.get("authData");
if (authData != undefined && authData.anonymous != undefined) {
console.log("#### User is anonymous");
response.success(); // save and return
return;
}
To figure out if the user has just signed up, you'd compare if the email address was set (not changed but that it was undefined before). You do that by asking request.original for the email:
// object now has an email for the first time
if (request.object.get("email") != undefined &&
request.original.get("email") == undefined) {
console.log("#### User signed up");
…
}
I'm not sure what the recommended way is, but this seems to work to distinguish the anonymous user from the real, signed up user.

Is there a need to check IsSignedIn and IsInRole as a sign in check

Simple enough question, is this enough? or is there a corner case where a user can end up with some information pointing to not logged in but still with a role of some sort.
#if (User.IsInRole(Roles.Administrator))
or do I have to do this?
#if (SignInManager.IsSignedIn(User) && User.IsInRole(Roles.Administrator))
The Roles will be populated by the membership provider when a user logs in and persisted until their session expires, so just as long as you don't have an administrator role for anonymous users the first check will be enough.

How to work with not (yet) registered devise Users

I have a User model, for login and registration, its email field is used (everything vanilla from the devise gem).
I want (other) users to be able to e.g. add Users to a team, with the email-address as the identifier.
That is fine when the User is already existing (pseudo #team.users.add(User.find_by(email: other_users_email))) but I am unsure how to handle situations where the user does not yet exist (did not [yet] register).
When a (new) User sets up a new account, for the example above after successfull registration current_user.teams should show up correctly.
I do not want to force these potentially new users to use the system (e.g. using devise_invitable) and bother them with an email.
I followed the path of creating the User when a user with the given email does not yet exist, but then when the user actually tries to setup an account, it fails (email not unique).
Alternatively, I could remodel the TeamMember-part and let it optionally either store an email-adress or the reference to an existing User. Then what I would need is to check for "open" TeamMembers directly after User-Account-creation (so, TeamMembers with the given email). I could also do this on each requst, but that looks too expensive to me. There might be race conditions, but I could live with that (and check for the every-now-in-a-millenia-gap with a cron-job).
Any pointers? I am sure this is not that unusual.
I'd do this:
When a user A adds user B to a team by email, create the object for that user B, but set a flag, something like auto_created_and_inactive: true
When user B signs up on the site, you just have to handle this in your users#create: first, try to find an auto-created record and update it (set a password or whatever; also reset the flag). Or otherwise proceed with the usual route of creating a new record.
I have to admit that I did not yet tried #sergio-tulentsevs approach (implement RegistrationController#create). But to complete what I sketched in my question:
User model can define an after_confirmation method, which is called after ... confirmation! So, if I store every information about a potential user with a reference to his/her email-adress, once he/she registered I can query this information and e.g. complete Team-Memberships.
# app/models/user.rb
def after_confirmation
# (pseudo-code, did not try)
self.teams < TeamMembership.open.where(email: self.email)
end

How to login with 3 parameters in asp.net mvc

I have an application where one username can belong to many companies. Thus to distinguish them, i need to use the both username and password as unique pair to login.
I'm using ASP.NET MVC and i struggle to understand where the Login occurs.
Actually i can see where it validates the user but i don't find where it retrieves the user.
So where the
Select user where username=xx and password=xx occurs ?
Asked differently : i did not find wher User is set ? I see User.Identity.Name it in the code, but i don't see :
User=Select....
Thanks
John
John, as you are using MVC. You wont be seeing any queries in the code aside from the LINQ syntax. Im guessing what you are trying to do is a many to many relationship between the User table and the Company table. (one user has multiple companys and 1 company has multiple users)
Pretty much database wise this would mean you need an extra table with both primairy keys of Company and Users.
To get back to your question. ASP.net MVC has its own membership provider. You can choose to either use the default one with its own tables or overwrite it and create ur own custom membership provider (with the ability to use ur own user table)
The default one pretty much should contain most of the basic attributes. (password reset, password salt, email,..)
http://www.asp.net/web-forms/tutorials/security/membership/creating-the-membership-schema-in-sql-server-cs
skip to the step: Installing the Application Services to generate the tables
However guessing you already have a database with your very own user table. you should overwrite the custom membership class.
Simply this would mean you make a new class that inherits from the abstract class "MembershipProvider"
public class MyMembershipProvider : MembershipProvider
{
}
After that you have to let asp know that you will be overwriting the default membershipprovider with yours in web.config:
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider"
applicationName="MyApp"
Description="My Membership Provider"
passwordFormat="Clear"
connectionStringName="MyMembershipConnection"
type="MyApp.MyMembershipProvider" />
</providers>
</membership>
Some methods in the membership provider requires you to return or use an object of MembershipUser. Everything of how to implement this is right here:
http://msdn.microsoft.com/en-us/library/system.web.security.membershipuser.aspx
This is not a necessary step but its recommended.
Good luck john :)
If theres any confusion in the explanation, dont hesitate to ask
Short Answer:
The code you're describing happens behind the scenes in the LogOn action of the Account Controller:
MembershipService.ValidateUser(model.UserName, model.Password)
Which returns true for a valid user. The user is then "signed in" with the next line in the code:
FormsService.SignIn(model.UserName, model.RememberMe)
(You can see both of those functions defined in the AccountModels file under the Models folder)
If you want to also check company id while authenticating the user then you'll need to write your own auth method to replace ValidateUser. Ths will depend on what you're using for your store (SQL?)
But, as a broader point, best practices you should not allow the same user name for different users. It's just a bad idea and will lead to trouble.
UPDATE:
If I were recommending how to do this, I would suggest you user the UserProfile aspect of ASP.NET Membership. It is designed to capture and store additional user variables (such as company) while still using the nicely built and secure Membership that they've written for you. Read up on it, but below is my CreateUser function in the app I'm currently working on. Note how I use the Profile to store first and last name as well as a flag that the user needs their password reset.
Again, this would preempt the ability to have multiple users with the same username, but I really think you ought to avoid that.
[HttpPost]
public ActionResult CreateUser(string username, string email, string first, string last, string role)
{
string password;
MembershipUser user;
//Generate a random password
password = Auth.CreateRandomPassword(6);
try
{
//Create the user
user = Membership.CreateUser(username, password, email);
//Add the user to the chosen role
Roles.AddUserToRole(username, role);
//Create the user profile
UserProfile profile = UserProfile.GetUserProfile(username);
profile.FirstName = first;
profile.LastName = last;
profile.ForcePasswordReset = true;
profile.Save();
EmailNewUser(username, email, password);
}
catch (Exception ex)
{
HttpContext.Response.StatusCode = 500;
HttpContext.Response.StatusDescription = ex.Message;
HttpContext.Response.Clear();
}
return PartialView("UserTable", Auth.Users());
}

Manage multiple calls of Membership.GetUser().ProviderUserKey;

I was thinking yesterday how to solve this issue, because everything what i give or check about user is depended of his ProviderUserKey (ID).
So i made one static function like
public static Guid GetUserID()
{
string UserID = string.Empty;
if(HttpContext.Current.Session["UserID"] != null)
{
UserID = HttpContext.Current.Session["UserID"].ToString();
}
if(!string.IsNullOrEmpty(UserID))
{
return new Guid(UserID);
}
UserID = Membership.GetUser().ProviderUserKey.ToString();
HttpContext.Current.Session["UserID"] = UserID;
return new Guid(UserID);
}
Main point of this class is to reduce database connections to check/get user ID.
My problem with this function is not that this is not working, my problem is what if logged user log out and log with another account?
Or Is it better to add session value on log in and clear session value on log out?
Where you can see any other problem with this kind of "Get User ID"?
If you log the user out then you should also be killing the session.
When you login as another user you would also have the session reinitialized.
Note you'll want to keep the session and forms auth timeouts (assuming you are using forms auth) in sync with each other:
How can I handle forms authentication timeout exceptions in ASP.NET?
This should help keep the session in line with the forms auth token. You'll in turn need to kill the session on logout and intialize it upon login.
Another alternative is to implememt your own membership provider that caches this key to prevent constant db hits.
have you tried using the ProfileProvider?
you can use and customize with special properties and that's is managed by session each user.
example to get values:
HttpContext.Profile.GetPropertyValue["CustomProperty"]
In this video you can lean to implement it, create, configure and use...
http://www.asp.net/web-forms/videos/how-do-i/how-do-i-create-a-custom-profile-provider

Resources