Web app protected by single password for all clients - asp.net

I was wondering if there is a standard way of protecting a ASP.Net web application with just a single password? In other words no username needed and all clients use the same password for authentication.
Or does anyone have their own solution?

You simply could use Identity framework to aim this propose. Actually you don't need any user or password to authenticate.
[HttpPost]
public ActionResult Login(string password)
{
if (password=="MyVerySecretPassword")
{
var ident = new ClaimsIdentity(
new[] {
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, "JustAnuniqueName"),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
new Claim(ClaimTypes.Name,"JustAnuniqueName"),
},
DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignIn(
new AuthenticationProperties { IsPersistent = false }, ident);
return RedirectToAction("MyAction"); // auth succeed
}
// invalid password
ModelState.AddModelError("", "invalid username or password");
return View();
}
But it would be much better if you hash the password and check the hashed password instead of above simple if statement. To aim this you could use PasswordHasher class to hash and verify the password.
First hash your desired password and save it in preferred storage (DB, file, hard coded in code or everywhere else):
string hashedPassword = new PasswordHasher().HashPassword("MyVerySecretPassword");
Now since you have the hashed one. You could use VerifyHashedPassword() method to verify it.
if(new PasswordHasher()
.VerifyHashedPassword("myHashedPassword",password)==PasswordVerificationResult.Success)
{
// the password is correct do whatever you want
}
Also you could see my simple working example which I made to demonstrate it.

Related

Plain password in .net core SignInManager Identity

I have user table with plain passwords. I need connect the table into .net core web's AspNetUsers table.How to hash my user table's plain passwords like AspNetUsers's PasswordHash.
How can login with SignInManger plain-text password?
I recently did something like this. Our legacy system had its own password hashing method. I needed to covert everything over to asp.net users.
First thing I did was add two new columns to the Application user. These contain my legacy user password and that hash that was used to create it.
public string LegacyPasswordHash { get; set; }
public string LegacyPasswordSalt { get; set; }
Then i ran my sql script that copied all of the users in including their legacy password hash and salt.
Then i created a custom SignInManager.
public class ApplicationSignInManager : SignInManager<ApplicationUser> {}
In the password check method I test if its a legacy password user and if it is i covert the password that they just sent me over to a asp.net users password and delete their legacy password. Tip: is to remember to set the user security token on the user table as well this can not be null. You will have major issues with resting password if it is. As there is a bug in the token validation 2022
This is the section of the code i use for testing and resetting the password.
if (_password.EncodePassword(_user.LegacyPasswordSalt) == _user.LegacyPasswordHash)
{
_logger.LogInformation(LoggingEvents.LegacyUserCommand, "Legacy User {_user.Id} migrating password.", _user.Id);
await _userManager.AddPasswordAsync(_user, _password);
_user.SecurityStamp = Guid.NewGuid().ToString();
_user.LegacyPasswordHash = null;
_user.LegacyPasswordSalt = null;
await _userManager.UpdateAsync(_user);
return await new CheckTwoFactorCommand(_logger, _userManager, _user).Execute();
}
if (_shouldLockout)
{
_user.SecurityStamp = Guid.NewGuid().ToString();
await _userManager.UpdateAsync(_user);
_logger.LogInformation(LoggingEvents.LegacyUserCommand, "Login failed for Legacy user {_user.Id} invalid password. (LockoutEnabled)", _user.Id);
await _userManager.AccessFailedAsync(_user);
if (await _userManager.IsLockedOutAsync(_user))
return SignInResult.LockedOut;
}
_logger.LogInformation(LoggingEvents.LegacyUserCommand, "Login failed for Legacy user {_user.Id} invalid password", _user.Id);
return SignInResult.Failed;

Trying to keep user from using an old password for their new one

