MVC 5: On password change using UserManger, Invalid length for a Base-64 char array or string - asp.net-mvc

In my MVC(5.1) application. I wanted to add password change.
This is what I have done,
public class AccountController : Controller
{
private readonly ILicenserepository _licenserepository;
private readonly IUserRepository _userRepository;
public AccountController(ILicenserepository licenserepository, IUserRepository userRepository)
{
_licenserepository = licenserepository;
_userRepository = userRepository;
//UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new DatePickerDbContext()));
....
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
bool hasPassword = HasPassword();
ViewBag.HasLocalPassword = hasPassword;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasPassword)
{
if (ModelState.IsValid)
{
string userId = User.Identity.GetUserId();
IdentityResult result = UserManager.ChangePassword(userId, model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
AddErrors(result);
}
}
}
else
{
// User does not have a 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)
{
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
}
else
{
AddErrors(result);
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
}
When I run the application I get this error on browser
Invalid length for a Base-64 char array or string.
at line
IdentityResult result = UserManager.ChangePassword(userId, model.OldPassword, model.NewPassword);
I don't know what's wrong.
Is it because I haven't initialized my UserManger in constructor?
when I inspect the values in userId, model.OldPassword and model.NewPassword, all the values are as I expect. userId holds userId value, oldpassword holds oldpassowrd value and new password holds newpassword value provided from user.

It was my stupid mistake
I had created User like this before
var user = new ApplicationUser
{
FirstName = model.FirstName,
LastName = model.Lastname,
Phone = model.Phone,
Email = model.EmailId,
Company = model.Company,
PasswordHash = model.ConfirmPassword
UserName = model.UserName,
DateTimeRegistered = DateTime.UtcNow,
License = model.SelectedLicense,
IsApproved = true,
};
var result = UserManager.Create(user);
Here, the password would not be hashed and be saved as exact string user provides.
That was creating error.
should have provided password while creating the user like
var user = new ApplicationUser
{
FirstName = model.FirstName,
LastName = model.Lastname,
Phone = model.Phone,
Email = model.EmailId,
Company = model.Company,
UserName = model.UserName,
DateTimeRegistered = DateTime.UtcNow,
License = model.SelectedLicense,
IsApproved = true,
};
var result = UserManager.Create(user,model.ConfirmPassword);

Related

Token generated outside controller is too long and it's rejected by ConfirmEmail on Controller in MVC C#

I am updating my question as I have made some progress.
Thanks in advance for your support.
Question:
I am using GenerateEmailConfirmationTokenAsync to create a token outside the Controller (it's working fine), but somehow my token is longer than the ones created within the Controller using the GenerateEmailConfirmationTokenAsync and therefore the ConfirmEmail action rejects the token. (Error: Invalid Token).
I have tried Machinekey on webconfig, HttpUtility.UrlEncode, but I am still stuck. How to sort out the Invalid Token error on Controller ConfirmEmail?
Guys, can you help me please!
Thanks.
Here is my Code:
RegisterUser (outside Controller)
public async Task RegisterUserAsync()
{
var store = new UserStore<ApplicationUser>(db);
var UserManager = new ApplicationUserManager(store);
var query = from c in db.Customer
where !(from o in db.Users
select o.customer_pk)
.Contains(c.customer_pk)
select c;
var model = query.ToList();
if (query != null)
{
foreach (var item in model)
{
var user = new ApplicationUser { UserName = item.email, Email = item.email, customerId = item.customerId};
var result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id);
SmtpClient client = new SmtpClient();
MailMessage message = new MailMessage
{
IsBodyHtml = true
};
message.Subject = "Confirm Email";
message.To.Add(item.email1);
message.Body = "Please confirm your account by clicking here";
client.SendAsync(message, "userToken");
//Assign Role User Here
await UserManager.AddToRoleAsync(user.Id, "Client");
}
}
}
}
SendEmailConfirmation method (outside Controller)
public async Task<string> SendEmailConfirmationTokenAsync(string userID)
{
var store = new UserStore<ApplicationUser>(db);
var UserManager = new ApplicationUserManager(store);
var url = new UrlHelper();
var provider = new DpapiDataProtectionProvider("MyApp");
UserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
provider.Create("EmailConfirmation"));
string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
string encodedCode = HttpUtility.UrlEncode(code);
string callbackUrl = "http://localhost/Accounts/ConfirmEmail?userId=" + userID + "&code=" + encodedCode;
return callbackUrl;
}
where db is
ApplicationdDbContext db = new ApplicationdDbContext();
ConfirmEmail within the Identity Controller (Accounts Controller) - I've created Accounts instead of Account controller but it's working fine.
//
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var confirmed = await UserManager.IsEmailConfirmedAsync(userId);
if (confirmed)
{
return RedirectToLocal(userId);
}
var result = await UserManager.ConfirmEmailAsync(userId, code); //Here I get the error (Token Invlaid, despite the token and userId being displayed)
if (result.Succeeded)
{
ViewBag.userId = userId;
ViewBag.code = code;
}
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
[HttpPost]
[ValidateAntiForgeryToken]
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(SetPasswordViewModel model, string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.AddPasswordAsync(userId, model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(userId);
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToLocal(userId);
}
ViewBag.userId = userId;
ViewBag.code = code;
AddErrors(result);
return View(model);
}
I have worked for hours in this code but until now I can't sort it out.
Thanks for any comments or solution. The reason for this approach is that I have to use task scheduler(I'm using fluentscheduler, which is working fine).

add role in database in asp mvc identity

i need to when user regiter add in tabel AspNetRole add user id and role id .
but when i create a user show me this error .
how can i insert role in database ?
/*************************************************************************************************/
identityconfig :
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{
}
public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
return new ApplicationRoleManager(new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()));
}
}
public static class SecurityRole
{
public const string Admin = "admin";
public const string Accounting = "accounting";
}
StartupAuth :
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
AccountController :
public ApplicationRoleManager RoleManager
{
get
{
return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
}
private set
{
_roleManager = value;
}
}
public async Task<ActionResult> Register(RegisterViewModel model, HttpPostedFileBase IamgeProfile)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Username, Email = model.Email };
user.Name = model.Name;
user.Family = model.Family;
user.Address = model.Address;
user.BankName = model.BankName;
user.City = model.City;
user.Ostan = model.Ostan;
user.PhoneNumber = model.PhoneNumber;
user.HomeNumber = model.HomeNumber;
user.ShabaNo = model.ShabaNo;
user.PostaCode = model.PostaCode;
user.NationalCode = model.NationalCode;
if (IamgeProfile != null)
{
IamgeProfile = Request.Files[0];
var ext = System.IO.Path.GetExtension(IamgeProfile.FileName);
if (ext == ".jpeg" || ext == ".jpg" || ext == ".png")
{
string filename = model.Name + model.Family + model.NationalCode + ext;
IamgeProfile.SaveAs(Server.MapPath(#"~/Images/UserImageProfile/" + filename));
user.IamgeProfile = filename;
}
}
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
await UserManager.AddToRoleAsync(user.Id, role: SecurityRole.Accounting);
var 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 this link: link");
ViewBag.Link = callbackUrl;
return View("DisplayEmail");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
To add a role into AspNetRoles you can do this in your Seed() or other startup method:
if (!context.Roles.Any(r => r.Name == "Admin"))
{
var store = new RoleStore<IdentityRole>(context);
var manager = new RoleManager<IdentityRole>(store);
var role = new IdentityRole { Name = "Admin" };
manager.Create(role);
}
https://msdn.microsoft.com/en-us/library/dn613057(v=vs.108).aspx

Authorize Attribute with Roles

I want to implement my custom authorization, I wonder what is wrong with my code even I got the user credentials correctly it still redirects me to my Login Method, please see the code below
Edit: I have successfully implemented the Authorize Attribute with Roles, for future readers please see code below
Login Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login (AdminViewModels.Login viewModel, string returnURL)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
PasswordHasher passwordVerify = new PasswordHasher();
var query = (from acc in db.accounts.Where(x => x.username == viewModel.Username)
select new { acc.username, acc.password}).FirstOrDefault();
if (query != null)
{
if (ModelState.IsValid)
{
var result = passwordVerify.VerifyHashedPassword(query.password, viewModel.Password);
switch (result)
{
case PasswordVerificationResult.Success:
//set forms ticket to be use in global.asax
SetupFormsAuthTicket(viewModel.Username, viewModel.rememeberMe);
return RedirectToLocal(returnURL);
case PasswordVerificationResult.Failed:
ModelState.AddModelError("", "Wrong Username or Password");
return View(viewModel);
}
}
}
return View(viewModel);
}
Forms Auth Ticket
private account SetupFormsAuthTicket(string userName, bool persistanceFlag)
{
account user = new account();
var userId = user.id;
var userData = userId.ToString(CultureInfo.InvariantCulture);
var authTicket = new FormsAuthenticationTicket(1, //version
userName, // user name
DateTime.Now, //creation
DateTime.Now.AddMinutes(20), //Expiration
persistanceFlag, //Persistent
userData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
return user;
}
Global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//take out user name from cookies
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string[] roles = null;
trainingEntities db = new trainingEntities();
//query database to get user roles
var query = (from acc in db.account_roles where acc.account.username == username select acc.role.role_name).ToArray();
roles = query;
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles);
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Now you can use [Authorize(Roles = "Admin")]
to any action method or on top of controller
I have successfully implemented the Authorize Attribute with Roles, for future readers please see code below.
Login Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login (AdminViewModels.Login viewModel, string returnURL)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
PasswordHasher passwordVerify = new PasswordHasher();
var query = (from acc in db.accounts.Where(x => x.username == viewModel.Username)
select new { acc.username, acc.password}).FirstOrDefault();
if (query != null)
{
if (ModelState.IsValid)
{
var result = passwordVerify.VerifyHashedPassword(query.password, viewModel.Password);
switch (result)
{
case PasswordVerificationResult.Success:
//set forms ticket to be use in global.asax
SetupFormsAuthTicket(viewModel.Username, viewModel.rememeberMe);
return RedirectToLocal(returnURL);
case PasswordVerificationResult.Failed:
ModelState.AddModelError("", "Wrong Username or Password");
return View(viewModel);
}
}
}
return View(viewModel);
}
FormsAuthTicket
private account SetupFormsAuthTicket(string userName, bool persistanceFlag)
{
account user = new account();
var userId = user.id;
var userData = userId.ToString(CultureInfo.InvariantCulture);
var authTicket = new FormsAuthenticationTicket(1, //version
userName, // user name
DateTime.Now, //creation
DateTime.Now.AddMinutes(20), //Expiration
persistanceFlag, //Persistent
userData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
return user;
}
Global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//take out user name from cookies
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string[] roles = null;
trainingEntities db = new trainingEntities();
//query database to get user roles
var query = (from acc in db.account_roles where acc.account.username == username select acc.role.role_name).ToArray();
roles = query;
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles);
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Now you can use [Authorize(Roles = "Admin")]
to any action method or on top of controller
as I see in ControllerLogin attribute it is now being applied in a variable, when it should be applied to a method or a class
[CustomAuthorization(UserRole="Admin")]
// GET: Manage
private trainingEntities db = new trainingEntities();
public ActionResult Index()
{
return View();
}
Private trainingEntities dB = new TrainingEntities();
[CustomAuthorization(UserRole="Admin")]
Public ActionResult Index()
{
//yourcode
}

