UserManager.CreateAsync() requires a complex password - asp.net-mvc

I'm designing a register form and using ajax to send data to server.
My model:
public class RegisterViewModel
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Compare("Password")]
public string ConfirmPassword { get; set; }
}
and the action:
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// do stuff...
}
}
}
My problem: result.Succeeded returned false with error message (Password is helloworld):
Passwords must have at least one non letter or digit character.
Passwords must have at least one digit ('0'-'9'). Passwords must have
at least one uppercase ('A'-'Z').
As you can see above, I didn't set any regular expression to Password and ConfirmPassword. So why was I getting that?

Looks like you're using Identity. In the IdentityConfig.cs, there's a section where you configure the requirements for password. Look for this section:
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};

Related

Upload image to blog storage from mvc registration form

I'm trying to build the functionality where the user can upload a profile image to blob storage on registration and then i can be called and included in the manage index view.
I have been able to create the container but no blob is showing inside the container and the url is not being saved to the database either (just NULL) so I'm feeling like its the controller code that's the issue.
CONTROLLER
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model,
HttpPostedFileBase photo, PhotoService photoService)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, PhotoUrl = model.PhotoUrl };
ApplicationUser.PhotoUrl = await photoService.UploadPhotoAsync(photo);
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
//var db = new ApplicationDbContext();
//model.PhotoUrl = await photoService.UploadPhotoAsync(photo);
//db.Users.Add(user);
//db.SaveChanges();
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
MODEL
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string PhotoUrl { get; set; }
}
SERVICE
public class PhotoService : IPhotoService
{
async public Task<string> UploadPhotoAsync(HttpPostedFileBase photoToUpload)
{
if (photoToUpload == null || photoToUpload.ContentLength == 0)
{
return null;
}
string fullPath = null;
Stopwatch timespan = Stopwatch.StartNew();
try
{
CloudStorageAccount storageAccount = StorageUtils.StorageAccount;
// Create the blob client and reference the container
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("images");
// Create a unique name for the images we are about to upload
string imageName = String.Format("task-photo-{0}{1}",
Guid.NewGuid().ToString(),
Path.GetExtension(photoToUpload.FileName));
// Upload image to Blob Storage
CloudBlockBlob blockBlob = container.GetBlockBlobReference(imageName);
blockBlob.Properties.ContentType = photoToUpload.ContentType;
await blockBlob.UploadFromStreamAsync(photoToUpload.InputStream);
fullPath = blockBlob.Uri.ToString();
timespan.Stop();
// log.TraceApi("Blob Service", "PhotoService.UploadPhoto", timespan.Elapsed, "imagepath={0}", fullPath);
}
catch (Exception ex)
{
// log.Error(ex, "Error upload photo blob to storage");
throw;
}
return fullPath;
}
I'm working on this with this tutorial as a guide - https://learn.microsoft.com/en-us/aspnet/aspnet/overview/developing-apps-with-windows-azure/building-real-world-cloud-apps-with-windows-azure/unstructured-blob-storage
Would really appreciate it if someone could let me know how to get this working.
Thanks
"an object reference is required for the nonstatic field method or property".
You need to init your user as follows:
var user = new ApplicationUser {
UserName = model.Email,
Email = model.Email,
PhotoUrl = await photoService.UploadPhotoAsync(photo)
};
var result = await UserManager.CreateAsync(user, model.Password);
Moreover, I found the tutorial defined a interface IPhotoService and implement it by the PhotoService class, but I found you define the PhotoService in your Register action. In order to leverage dependency injection, I would recommend you follow this tutorial.
Also, I would recommend you move the HttpPostedFileBase photo parameter from the Register method into your RegisterViewModel as follows:
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public HttpPostedFileBase Photo { get; set; }
}
Then, you could init your ApplicationUser instance as follows:
var user = new ApplicationUser {
UserName = model.Email,
Email = model.Email,
PhotoUrl = await photoService.UploadPhotoAsync(model.Photo)
};
And for your register view, you could init the input file as follows:
#using (Html.BeginForm("Register", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
//other properties
#Html.TextBoxFor(m => m.Photo, new {type = "file"})
}
TEST:
In general, you need to make sure you have set the multipart/form-data for your register form in the register view. And the parameter photoService in the Register method is not null. Also, you need to make sure your container name images exists, or you need to await container.CreateIfNotExistsAsync(). Additionally, you could leverage debugging via Visual Studio and step into your code to narrow this issue, details about how to debug, you could follow here.