I am trying to prevent the user from using any of their last 5 passwords. I am using the exact template from Visual Studio 2015 that gives you a basic Identity user system with it.
I have went ahead to add the following columns to my user database, passwordLastChanged(Date) and 5 columns named previousPassword1 (through 5) respectively.
I need to retreive the current user password from the database using something along the lines of User.Identity.GetUserPassword and I also need something along the lines of model.NewPassword.Encrypt (those don't exist!). Are there methods that do these actions that I am just not seeing in the api?
And I also have to encrypt my new data to see if it matches, how can I encrypt a string the same way that my user password is encrypted?
//
// POST: /Manage/ChangePassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
}
AddErrors(result);
return View(model);
}
Ok going to turn my comments into some kind of answer. So as mentioned passwords are stored in the table Hashed not encrypted. So by definition they can't be retrived (easily).
There's nothing to stop you putting the hashed passwords into the table though to keep a records of previous passwords. Then when a user enters a password you hash it and check if it's already been used, exactly the same as if it wasn't hashed but using the hash not the raw password (this keeps everything nice and secure too).
I wonder If I can just call a function or if I will have to implement
the actual hashing algorithm that uses the same salt and what not?
UserManager implements a PasswordHasher. So to get a password hashed in the same fashion as the default hash should be as simple as:
PasswordVerificationResult passwordMatch = UserManager.PasswordHasher.VerifyHashedPassword(previousPassword1, rawPassword);
I don't use this auth method so the above is untested and based on the MSDN docs only. Should be fine but let me know if it doesn't work.
If the passwords match then passwordMatch == PasswordVerificationResult.Success. You may also need to allow for PasswordVerificationResult.SuccessRehashNeeded

Creating users with no password using ASP.NET Identity

I have been given the requirement to provide the ability to create users through the UI with no password. I am trying to accomplish this using ASP.NET Identity.
I am able to successfully create a user without a password using the UserManager's Create method:
if (vm.ShouldHavePassword)
{
userManager.Create(userToInsert, vm.Password);
}
else
{
userManager.Create(userToInsert);
}
After the call to the Create method, the test user gets successfully saved into our AspNetUsers table. And when I do not provide a password, the PasswordHash column in our AspNetUsers table is set to NULL.
My issue is, I cannot login as the test user that does not have a password. The following is the method call that we use to validate a user's credentials:
result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
I attempted to login as a test user that has a NULL PasswordHash multiple times. To do this, I do not provide a password in our login form. As a result, a NULL password is passed into the PasswordSignInAsync method. The return value of this method call is always SignInStatus.Failure.
Using ASP.NET Identity, how can I configure my code to correctly authenticate user credentials when the credentials contain a NULL password, and the user in the database contains a NULL PasswordHash? Is such a thing even possible?
Yes you can. ASP.NET Identity Framework is fully customizable. Just override PasswordValidator.ValidateAsync and PasswordHasher.VerifyHashedPassword methods like this:
internal class CustomPasswordValidator: PasswordValidator
{
public override async Task<IdentityResult> ValidateAsync(string item)
{
if (string.IsNullOrEmpty(item)) return IdentityResult.Success;
return await base.ValidateAsync(item);
}
}
internal class CustomPasswordHasher : PasswordHasher
{
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
if (hashedPassword == null && string.IsNullOrEmpty(providedPassword))
return PasswordVerificationResult.Success;
return base.VerifyHashedPassword(hashedPassword, providedPassword);
}
}
And set them like this:
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
manager.PasswordValidator = new CustomPasswordValidator();
manager.PasswordHasher = new CustomPasswordHasher();
Okay, what you need to do is find the user (AspNetUsers user) using your db context. After you have the user, you can check if their PasswordHash is null.
If yes, then just sign them in using SignInManager.SignIn.
If not, use SignInManager.PasswordSignIn.
example..
//alternatively, you can find the user using Email, Id or some other unique field
var user = db.AspNetUsers.FirstOrDefault(p => p.UserName);
if (user != null)
{
if (user.PasswordHash == null)
await SignInManager.SignInAsync(user, true, true);
else
await SignInManager.PasswordSignInAsync(model.UserName, model.Password,
model.RememberMe, shouldLockout: false);
}
Hope it helps.
I don't think you can validate user without password. As a workaround: Instead of blank password, I'll recommend to use some Dummy/Common password from C# code, both while creating User and while validating credential
When creating user
if (vm.ShouldHavePassword)
{
userManager.Create(userToInsert, vm.Password);
}
else
{
userManager.Create(userToInsert, "someDummy123$");
}
When validating
result = await SignInManager.PasswordSignInAsync(model.UserName, "someDummy123$", model.RememberMe, shouldLockout: false);