I can login only when user's UserName is exacly the same Email in database

I created fresh ASP .NET MVC 5 web application with individual accounts(default template). When I create user if I give him different UserName and Email I cannot login. If Email and UserName are the same I can login.
I used default template. What I need change to let UserName and Email be different?
Put in other words: The problem is that I can login only the user which have UserName and Email properties equal.
Put in other other words: There are two users, one have: Email:mailbox#gmail.com, UserName:mailbox#gmail.com I can login as this user. Second user Email:mailbox#gmail.com, UserName:SOMETHING I cannot log as this user.
Account controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Owin;
using WebApplication2.Models;
namespace WebApplication2.Controllers {
[Authorize]
public class AccountController : Controller {
private ApplicationUserManager _userManager;
public AccountController() {
}
public AccountController(ApplicationUserManager userManager) {
UserManager = userManager;
}
public ApplicationUserManager UserManager {
get {
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set {
_userManager = value;
}
}
//
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl) {
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) {
if (ModelState.IsValid) {
var user = await UserManager.FindAsync(model.Email, model.Password);
if (user != null) {
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
} else {
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register() {
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> 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) {
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);
}
//
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code) {
if (userId == null || code == null) {
return View("Error");
}
IdentityResult result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded) {
return View("ConfirmEmail");
} else {
AddErrors(result);
return View();
}
}
//
// GET: /Account/ForgotPassword
[AllowAnonymous]
public ActionResult ForgotPassword() {
return View();
}
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model) {
if (ModelState.IsValid) {
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id))) {
ModelState.AddModelError("", "The user either does not exist or is not confirmed.");
return View();
}
// 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.GeneratePasswordResetTokenAsync(user.Id);
// var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking here");
// return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ForgotPasswordConfirmation
[AllowAnonymous]
public ActionResult ForgotPasswordConfirmation() {
return View();
}
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code) {
if (code == null) {
return View("Error");
}
return View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model) {
if (ModelState.IsValid) {
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null) {
ModelState.AddModelError("", "No user found.");
return View();
}
IdentityResult result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded) {
return RedirectToAction("ResetPasswordConfirmation", "Account");
} else {
AddErrors(result);
return View();
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ResetPasswordConfirmation
[AllowAnonymous]
public ActionResult ResetPasswordConfirmation() {
return View();
}
//
// POST: /Account/Disassociate
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Disassociate(string loginProvider, string providerKey) {
ManageMessageId? message = null;
IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded) {
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
await SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
} else {
message = ManageMessageId.Error;
}
return RedirectToAction("Manage", new { Message = message });
}
//
// GET: /Account/Manage
public ActionResult Manage(ManageMessageId? message) {
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
ViewBag.HasLocalPassword = HasPassword();
ViewBag.ReturnUrl = Url.Action("Manage");
return View();
}
//
// POST: /Account/Manage
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(ManageUserViewModel model) {
bool hasPassword = HasPassword();
ViewBag.HasLocalPassword = hasPassword;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasPassword) {
if (ModelState.IsValid) {
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
if (result.Succeeded) {
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
} else {
AddErrors(result);
}
}
} else {
// User does not have a 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) {
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (result.Succeeded) {
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
} else {
AddErrors(result);
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl) {
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl) {
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null) {
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null) {
await SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
} else {
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
//
// POST: /Account/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider) {
// Request a redirect to the external login provider to link a login for the current user
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
}
//
// GET: /Account/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback() {
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null) {
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
if (result.Succeeded) {
return RedirectToAction("Manage");
}
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl) {
if (User.Identity.IsAuthenticated) {
return RedirectToAction("Manage");
}
if (ModelState.IsValid) {
// Get the information about the user from the external login provider
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null) {
return View("ExternalLoginFailure");
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user);
if (result.Succeeded) {
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded) {
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);
// SendEmail(user.Email, callbackUrl, "Confirm your account", "Please confirm your account by clicking this link");
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff() {
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure() {
return View();
}
[ChildActionOnly]
public ActionResult RemoveAccountList() {
var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId());
ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
}
protected override void Dispose(bool disposing) {
if (disposing && UserManager != null) {
UserManager.Dispose();
UserManager = null;
}
base.Dispose(disposing);
}
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager {
get {
return HttpContext.GetOwinContext().Authentication;
}
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent) {
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, await user.GenerateUserIdentityAsync(UserManager));
}
private void AddErrors(IdentityResult result) {
foreach (var error in result.Errors) {
ModelState.AddModelError("", error);
}
}
private bool HasPassword() {
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null) {
return user.PasswordHash != null;
}
return false;
}
private void SendEmail(string email, string callbackUrl, string subject, string message) {
// For information on sending mail, please visit http://go.microsoft.com/fwlink/?LinkID=320771
}
public enum ManageMessageId {
ChangePasswordSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
Error
}
private ActionResult RedirectToLocal(string returnUrl) {
if (Url.IsLocalUrl(returnUrl)) {
return Redirect(returnUrl);
} else {
return RedirectToAction("Index", "Home");
}
}
private class ChallengeResult : HttpUnauthorizedResult {
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null) {
}
public ChallengeResult(string provider, string redirectUri, string userId) {
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context) {
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null) {
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}
#endregion
}
}
Login view:
#using WebApplication2.Models
#model LoginViewModel
#{
ViewBag.Title = "Log in";
}
<h2>#ViewBag.Title.</h2>
<div class="row">
<div class="col-md-8">
<section id="loginForm">
#using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Use a local account to log in.</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Log in" class="btn btn-default" />
</div>
</div>
<p>
#Html.ActionLink("Register as a new user", "Register")
</p>
#* Enable this once you have account confirmation enabled for password reset functionality
<p>
#Html.ActionLink("Forgot your password?", "ForgotPassword")
</p>*#
}
</section>
</div>
<div class="col-md-4">
<section id="socialLoginForm">
#Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { Action = "ExternalLogin", ReturnUrl = ViewBag.ReturnUrl })
</section>
</div>
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
DISCLAIMER: I do not ask how to create users I did.
var store = new UserStore<ApplicationUser>(context);
var userManager = new ApplicationUserManager(store);
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
string roleName = "Admin";
if (!roleManager.RoleExists(roleName)) {
roleManager.Create(new IdentityRole(roleName));
}
roleName = "TeleMarketer";
if (!roleManager.RoleExists(roleName)) {
roleManager.Create(new IdentityRole(roleName));
}
roleName = "Marketer";
if (!roleManager.RoleExists(roleName)) {
roleManager.Create(new IdentityRole(roleName));
}
var user = new ApplicationUser() { Email = "informatyka4444#wp.pl", UserName = "Robert" };
userManager.Create(user, "TestPass44!");
userManager.AddToRole(user.Id, "Admin");
user = new ApplicationUser() { Email = "s8359#pjwstk.edu.pl", UserName = "Admin" };
userManager.Create(user, "TestPass44!");
userManager.AddToRole(user.Id, "Admin");
user = new ApplicationUser() { Email = "marketer#wp.pl", UserName = "Marketer" };
userManager.Create(user, "TestPass44!");
userManager.AddToRole(user.Id, "TeleMarketer");
user = new ApplicationUser() { Email = "telemarketer#wp.pl", UserName = "TeleMarketer" };
userManager.Create(user, "TestPass44!");
userManager.AddToRole(user.Id, "Marketer");
The problem is actually very simple and quite reproducible. It looks like the guys at Microsoft took a shortcut and assumed that everyone would always want their email address to be their username. In my case I had a strict requirement where the user name cannot be an email address.
I was able to make the changes necessary to use both fields as truly intended, but when I tried to log in using the scaffolded/provided/out-of-the-box /Manage/Login.cshtml view it absolutely forced me to use an email address as the user name. This is not the intended behavior because even the under the covers method specifies "username" as the parameter. I don't have this problem when I use my own view/processing to log into the application. And yes, my stuff goes through the same (and official) SignInManger.
The validation happening on the client side is what was forcing us to use an email address field value. This doesn't appear to be exposed anywhere that is configurable... and it took me the LONGEST time to find it. I actually felt pretty stupid for about 5 minutes.
The solution (for my scenario) was to...
Open the /Models/AccountViewModels.cs file.
Navigate to the LoginViewModel class.
On the Email property remove (or comment out) the [EmailAddress] decorator Attribute.
Modify the Display decorator Attribute (specifically, the Name property) to something appropriate -- fyi, this is what will be shown in the #Html.LabelFor() and validation messages as well.
Clear out the browser cache for that site. You can try a hard refresh, but YMMV depending on your browser.
Give it a whirl
In my case, problem solved. I hope this helps (even though it's nearly a year later)!
If you look closely into the generated code you will find the LogIn method:
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) {
if (ModelState.IsValid) {
var user = await UserManager.FindAsync(model.Email, model.Password);
if (user != null) {
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
} else {
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Here you see that the user is being found by using his email address - that is why u have to login using the Email. If you want to change the behaviour - simply modify the LoginViewModel class and the login View.
Take a look at what you're doing in your Register action:
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
When a user registers, they supply a username and an email. But you're ignoring the username and using the email for both values. I imagine this would be highly unintuitive for users registering in your application.
Try using the username value as the username value:
var user = new ApplicationUser() { UserName = model.UserName, Email = model.Email };
Asp.Net Identity uses username and password to authenticate if you want to login with either username or password check if email is supplied then use the userManager.FindByEmailAsync(model.Email); to retrieve corresponding username and login check my login code below
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
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);

MVC 5.1 identity 2 add new Role to myself need to log out

When I add new Role to my own account I have to log out and log back in so this role will start working. Is there a way to re-load roles on the fly (after adding/deleting) ?
I'm using Individual Accounts stored in Ms SQL Server 2012 in MVC 5.1.2 and Identity v. 2.0.0
Below is controller code:
// GET: /Users/Edit/1
public async Task<ActionResult> Edit(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await UserManager.FindByIdAsync(id);
if (user == null)
{
return HttpNotFound();
}
var userRoles = await UserManager.GetRolesAsync(user.Id);
return View(new EditUserViewModel()
{
Id = user.Id,
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
CustomerID = user.CustomerID,
siteID = user.SiteID,
RolesList = RoleManager.Roles.ToList().Select(x => new SelectListItem()
{
Selected = userRoles.Contains(x.Name),
Text = x.Name,
Value = x.Name
}),
SitesList = db.sites.ToList().Select(y=> new SelectListItem()
{
Selected= user.SiteID==y.siteID,
Text = y.siteCode,
Value= y.siteID.ToString()
})
});
}
//
// POST: /Users/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Email,Id,FirstName,LastName,CustomerID,siteID")] EditUserViewModel editUser, params string[] selectedRole)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByIdAsync(editUser.Id);
if (user == null)
{
return HttpNotFound();
}
user.UserName = editUser.Email;
user.Email = editUser.Email;
user.FirstName = editUser.FirstName;
user.LastName = editUser.LastName;
user.CustomerID = editUser.CustomerID;
user.SiteID = editUser.siteID;
var userRoles = await UserManager.GetRolesAsync(user.Id);
selectedRole = selectedRole ?? new string[] { };
var result = await UserManager.AddUserToRolesAsync(user.Id, selectedRole.Except(userRoles).ToList<string>());
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
result = await UserManager.RemoveUserFromRolesAsync(user.Id, userRoles.Except(selectedRole).ToList<string>());
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
return RedirectToAction("Index");
}
editUser.RolesList = RoleManager.Roles.ToList().Select(x => new SelectListItem()
{
//Selected = userRoles.Contains(x.Name),
Text = x.Name,
Value = x.Name
});
ModelState.AddModelError("", "Something failed.");
return View(editUser);
}

Resources