MVC: Custom login page uses same field validators as Register page incorrectly

New to MVC. Using Entity Framework and database first.
Problem
-Register page has fields for username, password, confirm password, and e-mail with validators.
-Login page has username and password fields also with validators. Login page asks for confirm password and e-mail when it shouldn't.
Goal
-Fix this, and maybe learn some proper way of using models in the meantime. I have read many articles and seen many videos but a lot use code first, which I am not using. Very confusing to me.
CODE
My User database doesn't have a Confirm Password field so I made a model...
namespace StoreFront.Models
{
[MetadataType(typeof(RegisterViewModel))]
public partial class User
{
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; }
}
}
My CustomerBaseViewModel has the LoginViewModel and RegisterViewModel that my views should be using...
public class LoginViewModel
{
public int UserID { get; set; }
[DisplayName("Username")]
[Required(ErrorMessage = "Username is required")]
public string UserName { get; set; }
[DisplayName("Password")]
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
}
public class RegisterViewModel
{
public int UserID { get; set; }
[DisplayName("Username")]
[Required(ErrorMessage = "Username is required")]
public string UserName { get; set; }
[DisplayName("Password")]
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
[DisplayName("Confirm Password")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwords must match")]
public string ConfirmPassword { get; set; }
[DisplayName("Email")]
[Required(ErrorMessage = "Email is required")]
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$",
ErrorMessage = "Please enter a valid email")]
public string EmailAddress { get; set; }
}
I have two separate controllers...one for login and one for register. Not sure if you need to look at it but I added anyway
public class LoginController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(User user)
{
using (StoreFrontContext db = new StoreFrontContext())
{
if (!ModelState.IsValid)
{
return View();
}
else
{
var usr = db.Users.Where(u => u.UserName.ToLower() == user.UserName.ToLower() && u.Password == user.Password).FirstOrDefault();
if (usr != null)
{
Session["Username"] = usr.UserName.ToString();
Session["UserID"] = usr.UserName.ToString();
}
else
{
ModelState.AddModelError("", "Username or Password is incorrect.");
}
}
}
return View();
}
}
public class RegisterController : Controller
{
StoreFrontContext db = new StoreFrontContext();
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(User account)
{
if (ModelState.IsValid)
{
using (db)
{
bool duplicate = true;
foreach (var name in db.Users)
{
if (account.UserName.ToLower() == name.UserName.ToString().ToLower())
{
ModelState.AddModelError("", "Username already exists in database!");
duplicate = true;
break;
}
else
{
duplicate = false;
}
}
if (!duplicate)
{
account.IsAdmin = false;
account.DateCreated = DateTime.Now;
db.Users.Add(account);
db.SaveChanges();
ModelState.Clear();
}
}
}
return View();
}
}
Login Index() View uses #model StoreFront.Models.LoginViewModel and Register Index() View uses #model StoreFront.Models.RegisterViewModel
I can't seem to figure out what is wrong, but I believe it's what is being passed through the View or the controller. I'm still not very familiar with the Model part of MVC yet..been very struggling on that, so any pointers on that would also help. Any help would be strongly appreciated!!

Identity 2.1 - User creation using temporary password results in ModelState not valid