Complex authentication with existing user database in MVC5

I'm migrating a SaaS app from Classic ASP to .NET MVC5 and will use EF6 Database First. The login form for end users is customisable by each tenant (on their own subdomain but pointing to the same web application). We wish to use the existing database schema and the new authentication & authorization filters.
For example, a user on one tenant may login by entering their first name, surname and a code generated by our system. A user on another tenant may login by entering their email address and a password. Additionally, each tenant has a separate administrator login which uses a username and password. Another tenant may use LDAP authentication against a remote AD server.
Is there a definitive best practice way of doing custom authentication?
Almost every article appears to suggest different ways of accomplishing this: simply setting FormsAuthentication.SetAuthCookie, using a custom OWIN provider, override AuthorizeAttribute, etc.
In Classic ASP, we queried the database to find out the type of login for that tenant, displayed the appropriate fields on the login screen and then on post back, checked the fields match what's in the database and then set the session variables appropriately which were checked on each page request.
Thanks
I find that Identity framework is very flexible in terms of authentication options. Have a look on this bit of authentication code:
var identity = await this.CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
This is pretty standard run of the mill authentication part in Identity, you'll find this in every Identity sample on the web. If you look closely it is very flexible - all you need for authentication is ApplicationUser object that framework does not care how you get.
So in theory you can do things like this (pseudocode, I did not try to compile this):
// get user object from the database with whatever conditions you like
// this can be AuthCode which was pre-set on the user object in the db-table
// or some other property
var user = dbContext.Users.Where(u => u.Username == "BillyJoe" && u.Tenant == "ExpensiveClient" && u.AuthCode == "654")
// check user for null
// check if the password is correct - don't have to do that if you are doing
// super-custom auth.
var isCorrectPassword = await userManager.CheckPasswordAsync(user, "enteredPassword");
if (isCorrectPassword)
{
// password is correct, time to login
// this creates ClaimsIdentity object from the ApplicationUser object
var identity = await this.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// now we can set claims on the identity. Claims are stored in cookie and available without
// querying database
identity.AddClaim(new Claim("MyApp:TenantName", "ExpensiveClient"));
identity.AddClaim(new Claim("MyApp:LoginType", "AuthCode"));
identity.AddClaim(new Claim("MyApp:CanViewProducts", "true"));
// this tells OWIN that it can set auth cookie when it is time to send
// a reply back to the client
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
Using this authentication, you have set a few claims on the user - they are stored in the cookie and available everywhere via ClaimsPrincipal.Current.Claims. Claims are essentially a collection of key-value pairs of strings and you can store there anything you like.
I usually access claims from the user via extension method:
public static String GetTenantName(this ClaimsPrincipal principal)
{
var tenantClaim = principal.Claims.FirstOrDefault(c => c.Type == "MyApp:TenantName");
if (tenantClaim != null)
{
return tenantClaim.Value;
}
throw new ApplicationException("Tenant name is not set. Can not proceed");
}
public static String CanViewProducts(this ClaimsPrincipal principal)
{
var productClaim = principal.Claims.FirstOrDefault(c => c.Type == "MyApp:CanViewProducts");
if (productClaim == null)
{
return false;
}
return productClaim.Value == "true";
}
So in your controller/view/business layer you can always call to ClaimsPrincipal.Current.GetTenantName() and in this case you'd get "ExpensiveClient" back.
Or if you need to check if a specific feature is enabled for the user, you do
if(ClaimsPrincipal.Current.CanViewProducts())
{
// display products
}
It is up to you how you store your user properties, but as long as you set them as claims on the cookie, they will be available.
Alternatively you can add claims into the database for every user:
await userManager.AddClaimAsync(user.Id, new Claim("MyApp:TenantName", "ExpensiveClient"));
And this will persist the claim into the database. And by default, Identity framework adds this claim to the user when they login without you needing to add it manually.
But beware, you can't set too many claims on a cookie. Cookies have 4K limit set by browsers. And the way Identity cookie encryption works it increases encoded text by about 1.1, so you can have roughly 3.6K of text representing claims. I've run into this issue here
Update
To control access to controllers via claims you can use the following filter on the controller:
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
public string Name { get; private set; }
public ClaimsAuthorizeAttribute(string name)
{
Name = name;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var user = HttpContext.Current.User as ClaimsPrincipal;
if (user.HasClaim(Name, Name))
{
base.OnAuthorization(filterContext);
}
else
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary()
{
{"controller", "errors"},
{"action", "Unauthorised"}
});
}
}
}
and then use this attribute on controllers or separate actions like this:
[ClaimsAuthorize("Creating Something")]
public ActionResult CreateSomething()
{
return View();
}
User will require "Create Something" claim on them to access this action, otherwise they will be redirected to "Unauthenticated" page.
Recently I've played with claims authentication and made a prototype application similar to your requirement. Please have a look on the simple version: https://github.com/trailmax/ClaimsAuthorisation/tree/SimpleClaims where claims are stored individually for each user. Or there is more complex solution where claims belong to a role and when users login, role claims assigned to the user: https://github.com/trailmax/ClaimsAuthorisation/tree/master
There's two components you need. The authentication itself and the strategy each user gets for authentication.
The first is easy and is accomplished with these two lines...
var identity = await UserManager.CreateIdentityAsync(user,
DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{ IsPersistent = isPersistent }, identity);
When a user is Signed In, they get an identity which contains the user's claims on roles and who they are. These are given to the user as a cookie. After this point you just decorate controllers with [Authorize] to make sure only authenticated users can log in. Pretty standard here.
The only complicated part in the problem is the second part; The strategy for how each user gets authenticated set by the admin.
Some pseudocode for how this could work in actions is this...
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(int tenantId)
{
var tenant = DB.GetTenant(tenantId);
return View(tenant);
}
In your view you would output the authentication strategy for the tenant. That may be email and password, a code and email, or whatever your requirements.
When the user enters their info and clicks to login, you then have to determine what strategy they were using, and check to see if their information matches.
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model)
{
var tenant = DB.GetTenant(model.tenantId);
//If user info matches what is expected for the tenants strategy
if(AuthenticateUserInfo(tenant, model.UserInputs))
{
//Sign the user in
var identity = await UserManager.CreateIdentityAsync(user,
DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{ IsPersistent = isPersistent }, identity);
}
}
I did a lot of hand-waving in the second part because of the complicated nature of how dynamic it is. Overall you should use the same strategies you used in your legacy application to generate the right inputs and such. Nothing has changed there, only the way you sign in is going to be different.
Using Visual Studio 2013 Update 3 you can create a new Web Application that comes with MVC5, EF6 and Identity already installed. Here is how to select Identity when you create a new Application:
With MVC Template selected, click Change Authentication and the highlighted window will pop up. Individual User Accounts = Identity. Click ok and continue.
Having done that, you have created an application with Identity. You can now customize your login and registration as follows.
You want to look at your AccountController.cs in the Controllers folder. Here you will find the script for Registration and Login.
If you look at the
public async Task<ActionResult> Register(RegisterViewModel model)
function, you'll notice it contains:
IdentityResult result = await UserManager.CreateAsync(new ApplicationUser() { UserName = newUser.UserName }, newUser.Password);
This is where the user gets created. If you want to use Identity, you should save the users username and password. You can use an e-mail as the username if you want. etc.
After doing that, I add the user a specified role (I find the user and then add it to the role):
ApplicationUser userIDN = UserManager.FindByName(newUser.UserName);
result = await UserManager.AddToRoleAsync(userIDN.Id, "Admin");
In my scenario, I have created an additional extended table where I hold their address, phone number, etc. In that table, you can hold any additional login information. You can add these new entries before or after creating the users account in Identity. I would create the extended information and then create the Identity account just to be sure.
IMPORTANT: For any scenarios where a user is logging in with something that is not a username or e-mail address that isn't saved into via Identity, you will have to do a custom solution.
Example: User types in their first name, surname and the code. You could do two things: Save the first name and surname into the username field of identity and the code into the password and verify the login that way
OR
you would check your custom table for those properties and make sure they match, if and when they do you could call this little beauty:
await SignInAsync(new ApplicationUser() { UserName = model.UserName }, isPersistent: false);
Once you call that SignInAsync function, you can go ahead and direct them to your protected page.
NOTE: I'm creating the ApplicationUser on the function call but if you use it more than once it would be ideal for you to declare the ApplicationUser as follows:
ApplicationUser user = new ApplicationUser() { UserName = model.UserName };
NOTE #2: If you don't want to user Async methods, those functions all have non-async versions of them.
Note #3: At the very top of any page using UserManagement, it is being declared. Make sure if you are creating your own controller that wasn't generated by Visual Studio to use Identity, you include the UserManagement declaration script at the top inside of the class:
namespace NameOfProject.Controllers
{
[Authorize]
public class AccountController : Controller
{
public AccountController() : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))) { }
public AccountController(UserManager<ApplicationUser> userManager) { UserManager = userManager; }
public UserManager<ApplicationUser> UserManager { get; private set; }
Please let me know if you have any questions and I hope this helps.

