MVC 5 ASP.NET Identity - CreateAsync invalid user id - c#

I have two websites using one database, I use asp.net identity (2.2.1.40403) and I have a problem I can't understand. Now, this is a third time this happened and I have no idea where the problem can be.
I have a register and send email method like this
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new User { UserName = model.Email, Email = model.Email, RegisterDate = DateTime.Now };
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 http://go.microsoft.com/fwlink/?LinkID=320771
await SendConfirmationEmail(user);
return View("ConfirmationEmailSent");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
private async Task SendConfirmationEmail(Dal.Models.User user)
{
// 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, "Potvrzení Vašeho účtu", "Prosím potvrďte svou emailovou adresu kliknutím zde.");
}
What happened is that when user registered he received URL when userId was set to 3d847c51-7217-49fe-ae9d-d8e46e291559, but in database the user was created with 95789d6e-b66e-4c9e-8ee4-fe384b82e838. I don't understand how this can happen. By the way there is no user in database with Id 3d847c51-7217-49fe-ae9d-d8e46e291559. Do you have any idea why and how this can happen?

I would suggest calling back the user by an identifier after create was successful to make sure the properties match up.
//...other code removed for brevity
var user = new User { UserName = model.Email, Email = model.Email, RegisterDate = DateTime.Now };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//pick one
//user = await UserManager.FindById(user.Id);
//user = await UserManager.FindByName(user.UserName);
user = await UserManager.FindByEmailAsync(user.Email);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
await SendConfirmationEmail(user);
return View("ConfirmationEmailSent");
}
AddErrors(result);
//...other code removed for brevity

I am also suspect that issue is related to UserManager.CreateAsync() method. You are using correctly. I will rather use manually generated user id instead generated by UserManager.
In your case will be:
var user = new User { UserName = model.Email, Email = model.Email, RegisterDate = DateTime.Now };
user.Id = Guid.NewGuid().ToString();
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SendConfirmationEmail(user);
return View("ConfirmationEmailSent");
}

Related

Using UserManager to change the email of a user

I am trying to implement a feature to change the email of a user. Asp net core identity by default allows the user to change an email however it requires a confirmation link. I was wondering if it was possible to not use the confirmation link and just edit the function to update the email of the user with the new email. Thanks in advance
I have tried doing
await _userManager.ChangeEmailAsync(user, Input.NewEmail, code);
and
var changingser = _userManager.Users.First(u => u.Email.Equals(email));
changingser.Email = Input.NewEmail;
Which did not seem to work
Email.cshtml: Change Email function
public async Task<IActionResult> OnPostChangeEmailAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!ModelState.IsValid)
{
await LoadAsync(user);
return Page();
}
var email = await _userManager.GetEmailAsync(user);
if (Input.NewEmail != email)
{
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
await _userManager.ChangeEmailAsync(user, Input.NewEmail, code);
/* var callbackUrl = Url.Page(
"/Account/ConfirmEmailChange",
pageHandler: null,
values: new { userId = userId, email = Input.NewEmail, code = code },
protocol: Request.Scheme);*/
//await _emailSender.SendEmailAsync(
// Input.NewEmail,
// "Confirm your email",
// $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
StatusMessage = "Email has been changed";
return RedirectToPage();
}
StatusMessage = "Your email is unchanged.";
return RedirectToPage();
}
Edit: Failed invalid token?
Yes, you can.
Use SetEmailAsync to see the user email.
This will still require the email to be 'confirmed' so generate a token, then immediately confirm the new email address.
Something like:
await _userManager.SetEmailAsync(user, Input.NewEmail);
var token = await _userManager.GenerateEmailConfirmationTokenAsync(existingUser);
await _userManager.ConfirmEmailAsync(existingUser, token);

Register user with identity

I'm in a project where only administrators can add new members to the system. But I'm getting the following error:
The data protection operation was unsuccessful. This may have been caused by not loading the user profile into the user context of the current thread, which can happen when the thread is impersonating.
Description: An unhandled exception occurred during the execution of the current WEB request. Examine the stack trace for more information about the error and where it originated in the code.
Exception Details: System. Security. Cryptography. Cryptographicexception: The data protection operation was unsuccessful. This may have been caused by not loading the user profile into the user context of the current thread, which can happen when the thread is impersonating.
The problem only happens after the project is published.
My Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model, HttpPostedFileBase AvatarAux = null)
{
ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string idRepresentanteLogado = identity.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid).Value;
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
IsInativo = model.IsInativo,
DataCadastro = model.DataCadastro,
Nome = model.Nome,
Id_Tipo_Representante = model.Tipo_Representante.Id,
UserResponsavel = _userManager.FindById(idRepresentanteLogado),
Avatar = AvatarAux != null ? ImagemConverter.ImagemToByte(AvatarAux) : ImagemConverter.ObterImagemUsuarioDefault(),
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code }, protocol: Request.Url.Scheme);
await _userManager.SendEmailAsync(user.Id, "Confirme sua Conta", "Por favor confirme sua conta clicando neste link: <a href='" + callbackUrl + "'></a>");
ViewBag.Link = callbackUrl;
return View("DisplayEmail");
}
AddErrors(result);
return View(model);
}
I managed to get around the problem by commenting on the following code:
if (result.Succeeded)
{
//var code = await _userManager.GenerateEmailConfirmationTokenAsync(user.Id);
//var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code }, protocol: Request.Url.Scheme);
//await _userManager.SendEmailAsync(user.Id, "Confirme sua Conta", "Por favor confirme sua conta clicando neste link: <a href='" + callbackUrl + "'></a>");
//ViewBag.Link = callbackUrl;
return RedirectToAction("ListUsers", "Account");
}

