Changing User Name breaks ASP.NET Identity - c#

I have the following I use to change the UserName
// POST: /Manage/ChangeUserName
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangeUserName(ChangeUserNameViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = UserManager.FindById(User.Identity.GetUserId());
user.UserName = model.NewUserName;
var result = await UserManager.UpdateAsync(user);
if (result.Succeeded)
{
return RedirectToAction("Index", new { Message = ManageMessageId.ChangeUserNameSuccess });
}
AddErrors(result);
return View(model);
}
It successfully changes the name in the DB.
However once I log out. I can't log back in.
The user is still in the DB.
And the Login code
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
Just returns SignInStatus.Failure
No other errors are occurring.
Any ideas why that is occurring?

Related

How to do different login by roles ASP.NET Core 2.2

After a little research and with the help of another fellow from here, i managed to create an "Admin" role and assign it to an user ( I only needed one Admin ). Now i have some troubles with making the login redirect to different pages regarding the role the user has.
I am using asp.net core 2.2 default web site template and authentication selected as individual user account.
Here is my CreateRole method:
private async Task CreateUserRoles(IServiceProvider serviceProvider)
{
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
IdentityResult roleResult;
//Adding Admin Role
var roleCheck = await roleManager.RoleExistsAsync("Admin");
if (!roleCheck)
{
//create the roles and seed them to the database
roleResult = await roleManager.CreateAsync(new IdentityRole("Admin"));
}
//Assign Admin role to the main User here we have given our newly registered
//login id for Admin management
ApplicationUser user = await userManager.FindByEmailAsync("example#gmail.com");
await userManager.AddToRoleAsync(user, "Admin");
}
And here is the post of my Login method with what i've tried so far:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
var status = await _signInManager.UserManager.GetUsersInRoleAsync("Admin");
if (Input.Email.Contains(status.ToString()))
{
return LocalRedirect(returnUrl);
}
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
// If we got this far, something failed, redisplay form
return Page();
}
You need to fetch the user's role from the database:
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
var user = await _signInManager.UserManager.FindByEmailAsync(Input.Email);
// Get the roles for the user
var roles = await _signInManager.UserManager.GetRolesAsync(user);
if (roles.Any(role=>"Admin".Equals(role)))
{
return LocalRedirect("YourURL");
}
Or use UserManager.IsInRoleAsync function :
var user = await _signInManager.UserManager.FindByEmailAsync(Input.Email);
var isInRole = await _signInManager.UserManager.IsInRoleAsync(user, "Admin");
if (isInRole)
{
return LocalRedirect("YourURL");
}
Right after the user logged in you can check their role using HttpContext.User.IsInRole function:
var user = await _userManager.FindByEmailAsync(Input.Email);
var result = await _signInManager.PasswordSignInAsync(user, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
var principal = await _signInManager.ClaimsFactory.CreateAsync(user);
if (principal.IsInRole("Admin")) {
return LocalRedirect("SomeSpecialUrlForAdmin");
}
else {
return LocalRedirect(returnUrl);
}
}

Core 2 Identity - Regenerate EmailConfirmationLink

