Use Username instead of Email for identity in Asp.net mvc5 - asp.net

Whenever I create a new application with visual studio 2013 express for web and using the individual accounts authentication and i hit the register button I notice that it implements 'Email' instead of 'Username' and the same is in the LoginViewModel as it uses Email to Sign in instead of Username. How can i change this to use Username instead of the default Email without trouble? Also i would like to know how to convert the default 'guid' that is a string type to 'id' (integer type).

The linked question in the accepted answer descibes how to use Email instead of UserName, OP wanted the UserName instead of email which is what I was looking for in Identity 2.0 in an MVC project.
In case anyone else gets here from a google search it is actually very easy to do this. If you look at the register post action it is simply setting the UserName to the Email address. So.........
Add UserName to the RegisterViewModel and add it to the register view.
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control", #placeholder = "Username or email" })
</div>
</div>
In the Register Post Action on the AccountController set the UserName to the ViewModel's UserName and the Email to the ViewModel's Email.
var user = new ApplicationUser { UserName = model.UserName, Email = model.Email };

In order to make email as non unique:
Configure below in IdentityConfig
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};

Please look into this thread
Thread Details :
Assumptions:
Username is unique for each user. It is either input by user or generated by application on registration.
No # symbol allowed in Username.
Remove EmailAddress annotation and define Display text in the default LoginViewModel:
public class LoginViewModel
{
[Required]
[Display(Name = "Username/Email")]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
As user can enter either Username or Email, so we will make # character for validation criteria. Here is the flow to be implemented:
If in the string # is present, apply Email validation else apply Username format validation.
In case of valid Email, first we need to get Username. As it is considered that Username is unique so we can get it with userManager.FindByEmailAsync method.
Use Username for SignIn verification.
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (model.Email.IndexOf('#') > -1)
{
//Validate email format
string emailRegex = #"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}" +
#"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
#".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
Regex re = new Regex(emailRegex);
if (!re.IsMatch(model.Email))
{
ModelState.AddModelError("Email", "Email is not valid");
}
}
else
{
//validate Username format
string emailRegex = #"^[a-zA-Z0-9]*$";
Regex re = new Regex(emailRegex);
if (!re.IsMatch(model.Email))
{
ModelState.AddModelError("Email", "Username is not valid");
}
}
if (ModelState.IsValid)
{
var userName = model.Email;
if (userName.IndexOf('#') > -1)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
else
{
userName = user.UserName;
}
}
var result = await _signInManager.PasswordSignInAsync(userName, model.Password, model.RememberMe, lockoutOnFailure: false);
No special need to change in View. Run the application and test login with Email or Username.
Note: Keep in mind this tutorial follows MVC .Net Identity default structure.

You can also use username and/or password like this
var user = await _userManager.Users
.FirstOrDefaultAsync(u => u.UserName == username || u.Email == username);
if (user != null){
var result = await _signInManager
.PasswordSignInAsync(user.Email, password, false, false);
}

Related

Register with unique email OR unique username in ASP.NET Core MVC

I am working on a system that should allow users to register by a unique username, or unique email. Register by both is possible as well.
I am using the default identity pages with some modifications, here is a sample of the register code:
public class InputModel
{
//[Required] //Validated in server side based on role
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
//[Required] //Validated in server side based on role
[Display(Name = "Username")]
public string UserName { get; set; }
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
string username = Input.UserName ?? Input.Email;
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = username,
Email = Input.Email,
Name = Input.Name,
};
}
}
Basically, if the user entered a username, Username column will be Input.UserName.
if no username (only email), the Username = Input.Email, because obviously it cannot be empty. Now both username and email are equal as the default.
examples:
Username: a#a , Email: a#a , >> no username
Username: xyz , Email: null , >> no email
Username: abc , Email: a#b , >> user entered both username and email
For now, username is always unique and always required (required by identity not the user), but not the case for the email, it can be null as expected but its not unique, I added this line in the startup.cs for the uniqueness:
services.AddIdentity<IdentityUser, IdentityRole>(options => {
options.User.RequireUniqueEmail = true;
})
but now it cannot be null, it give this validation error:
Email '' is invalid.
Please try to implement a setter for Email that converts empty string to null
I added this line in the startup.cs for the uniqueness: options.User.RequireUniqueEmail = true;
but now it cannot be null, it give this validation error: Email '' is invalid.
In source code of UserManager<TUser>.CreateAsync method, we can find that it will call ValidateUserAsync method to valid user before saving the user.
var result = await ValidateUserAsync(user);
in code of ValidateUserAsync method, we can find it call another method ValidateAsync, like below.
var result = await v.ValidateAsync(this, user);
and in ValidateAsync method, if we configured RequireUniqueEmail to true, it will valid Email of current user by calling ValidateEmail method.
if (manager.Options.User.RequireUniqueEmail)
{
await ValidateEmail(manager, user, errors);
}
Go to definition of ValidateEmail method, we can easily find it will check if the email parameter is null or System.String.Empty, or if value consists exclusively of white-space characters.
// make sure email is not empty, valid, and unique
private async Task ValidateEmail(UserManager<TUser> manager, TUser user, List<IdentityError> errors)
{
var email = await manager.GetEmailAsync(user);
if (string.IsNullOrWhiteSpace(email))
{
errors.Add(Describer.InvalidEmail(email));
return;
}
if (!new EmailAddressAttribute().IsValid(email))
{
errors.Add(Describer.InvalidEmail(email));
return;
}
var owner = await manager.FindByEmailAsync(email);
if (owner != null &&
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
{
errors.Add(Describer.DuplicateEmail(email));
}
}
you can try to validate if the email or username already exist by using FindByNameAsync or FindByEmailAsync by using the UserManager. If the return is null then it means that the username/email doesn't exist yet.
Here is the sample approach for this:
//private readonly UserManager<IdentityUser> _userManager ;
var isUniqueUserName = await _userManager.FindByNameAsync(UserNameFromInput)==null;
var isUniqueEmail = await _userManager.FindByEmailAsync(EmailFromInput) == null;
if (!isUniqueUserName || !isUniqueEmail)
{
if(!isUniqueUserName)
ModelState.AddModelError("UserName", "UserName already exist.");
if(!isUniqueEmail)
ModelState.AddModelError("Email", "Email already exist.");
return View(registerViewModel);
}

How to login with "UserName" instead of "Email" in MVC Identity?

I need to set my login to use username instead of email address, how can I change it?
It's actually using the e-mail address as the username, so in the ASPNetUsers table, you'll see both the username and email fields with the email address.
Go into the AccountController, look for Register method (POST).
Change this:
var user = new ApplicationUser { UserName = model.Email, Email = model.Email};
to this:
var user = new ApplicationUser
{
UserName = model.UserName,
Email = model.Email
};
Then go into the Login.cshtml and change all corresponding e-mail model fields to username instead.
Finally, go into the Login method (POST) in the AccountController and change model.Email to model.UserName.
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password,
model.RememberMe, shouldLockout: false);
You also have to make changes in AccountViewModels.cs in order to introduce your new UserName property.
Here is how you do it
var user = await _userManager.Users
.FirstOrDefaultAsync(u => u.UserName == username || u.Email == username);
if (user != null){
var result = await _signInManager
.PasswordSignInAsync(/*email*/user.Email, password, false, false);
/*removed for brevity*/
}
Think you have a user having username=test and email=myemail#example.com then you would like to allow users to authenticate using test instead of myemail#... `
PS. While the answer from #Jason works, sometimes you'd like to authenticate a user with real username and password, not using the email.
In this case use my answer