ASP.NET MVC 5 Default WebApp Forgot Password module missing?

In MVC 4 with SimpleMembership all these functions come with the default webbapp that you create in Visual Studio.
I was wondering where I can find the same for MVC 5 using the new ASP.NET Identity membership system? Is there some official blog or something that is beeing hidden from me in google search results?
UPDATE1: http://blogs.msdn.com/b/webdev/archive/2013/12/20/announcing-preview-of-microsoft-aspnet-identity-2-0-0-alpha1.aspx
UPDATE2: ASP.NET Identity 2.0 RTM has been released. Forgot Password is included in the samples/templates. http://blogs.msdn.com/b/webdev/archive/2014/03/20/test-announcing-rtm-of-asp-net-identity-2-0-0.aspx
We are working on adding these features to the ASP.NET Identity system and the MVC 5 templates.
I ran into this as well. To fix it, I created some controller actions in AccountController.cs (and corresponding views) to handle it.
Here are the actual lines that reset the user's password:
[AllowAnonymous]
[HttpPost]
public ActionResult ResetForgottenPassword(string key, ManageUserViewModel model)
{
var user = db.Users.SingleOrDefault(u => u.ForgotPasswordCode != null && u.ForgotPasswordCode == key);
if (user == null || !user.ForgotPasswordDate.HasValue || user.ForgotPasswordDate.Value.AddDays(1) < DateTime.UtcNow)
return new HttpUnauthorizedResult();
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
if (UserManager.HasPassword(user.Id))
UserManager.RemovePassword(user.Id);
IdentityResult result = UserManager.AddPassword(user.Id, model.NewPassword);
if (result.Succeeded)
{
//Clear forgot password temp key
user.ForgotPasswordCode = null;
user.ForgotPasswordDate = null;
db.SaveChanges();
//Sign them in
var identity = UserManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, identity);
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
}
else
{
AddErrors(result);
}
}
ViewBag.ForgotPasswordCode = key;
return View(model);
}
Some custom items are the new fields on the user object:
ForgotPasswordCode and ForgotPasswordDate to keep track of the user throughout the "reset password email" process.
I pass the key around in in the ViewBag once the user arrives from the email link.
The db variable is a property of my database context class inherited from a base controller.
I use UTC DateTimes in my database. Change DateTime.UtcNow to DateTime.Now if you do not.
Probably not the best solution, but it's a fairly quick and simple patch.
You can build a reset password by yourself (not sure that is the better choice, but is better than nothing)
Generate the hash with:
var newPwdHash = new PasswordHasher().HashPassword(newPasswordPlain)
And replace to the user's passwordhash property
If you cannot wait for the ASP.NET Identity Team to add this feature you can get an implementation of password reset from the open source project SimpleSecurity. Just take a look at the ResetPassword action on the AccountController. You can read about how the password reset was implemented here. Although the article references SimpleMembership, SimpleSecurity uses the same API to support either SimpleMembership or ASP.NET Identity in your MVC application.

Resources