I have an ASP.NET MVC 4 project which I have successfully connected to a MySQL database. I have done this by adding a ADO.NET/EntityFramework class which created a Model.edmx object.
Within the database, I have created a table called user which holds what you should expect in a User table such as Email, UserName, Password, FirstName. etc etc.
I have created some dummy records and added the following code to the Login method within the AccountController:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var database = new Database();
user user = database.SelectByUserName(model.UserName).FirstOrDefault<user>();
var hash = Utilities.HashPassword(model.Password, user.Salt);
if (hash == user.Password && WebSecurity.Login(user.UserName, user.Password))
{
//Correct Login Details!
RedirectToAction("About", "Home");
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
For some reason, the WebSecurity.Login method returns false and the user isn't redirected to the Home page.
Why is it returning false? What am I missing and how would the WebSecurity.Login even know what credentials are required i.e. How does it even know that it should look inside the user table which I created?
WebSecurity doesn't default to looking at your database, it will actually make it's own tables using the DefaultConnection that is defined in Web.Config. To work around this you need to add a new connection string Web.Config and then during app initialization force WebSecurity to look at that connection.
The easiest way to accomplish this, assuming you have a MySQL specific connection string in your Web.Config named "AccountConnection" is by adding the following to your Application_Start()
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
And then you'll need the following fields and function:
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
Database.SetInitializer<UsersContext>(null);
try
{
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
// Overload is: Web.Config Connection string by name, user table name, user id column name, user name column name, auto create missing tables
WebSecurity.InitializeDatabaseConnection("AccountConnection", "UserProfile", "UserId", "Email", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The Membership database could not be initialized.", ex);
}
}
}
Whether you can make WebSecurity work with MySQL I have no idea, though I believe I've read some place that it is supported.
Note: the UserContext should have been auto generated when you installed WebSecurity into your solution. If not it's a CodeFirst model that you can easily add.
There are one of two reasons your code will not work. Understand that WebSecurity and SimpleMembershipProvider (assuming you are using it) uses PBKDF2 algorithm to populate the password field when you call WebSecurity.CreateUserAndAccount or WebSecurity.CreateAccount.
So Either:
You did not use one of these two methods to create the user, in which case WebSecurity.Login will almost always fail (99.99%).
or
You did use one of the methods above and the code in Utilities.HashPassword() (which seems redundant since the Create Account methods listed above hash passwords anyway...) does not hash the password Exactly the same way WebSecurity does so hash == user.Password will always fail.
Related
I'm using MVC 5 and EF 6 (Datafirst),using msssql management studio.
I created a new mvc project, which came up with built database (AspNetUsers etc)
I also created a new table called UserDetails, which it purpose to contain more details about the user by it's Id (so I created a link between AspNetUsers id column to UserDetails UserId column)
therefore I added the following code
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaim(new Claim("FirstName", FirstName.ToString()));
return userIdentity;
}
//Extended Propeties
public string FirstName { get; set; }
}
But of course it's not working, I looked over the internet for over 4 hours now, can someone please guide me ? I'm new to MVC, everything seems to be complicated much.
I also have the following static method :
public static class IdentityExtensions
{
public static string GetFirstName(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("FirstName");
// Test for null to avoid issues during local testing
return (claim != null) ? claim.Value : string.Empty;
}
}
in order to get it in the view and display it..
my goal is to display data from another table (UserDetails) in the view based on connection of 1-1 from AspNetUsers (UserDetails.UserId == AspNetUsers.Id)
All you have to do is extend the IdentityUser class, then add your custom properties like FirstName etc.. then since you are using EntityFramework Database first you need to enable the migrations with this command in your package manager console enable-migrations, then add an initial migration like add-migration initialMigration, after that update the database using migrations with this command update-database, the AspNetUsers table in your database will now have the new columns you added. Use migration to keep your database in sync with your models
if there is correct connection between both table you can use Eagerly Loading to get the details of one entity from another.
My current Asp.Net MVC 5 project mandates Email address for UserName. Now I want to upgrade ASPNet Identity v1.0 to v2.0 to leverage all its new features (see here).
However, ASPNet Identity v2.0 adds Email as a separate column to the Users table and adds a corresponding property to the IdentityUser class.
I don't want to duplicate UserName into this new Email column. How can I map this Email Property of IdentityUser to use existing UserName column & property? Is it possible to ignore this Email property and skip adding the column in the Users table? Has anybody tried this?
Please share.
Update
This is the identity 2.0 limitation. We cannot ignore Email property or leave it Null. Some of the Identity functionality will not work. :(
You can try one of these:
Try to ignore it by either overriding Email property in your User class and unmapping it or using fluent API.
public class ApplicationUser : IdentityUser
{
// ....
[NotMapped]
public override string Email { get; set; }
}
or
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>().Ignore(u => u.Email);
}
When you register your user just make sure that you populate Email with your UserName
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
// ...
}
}
Of course, you can always ignore Email column if you're not going to use it, since it allows NULLs, it'll just be sitting in your AspNetUsers table with bunch of NULLs, not the best approach but remember that by ignoring it you might lose new features that ASP.NET Identity 2 might offer that you might want to use.
NOTE However I'm not sure if option number 1 will work on Email property since it's probably used all over the place in new Identity code. Worth a try though. I know that's how you can get rid of other columns if you don't need them. I personally happen to use new Email property/column so I haven't tried it.
Not sure if it helps you, but thought I'd share it just in case.
I have the same problem, and the way that I resolved it was that the email address was the same as the username when creating a user:
var newUser = new ApplicationUser()
{
UserName = email,
Email = email,
};
However, if you try to create an account with a duplicate username, you will get 2 validation errors, one for the username field, and one for the email address.
To get around that, allow email addresses to not be unique (they will still be unique though as your usernames are unique) by editing the identityconfig.cs file:
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
The problem I'm having right now it's that ASP.NET MVC validates the request object, user in this case
public ActionResult Edit(User user)
What I want to do is that if the user's password is left blank, do not update the password, just use the old password, but if it's set, update it.
The problem is that the framework complains that user does not has a password, even if I update the user object, it complains
public ActionResult Edit(User user)
{
user.Password = "Something";
// more code...
}
Apparently it does the validation on the request object, is there a way I can skip the validation in this case, or at least delay it until I finished modifying the user object?
This is the full method code
[HttpPost]
public ActionResult Edit(User user)
{
if (string.IsNullOrEmpty(user.Password))
{
var oldUser = db.Users.Single(u => u.Id == user.Id);
user.Password = oldUser.Password;
}
try
{
db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View(user);
}
}
The model state will still be invalid even after you set the password to something. After you do that try clear the model state using ModelState.Clear(); Or modify the ModelState accordingly i.e clear the error state only for password property ModelState.Remove("Password");
[HttpPost]
public ActionResult Edit(User user)
{
if (string.IsNullOrEmpty(user.Password))
{
var oldUser = db.Users.Single(u => u.Id == user.Id);
user.Password = oldUser.Password;
ModelState.Clear(); // or change the state accordingly (ModelState.Remove("Password");)
}
.......
}
From your posts it looks like you are ware of the [Required] attribute on the password field. I suggest you force javascript validation and let the user know that they cannot update the password to be blank. Or if you are updating User information in a form that doesn't include password create a new viewmodel or temp model that does not have the password field and the model is specific to that form. Post to this model and bind to this model. Then update the actual user model by using the data from the temp user model. This will be better practice to follow.
The problem here isn't related to the server, it's the client side. I assume you have a Required attribute on the Password property so when MVC generates the view for the model it automatically generates client-side validation AKA Unobtrusive validation.
You could write your own custom attribute to handle your particular scenario, however, that would mean you would also need to write your own client-side validation. The simple fix here is to remove the Required attribute and handle the validation on the server side.
What I want to do is that if the user's password is left blank, do not update the password, just use the old password, but if it's set, update it.
If that is the case then your logic is actually wrong anyway. You shouldn't have the Required attribute on the Password as your rules dictate that it can be empty.
Rails has a very convenient uniqueness validation.
ASP.NET MVC doesn't.
I need to make sure that the e-mail address a user has entered hasn't been registered by anyone yet.
I can see only one way of doing this kind of validation: create a new data context object in the UniqueAttribute class.
But I'm afraid that wasting memory on a new data context object just for one validation is dangerous.
Am I wrong? Is there a better way to do that?
Update
This is what I got so far
public class UniqueEmailAttribute : ValidationAttribute {
public override bool IsValid(object value) {
DataContext db = new DataContext();
var userWithTheSameEmail = db.Users.SingleOrDefault(
u => u.Email == (string)value);
return userWithTheSameEmail == null;
}
}
// Usage
[UniqueEmail(ErrorMessage="This e-mail is already registered")]
public string Email { get; set; }
There are two problems.
It would be good to have just one UniqueAttribute class, not separate classes for e-mails, usernames etc. How can I do that?
Creating a new data context every time you need to validate a single attribute.
SOLUTION
So in the end I created a unique constraint on the table and now I just have to intercept SqlException in Users repository. Works great and is probably more efficient than searching for the same node in the whole table. Thanks!
Mvc 3 Relaease candidate has new New Validation Attributes as a remotevalidation -where you can register a method for validation on clientside(jquery).
see below example-
RemoteAttribute
The new RemoteAttribute validation attribute takes advantage of the jQuery Validation plug-in's remote validator, which enables client-side validation to call a method on the server that performs the actual validation logic.
In the following example, the UserName property has the RemoteAttribute applied. When editing this property in an Edit view, client validation will call an action named UserNameAvailable on the UsersController class in order to validate this field.
public class User {
[Remote("UserNameAvailable", "Users")]
public string UserName { get; set; }
}
The following example shows the corresponding controller.
public class UsersController {
public bool UserNameAvailable(string username) {
return !MyRepository.UserNameExists(username);
}
}
Mvc 3
UPDATE
public bool UserNameAvailable(string Propertyname)
{
if (Request.QueryString[0]= "UserName")
{
//validate username
}
elseif (Request.QueryString[0]= "Email")
{
//Validate Email
}
}
ASP.Net does have a feature that can automatically check the uniqueness of a user's email address when a user registers. It is the ASP.Net Membership service and you can use it to do what you want even if you don't use all of the features of it.
If you are not using the full Membership feature in your MVC application, then all you need to do is use
Membership.FindUsersByEmail(emailYouAreLookingFor);
If any values come back, you know that the address is not unique. If you ARE using the Membership service to create users, then the Membership service will check AUTOMATICALLY and return a code to you if the user's email address is not unique.
The Membership service sits in the System.Web.Security area so you would need a
using System.Web.Security;
reference in your controller.
Here is an example
MembershipCreateStatus createStatus = MembershipService.CreateUser(UserName, Password, Email);
if (createStatus == MembershipCreateStatus.DuplicateEmail)
{
//do something here
}
else
{
//do something here
}
I hope this helps!
The right way to make a generic remote unique validator in MVC can be found in this MVC forum. by counsellorben. It's based on my MVC unique remote validator article http://msdn.microsoft.com/en-us/library/gg508808(VS.98).aspx
A foolproof way of doing this is to create a validation attribute that would query the database for the email address. It would certainly add latency.
An alternative would be to create a unique constraint on the table and intercept SqlException.
I have a sql server table with 2 fields, ID (primary key) and Name (unique key).
I'm using linq to sql to produce model objects for asp.net MVC from this table.
To perform the model validation I've implemented IDateErrorInfo in a partial class
public partial class Company : IDataErrorInfo
{
private Dictionary<string, string> _errors = new Dictionary<string,string>();
partial void OnNameChanging(string value)
{
if (value.Trim().Length == 0)
{
_errors.Add("Name", "Name is required");
return;
}
}
}
This performs as expected with the Model.IsValid property and Html.ValidationSummary helper.
However, this code is just checking that the newly created Company has a Name that is not blank. I also need to check if the Name has been used by another Company in the table.
I could just call the AddCompany method on my repository and catch the SQLException, but this feels dirty.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude="ID")] Company companyToCreate)
{
if (!ModelState.IsValid)
{
return View();
}
//Add to the Database
try
{
_companyRepos.AddCompany(companyToCreate);
return RedirectToAction("Index");
}
catch(SQLException ex)
{
return View("do something to show the error inserting");
}
}
Ideally I want the OnNameChanging method in the partial class to perform the unique key check before I try to add the Company.
Any ideas on how I should be doing this? The only thought I've has so far is to create a fresh database connection in the partial class and query the table.
Thanks
One possibility is for the AddCompany method to return a boolean indicating success or failure of the operation.
However, it is customary to catch this type of error before you attempt to add the record. Put a
bool Exists(string companyName)
method in your Company Repository, and use this to catch the error before attempting to add the record.
The reason this is preferable is you know exactly why the failure occurred. Otherwise, you would either have to catch a custom exception or examine a returned error code.
However you slice it, you're going to obviously have to hit the database to get a list of names already in use. Therefore, I would suggest adding a method in your repository that basically just returns an IList<string> or IEnumerable<string> that simply contains all the distinct names in that table in the DB. Then, in your validating method, simply use that method on the repository to get all the unique names, and implement your check there.