I have a fairly standard implementation of Identity in my Core 2 application.
I have enabled the requirement for a confirmation email. This all works well - it generates and sends the email, with a valid link, which when clicks confirms the user's account. No problems there.
The situation is though, that if an email goes missing/is deleted/never arrives for whatever reason.
What I am doing, is using the standard login method, and if IsNotAllowed then I push them towards a page which tells them they need to activate their account, check their email etc.
On that page, I have a Form with a button which Posts to a controller to regenerate and resend the email.
I literally copy and pasted the code from the standard Register method into my custom method.
The problem is when clicking the link the Activation fails. I get an InvalidToken error.
Any ideas why and how to fix?
Login Method
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email,
model.Password, model.RememberMe, lockoutOnFailure : false);
if (result.IsNotAllowed)
{
var user = await _userManager.FindByEmailAsync(model.Email);
EmailConfirmationViewModel viewModel = new EmailConfirmationViewModel();
viewModel.Email = model.Email;
return RedirectToAction("AwaitingEmailConfirmation", viewModel);
}
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return RedirectToAction("Index", "Races");
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToAction(nameof(Lockout));
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Register Method
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName,
MobileNumber = model.MobileNumber,
Marketing = model.Marketing,
Newsletter = model.Newsletter,
Source = "Website"
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
//await _createContact.CreateContactAsync(model.Email, model.FirstName,
//model.LastName, model.Marketing, model.Newsletter);
var fields = new Dictionary<string, string>();
fields.Add("firstname", model.FirstName);
fields.Add("lastname", model.LastName);
fields.Add("newsletter", model.Newsletter.ToString());
fields.Add("marketing", model.Marketing.ToString());
fields.Add("source", "Website");
string publicaccountid = "55ebcc8b-b23f-4843-9dcb-1df08811de65";
var createcontact = ElasticEmailClient.Api.Contact.AddAsync(publicAccountID: publicaccountid,
email : model.Email, field : fields, sendActivation : false);
//await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created a new account with password.");
EmailConfirmationViewModel viewModel = new EmailConfirmationViewModel();
viewModel.Email = user.Email;
return RedirectToAction("AwaitingEmailConfirmation", viewModel);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Resend Email Method
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> ResendConfirmationEmail(EmailConfirmationViewModel model)
{
var user = await _userManager.FindByEmailAsync(model.Email);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
return RedirectToAction("AwaitingEmailConfirmation", model);
}

Why signinmanager.passwordsigninasync method return failure all time in MVC5

Here is my Login method:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
//This doesn't count login failures towards account lockout
//To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Here is my Authorize filter:
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var userEmailId = httpContext.Session["userName"];
bool authorize = false;
foreach (var role in allowedroles)
{
/* getting user form current context */
var user = context.employees.Where(m => m.emailId == userEmailId && m.Role == role);
if (user.Count() > 0)
{
authorize = true; /* return true if Entity has current user(active) with specific role */
}
}
return authorize;
}
Here is my action method:
[AuthAttribute]
[CustomAuthorize("Admin", "SuperAdmin")]
public ActionResult GetEmployeeList(string sortOrder, string currentFilter, string searchString, int? page)
{
return View(db.GetListviewData().ToList());
}
I have try to used the authorize filter to check the user role from the database but when I try to login in my application SignInManager.PasswordSignInAsync method returns failure every time.
Please help me, thanks in advance
I had the same problem, but I realized it only occurred after a user changed their email (obviously then UserName no longer equals Email).
Take a look at the signature for PasswordSignInAsync, the 1st parameter is UserName, yet the default code from Visual Studio project templates has what you and I both had, Email.
Change:
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
To:
var result = await SignInManager.PasswordSignInAsync(user.UserName, model.Password, model.RememberMe, shouldLockout: false);

MVC External login - How to skip association form

I want to skip external login register - association form
I am using google external login with MVC5,
If you login with google account, it will show you above screen after entering google credentials first time. I just want to skip this screen.
Above view get return from
[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 });
}
}
return View("ExternalLoginConfirmation", new
ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
Above code return ExternalLoginConfirmation view and shows above screen. on submit above form it will submitted to below action
public async Task
ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model,
string returnUrl)
To skip above register screen, I need to call ExternalLoginConfirmation from ExternalLoginCallback, so How can I do that.
Well, didn't take too long to put this together. I haven't tested it, but if it doesn't work as-is, should be really close. I mostly just copied the code from ExternalLoginConfirmation to ExternalLoginCallback:
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// Added this section to automatically create account if we have an email address
if (!string.IsNullOrEmpty(loginInfo.Email))
{
var user = new ApplicationUser { UserName = loginInfo.Email, Email = loginInfo.Email };
var createUserResult = await UserManager.CreateAsync(user);
if (createUserResult.Succeeded)
{
var addLoginResult = await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
if(addLoginResult.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
return RedirectToLocal(returnUrl);
}
}
}
// 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 });
}

Can't get User.Identity.GetUserId()

I've got a simple web application and I can't user User.Identity.GetUserId() method. Every time I use it, it returns back null, or 0 if I use User.Identity.GetUserId<int>().
For example in this code part.
public async Task<ActionResult> SetPassword(SetPasswordViewModel model)
{
var userId = User.Identity.GetUserId();
if (ModelState.IsValid)
{
var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
}
AddErrors(result);
}
Please , someone help me with this problem. Thanks again.

Categories