Adding user data to manage page in MVC5

I've been following the Add Profile Data to User Class part of this tutorial to add more fields to my registration page in MVC 5. So far it's working fine and I have no issues. The problem is now I'm not sure on how that gets displayed on the Manage page where the user can see his profile info like changing his password. For example, I want to add on a first and last name on that page.
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on#ap
Here is a screenshot of the page I'm talking about:
http://puu.sh/dcMmj/82ddd8fc97.PNG
My project is what Visual Studio creates for you with the added first and last name in the registration page. Added this in the following Identity Model like in the tutorial
public class ApplicationUser : IdentityUser
{
public string FirstName{ get; set; }
public string LastName { 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;
}
}
I'm thinking I need to add something here in the ManageController.cs but I'm not sure.
// GET: /Manage/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(User.Identity.GetUserId()),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(User.Identity.GetUserId()),
Logins = await UserManager.GetLoginsAsync(User.Identity.GetUserId()),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(User.Identity.GetUserId()),
// I think it goes in here somewhere
};
return View(model);
}
You can replace your var model with this code:
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var model = new IndexViewModel {
HasPassword = HasPassword(),
PhoneNumber = user.PhoneNumber,
TwoFactor = user.TwoFactorEnabled,
Logins = await UserManager.GetLoginsAsync(user),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(user),
FirstName = user.FirstName
}
Now you can use model.FirstName in your View.