I'm trying to use a temporary password to register a user, which calls a password reset via email immediately during the registration process. When testing the code I get an error under the !ModelState.Valid check saying "The password field is required". I defined a password under model.Password for temporary use, so what am I missing here?
// POST: /Account/Register
[HttpPost]
[Authorize(Roles = "SuperAdmin, LEAAdmin, SchoolAdmin")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
model.BackgroundOnFile = false;
var userDetails = new ApplicationUser {
Id = model.Id,
UserName = model.Email,
Title = model.Title,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName,
LEAID = model.LEAID,
SchoolID = model.SchoolID,
BackgroundOnFile = model.BackgroundOnFile,
BoardStart = model.BoardStart,
BoardEnd = model.BoardEnd,
PhoneNumber = model.PhoneNumber,
IsBoardChair = model.IsBoardChair,
IsBoardMember = model.IsBoardMember,
IsAccountActive = model.IsAccountActive,
};
//Declaring in Scope
var result = new IdentityResult();
//Process if new
if(model.Id == "" || model.Id == "undefined" || model.Id == "null" || model.Id == "-9999")
{
model.Password = "D0neW!thTh15";
if (ModelState.IsValid)
{
result = await UserManager.CreateAsync(userDetails, model.Password);
var getPassword = new ForgotPasswordViewModel
{
Email = model.Email
};
//Email PW
await ForgotPassword(getPassword);
}
if(!ModelState.IsValid)
{
string validationErrors = string.Join(",",ModelState.Values.Where(E => E.Errors.Count > 0).SelectMany(E => E.Errors).Select(E => E.ErrorMessage).ToArray());
}
}
//Process if update
if (model.Id != "" || model.Id != "undefined" || model.Id != "null" || model.Id != "-9999")
{
if (ModelState.IsValid)
{
result = await UserManager.UpdateAsync(userDetails);
}
}
//Process Roles
//Remove access if deactivated
if (model.IsAccountActive == false)
{
var user = await UserManager.FindByIdAsync(userDetails.Id);
await UserManager.RemoveFromRolesAsync(userDetails.Id, UserManager.GetRoles(userDetails.Id).ToArray());
await UserManager.AddToRoleAsync(userDetails.Id, "Deactivated");
userDetails.LockoutEnabled = true;
await UserManager.UpdateAsync(userDetails);
}
else
{
//Moved to separate controller, for safety Admin rights can only be done for existing users
if(model.LEAAdmin == true)
{
await UserManager.AddToRoleAsync(userDetails.Id, "LEAAdmin");
}
//LEAAdmin is higher level, so don't allow override to a lower level
if(model.IsBoardChair == true && model.LEAAdmin == false)
{
await UserManager.AddToRoleAsync(userDetails.Id, "SchoolAdmin");
}
else
{
await UserManager.AddToRoleAsync(userDetails.Id, "User");
}
}
if (result.Succeeded)
{
//await SignInManager.SignInAsync(userDetails, isPersistent:false, rememberBrowser:false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
//return RedirectToAction("Index", "Home");
return Json("Password sent!");
}
AddErrors(result);
// If we got this far, something failed, redisplay form
//return View(model);
return View();
}
Here's the code for ApplicationUser, in case you need it:
public class ApplicationUser : IdentityUser
{
//Extra items required to register
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int LEAID { get; set; }
public int SchoolID { get; set; }
//public string Address1 { get; set; }
//public string Address2 { get; set; }
//public string City { get; set; }
//public string State { get; set; }
//Post Code is a string to accomodate future possible Canadian style post codes
//public string PostCode { get; set; }
public bool BackgroundOnFile { get; set; }
public bool IsBoardMember { get; set; }
public bool IsBoardChair { get; set; }
public System.DateTime? BoardStart { get; set; }
public System.DateTime? BoardEnd { get; set; }
public string NominatedBy { get; set; }
public bool IsAccountActive { get; set; }
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
return userIdentity;
}
}
UPDATE:
ViewModel as requested for Registration
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[System.Web.Mvc.CompareAttribute("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
ModelState validation happens in the default ModelBinder, when the values are passed to the server and the RegisterViewModel is created. What this means is that ModelState.IsValid does not automatically change when you update the model. To re-validate the model, you could do something like this:
//Set password here
ModelState.Clear(); // Clear the current ModelState
TryValidateModel(model); // Try to re-validate.
if (ModelState.IsValid) // Now check!
{
...
}
By doing this you are clearing the current ModelState and then forcing revalidation.

How to input data to custom column in userprofile table when creating a new user

I have added a custom column to my user profile table that is called CoID (int).
When I want to create a new user I want to send the value 1 to CoID, but I am experiencing some trouble and cant get it to work properly.
I am using the standard account tables from mvc4.
Here are some of my code
AccountModel.cs
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[DataType(DataType.Custom)]
[Display(Name = "CoID")]
[Compare("CoID", ErrorMessage = "plese insert CoID")]
public int CoID { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public int CoID { get; set; }
}
AccountController.cs
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
model.CoID = 1;
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, model.CoID);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
When executing this code i am getting this error:
The custom Data-type string cannot be null or empty.
I think your call to WebSecurity.CreateUserAndAccount might need looking at.
Perhaps the following might work better:
WebSecurity.CreateUserAndAccount(model.UserName, model.Password,
new { CoID = model.CoID });
As from the anonymous object it can work out what values you're trying to assign to what field names. Otherwise you're just passing through the value and it doesn't know where to put it.
Remove [DataType(DataType.Custom)] or change to [DataType("CoID")].

How to use custom httpcontext user when using custom Membership/Role Provider

Hi guys so I'm using a custom membership provider and custom role provider. And it is logging in using these correctly. I also implemented my own Membership user object so that I can gain access to other user information, and I don't need to load all the data everytime I change page, but I currently cannot get this to work properly. Below is my user object:
public class User : MembershipUser
{
[Required(ErrorMessage = "Username cannot be blank")]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required(ErrorMessage = "Password cannot be blank")]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "User ID")]
public long UserID { get; set; }
[Display(Name = "Family Name")]
[StringLength(50, ErrorMessage = "Family name cannot be longer than 50 characters")]
public string FamilyName { get; set; }
[Display(Name = "Given Name")]
[StringLength(50, ErrorMessage = "Given name cannot be longer than 50 characters")]
public string GivenName { get; set; }
public Company Company { get; set; }
public virtual IIdentity Identity { get; set; }
}
And when the user logs in I call the following login method:
[AllowAnonymous]
[HttpPost]
public ActionResult Login(User model, string returnUrl)
{
FormsAuthentication.SignOut();
if(Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, true);
return RedirectToAction("Index", "");
}
ViewBag.Message = "Failed to login";
return View();
}
But when I call HttpContext.User in index it just contains name/ID, not the rest of my user object. Do I need to create a custom FormAuthentication object? Or is it standard process to store all this user information inside the HttpContext.Session object? Or get my user to extend the System.Security.Principle.IPrinciple? Or even in the Controller.TempData? Or somewhere else I'm not familiar with. I dont want to have to hit the database everytime to load the user data.
Sorry if these are obvious questions I'm fairly new to web development and not sure what the generic way of doing these things are. Trying to use the in build Authorize attributes.
I did it by implementing my own identity. That way it's easy to add as many properties as I need. Below is a code example with custom property friendlyName
public class Identity : IIdentity
{
public Identity(int id, string name, string friendlyName, string roles)
{
this.ID = id;
this.Name = name;
this.FriendlyName = friendlyName;
this.Roles = roles;
}
public Identity(string name, string data)
{
if (string.IsNullOrWhiteSpace(data))
throw new ArgumentException();
string[] values = data.Split('|');
if (values.Length != 3)
throw new ArgumentException();
this.Name = name;
this.ID = Convert.ToInt32(values[0]);
this.FriendlyName = values[1];
Roles = values[2];
}
public string AuthenticationType
{
get { return "Custom"; }
}
public bool IsAuthenticated
{
get { return true; }
}
public override string ToString()
{
return FriendlyName;
}
public string GetUserData()
{
return string.Format("{0}|{1}|{2}", ID, FriendlyName, Roles);
}
public int ID { get; private set; }
public string Name { get; private set; }
public string FriendlyName { get; private set; }
public string Roles { get; private set; }
}
//in controller on login action:
Identity id = new Identity(user.ID, user.Username, "some friendly name", user.Roles);
DateTime expire = DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(id.ID, user.Username, DateTime.Now, expire, false, id.GetUserData());
string hashTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashTicket);
HttpContext.Response.Cookies.Add(cookie);
In global.asax you have:
public override void Init()
{
base.Init();
PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket == null || authTicket.Expired)
return;
Identity id = new Identity(authTicket.Name, authTicket.UserData);
Principal user = new Principal(id);
Context.User = user;
Thread.CurrentPrincipal = user;
}
}

Resources