Identity usermanager cannot find the Role

I am trying to add a user to a role after successfully creating the user.
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (!ModelState.IsValid) return View(model);
var user = new ApplicationUser
{
UserName = model.PhoneNumber,
PhoneNumber = model.PhoneNumber,
NationalId = model.NationalId,
FullName = model.FullName
};
var result = await _userManager.CreateAsync(user, model.NationalId);
if (result.Succeeded)
{
var res = await _userManager.AddToRoleAsync(user, "Admin");
await _signInManager.SignInAsync(user, false);
_logger.LogInformation(3, "Applicant created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
// If we got this far, something failed, redisplay form
return View(model);
}
But, I get this error.
An unhandled exception occurred while processing the request.
InvalidOperationException: Role ADMIN does not exist.
Update:
I called the
var myrole = await _roleManager.FindByNameAsync("Admin");
and it returned null. but when i inspect
var roles = _roleManager.Roles
i get all the roles including "Admin"
I found the problem in the seed method. i do not understand it however.
in the seed method i used the RoleStore to add roles.
var roles = new[] {"Admin", "Applicant", "Student", "Role1", "Role2", "Role3", "Role4"};
foreach (var role in roles)
{
var roleStore = new RoleStore<IdentityRole>(context);
if (!context.Roles.Any(r => r.Name == role))
await roleStore.CreateAsync(new IdentityRole(role));
}
the roles where created successfully in the database table AspNetRoles.
but when acted upon, the roles were never found.
i replaced the RoleStore with RoleManager
await _roleManager.CreateAsync(new IdentityRole(role));
and like magic, it all worked out. i will do further research on the difference and the cause to understand it more.

Unable to create user after deleting database

I've been learning MVC and got to this part:
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.UserName, Email = model.Email };
UserManager.Delete(user);
//myDBLoginDetails ent = new myDBLoginDetails();
//ent.uspInsertUser(user.UserName, model.Password, model.FirstName, model.LastName, user.Email);
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 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");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
I thought everything was going smoothly and I successfully registered\saved users to the SQL Server db (no MDF file). To continue testing and such I remmed out the code to register\insert the users and deleted the table and recreated it so I could eventually start from scratch.
But then when I tried to re-register a user so I could step through the code to learn what all is going on I discovered my user(s) are still on my PC as I got the messages:
Name tempUser is already taken.
Email 'tempUser#email.com' is already taken.
I tried to delete the user (using UserManager.Delete(user);) but got the message:
[InvalidOperationException: The object cannot be deleted because it
was not found in the ObjectStateManager.]
I searched for an explanation and resolution but have failed, and only discovered there may be other\better ways to register\edit\delete users and frustration eventually set in.
So, I guess I need a couple things solved. I have a LoginDetalsEDmodel.edmx so am I still correct to use a Stored Proc or do I use the Framework? If the Framework then how do I do that?
And.... how do I get rid of the user(s) that are still on my computer (which are gone from the DB)?
I hope I explained this well enough as I'm still learning......

How to create ApplicationUser by UserManager in Seed method of ASP .NET MVC 5 Web application

I can create users in the old way:
var users = new List<ApplicationUser> {
new ApplicationUser{PasswordHash = hasher.HashPassword("TestPass44!"), Email = "informatyka4444#wp.pl", UserName = "informatyka4444#wp.pl", SecurityStamp = Guid.NewGuid().ToString()},
new ApplicationUser{PasswordHash = hasher.HashPassword("TestPass44!"), Email = "informatyka4445#wp.pl", UserName = "informatyka4445#wp.pl", SecurityStamp = Guid.NewGuid().ToString()}
};
users.ForEach(user => context.Users.AddOrUpdate(user));
context.SaveChanges();
but I want to do it the ASP.NET MVC 5.1 way using UserManager. I peeked how the Register POST method looks in AccountController:
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) { [...]
so I tried do the same:
var user = new ApplicationUser() { Email = "informatyka4444#wp.pl",
UserName = "informatyka4444#wp.pl"};
IdentityResult result = UserManager.CreateAsync(user, "abcwq12312!P");
but I get this:
also If I just type UserManager. VS2013 does not shows any methods on the list.
So how to add user in this way?
EDIT1:
Ok so to create user CreateAsync is unnecessary the problem was somewhere else. One should use ApplicationUserManager not UserManager(this one did not add anything to the database).
var store = new UserStore<ApplicationUser>(context);
var manager = new ApplicationUserManager(store);
var user = new ApplicationUser() { Email = "informatyka4444#wp.pl", UserName = "informatyka4444#wp.pl" };
manager.Create(user, "TestPass44!");
I dont understand the error you are showing, unless you are providing a custom TUser or TKey in which case would be like :
IdentityResult user = await UserManager.CreateAsync<CustomUser, CustomKey>(user, "abcwq12312!P");
and passing user as your CustomUser instead of ApplicationUser and maybe int if your CustomKey is an int instead of string. (CreateAsync can infer types, I posted there to show them explicitly)
The other problem I see is you are not awaiting the task, you must also add await like :
IdentityResult user = await UserManager.CreateAsync(user, "abcwq12312!P");
Hope this helps.
EDIT:
For completeness I will post the full answer from this question but there is your answer. : Unable to access CreateAsync in User Manager
var result = await UserManager.CreateAsync(user, register.Password);
The UserManager in the above statement is not a Class as I've
expected. Its a property of type UserManager<ApplicationUser>.
So, at the beginning just declared a property as
public UserManager<ApplicationUser> UserManager { get; private set; }
And now I can use the Async version for creating users. The
following statement works.
var result = await UserManager.CreateAsync(user, register.Password);
I will also flag for possible duplicate.

Categories