Roles Create New DataBase When I Add a user to a role in MVC 5

i create my own database and add user identity table to this by change the connection string.
now my connection string is this:
when i create a new user it worked well.
but when i change the Register(RegisterViewModel model) in RegisterControler to add a user to a role like this code:
public async Task Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//add user to member role******************
if (!Roles.RoleExists("Member"))
Roles.CreateRole("Member");
Roles.AddUserToRole(model.Email, "Member");
//*******************************************
await SignInAsync(user, isPersistent: 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");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
user registerd but dont add to member Role! and it seem there is another connection string for Roles! beacause whene run this code ASPNETDB.MDF created in App_Data!
Please help me to solve this problem
In order to create roles in asp.net identity, you need to use AspNetRoleManager same as you are currently using AspNetUserManager.
The AspNetUserManager may looks like below.
public class AspNetRoleManager : RoleManager<IdentityRole, string>
{
public AspNetRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{
}
public static AspNetRoleManager Create(IdentityFactoryOptions<AspNetRoleManager> options, IOwinContext context)
{
return new AspNetRoleManager(new RoleStore<IdentityRole, string, IdentityUserRole>(context.Get<YourDataContext>()));
}
}
Then you need to register AspNetRoleManager in the owin startup. Same like the AspNetUserManager.
app.CreatePerOwinContext<AspNetRoleManager>(AspNetRoleManager.Create);
After that you can use it inside the controller to create roles.
var roleManager = HttpContext.GetOwinContext().Get();
// Check for existing roles
var roleManager = HttpContext.GetOwinContext().Get<AspNetRoleManager>();
var roleExists = await roleManager.RoleExistsAsync("Member");
if (!roleExists)
{
var role = new IdentityRole();
role.Name = "Member";
var result = roleManager.CreateAsync(role);
}
Then add new role to the user.
var user = await UserManager.FindByEmailAsync(model.Email);
var roleRsult = UserManager.AddToRole(user.Id, roleName);

MVC5 ApplicationUser custom properties

I am trying to get to grips with the new Membership system introduced in ASP.NET MVC 5 and I've come across a small issue which I am pretty sure you will be able to help me with.
I am going based off this tutorial and have introduced custom properties to ApplicationUser such as Name, Surname, DOB, etc.
However, instead of creating the user, I am trying to update the currently logged in one. I am looking at the controller method which is currently used to change password.
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
string userId = User.Identity.GetUserId();
bool hasLocalLogin = await IdentityManager.Logins.HasLocalLoginAsync(userId);
ViewBag.HasLocalPassword = hasLocalLogin;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasLocalLogin)
{
if (ModelState.IsValid)
{
IdentityResult result = await IdentityManager.Passwords.ChangePasswordAsync(User.Identity.GetUserName(), model.OldPassword, model.NewPassword);
if (result.Success)
{
return RedirectToAction("Manage", new { Message = "Your password has been changed." });
}
else
{
AddErrors(result);
}
}
}
else
{
// User does not have a local password so remove any validation errors caused by a missing OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
// Create the local login info and link it to the user
IdentityResult result = await IdentityManager.Logins.AddLocalLoginAsync(userId, User.Identity.GetUserName(), model.NewPassword);
if (result.Success)
{
return RedirectToAction("Manage", new { Message = "Your password has been set." });
}
else
{
AddErrors(result);
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
How exactly would I go on about updating an ApplicationUser's Surname for example? Do I need to call the DbContext or?
I hope my question is clear.
Explore IdentityManager.Store.UserManagement and IdentityManager.Store.Users.
ApplicationUser cUser = (ApplicationUser) await IdentityManager.Store.Users.FindByNameAsync(HttpContext.User.Identity.Name, new System.Threading.CancellationToken());
cUser.Surname = "New Something";
IdentityResult result1 = await IdentityManager.Store.SaveChangesAsync();
Above code is an example only. Basically you need to explore the Store property of IdentityManage.
When we used the Users object of our database context we ran into other tracking errors. In our application, we would retrieve users as such
var user = UserManager.FindById(userId);
Edit the properties:
user.StorageName = "gooblygook";
//whatever other properties you would like to use
And then we would save it with the UserManager in the controller:
UserManager.Update(user);
This is currently a working solution for us.
Mark the Person object as virtual in your ApplicationUser definition. That worked for me.
public class ApplicationUser : IdentityUser
{
public virtual Person Person { get; set; }

Resources