Identity usermanager cannot find the Role - c#

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.

Related

Update Identity User Claims in Web API

I'm currently trying to update the user's email/username from a mobile app to a Web API project. I'm currently using oauth and token authentication. When updating the identity user, the user becomes unauthenticated because the username and access token are no longer valid. From what I have read, I must update the identity claims. This is what I have tried so far:
var identity = new ClaimsIdentity(User.Identity);
if (result)
{
var identityUser = await UserManager.FindByNameAsync(User.Identity.Name);
identityUser.Email = AntiXssEncoder.HtmlEncode(value.Email, true);
identityUser.UserName = AntiXssEncoder.HtmlEncode(value.Email, true);
var identityResult = await UserManager.UpdateAsync(identityUser);
if(identityResult.Succeeded)
{
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
await UserManager.RemoveClaimAsync(identityUser.Id, identity.FindFirst(ClaimTypes.Name));
await UserManager.AddClaimAsync(identityUser.Id, new Claim(ClaimTypes.Name, value.Email));
identity.RemoveClaim(identity.FindFirst(ClaimTypes.Name));
identity.AddClaim(new Claim(ClaimTypes.Name, value.Email));
authenticationManager.AuthenticationResponseGrant =
new AuthenticationResponseGrant(
new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = false });
}
return Ok();
}
However, it still shows the previous email when using User.Identity.Name and the claims for the user within the authenticationManager have not been updated either. I'm not sure what else to do as there isn't much documentation on this for Web API. Any help is greatly appreciated.
Main problem is that claim which represents user's name is not updated in ClaimsIdentity you are using in the last step.
The easiest way to perform the update is to use SignInManager<TUser, TKey>.SignIn method
signInManager.SignIn(identityUser, isPersistent: false, rememberBrowser: false);
This is also an ASP.NET Identity idiomatic way since it is using associated IClaimsIdentityFactory to create claims for new identities.
Complete example
static async Task<IdentityResult> UpdateEmailAsync<TUser>(
IPrincipal principal,
UserManager<TUser, string> userManager,
SignInManager<TUser, string> signInManager,
string newEmail
)
where TUser : class, IUser<string>
{
string userId = principal.Identity.GetUserId();
IdentityResult result = await userManager.SetEmailAsync(userId, newEmail);
if (result.Succeeded)
{
// automatically confirm user's email
string confirmationToken = await userManager.GenerateEmailConfirmationTokenAsync(userId);
result = await userManager.ConfirmEmailAsync(userId, confirmationToken);
if (result.Succeeded)
{
TUser user = await userManager.FindByIdAsync(userId);
if (user != null)
{
// update username
user.UserName = newEmail;
await userManager.UpdateAsync(user);
// creates new identity with updated user's name
await signInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
// succeded
return result;
}
}
// failed
return result;
}
Then you can just call it from your code
string newEmail = AntiXssEncoder.HtmlEncode(value.Email, true);
IdentityResult result = await UpdateEmailAsync(identityUser, UserManager, SignInManager, newEmail);
if (result.Succeeded)
{
return Ok();
}

MVC 5 ASP.NET Identity - CreateAsync invalid user id

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");
}

Identity: Why is user.roles empty?

I have a method that gets all the users that i have in my db, simply put i do this:
var allUsers = context.Users.ToList();
What i can't figure it out is that when i debug the roles property it is empty:
but in dbo.UserRoles:
What am I missing here?
EDIT:
My registration method:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = model.FirstName, LastName = model.LastName };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
UserManager.AddToRole(user.Id, model.UserRole.ToString());
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return PartialView("~/Views/Account/Register.cshtml",model);
}
EDIT 2:
When getting the roles like this:
var roles = context.Roles.ToList();
I can see all the roles and I can also see which users have the specific role:
EDIT 3:
Tried turning lazyloading off and on
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.LazyLoadingEnabled = false;
Still doesn't give me the roles data.
You have to load related entities you want to use with Include like this :
var allUsers = context.Users.Include(u => u.Roles).ToList();
Then you should be able to access user roles.
More info about that topic here
So far I have not been able to solve this they way I want. I made a work arround that works:
I created a method that got me each individual user role like so:
public string GetRole(string userId)
{
var role = UserManager.GetRoles(userId);
return role[0];
}
and in my original Getuser method i called the my recently developed method:
public UserModel GetUsers()
{
var allUsers = context.Users.Include("Roles").ToList();
var model = new UserModel
{
Users = allUsers.Select(x => new OverWatchUser
{
Email = x.Email,
EmailConfirmed = x.EmailConfirmed,
FirstName = x.FirstName,
LastName = x.LastName,
OrgId = x.OrgId,
Role = GetRole(x.Id)
}).ToList()
};
return model;
}
This gives me the data I want but I consider this a dirty fix and I hope someone out there has a proper solution to the problem.
You can use context.Roles instead of context.Users as follows and filter the target user roles. The trick is to filter the target user roles in the where method.
string Id="the_required_user_Id";
var roles = context.Roles
.Include(r => r.Users)
.Where(r => (r.Users.Select(u => u.UserId).Contains(Id)))
.ToList();
This worked fine for me, hopefully this helps someone

Is there a way to create a master password for user accounts created using C#.NET Identities?

Is there a way to add a master password which could be used to login into user accounts created using .NET Identities. I would like an easy way for our system administrator to login to someone's account so they can see exactly what the customer would see when logged in.
One way would be to change the
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
method, so that it checks that the supplied password is indeed the master one, and if so use the supplied email address to grab the user, and then login normally with:
await SignInManager.SignInAsync(user, true, model.RememberMe);
Does this seem OK? Is there a better way?
There's an actual word for this: Impersonation.
Here's a link that will show you how to implement it.
public async Task ImpersonateUserAsync(string userName)
{
var context = HttpContext.Current;
var originalUsername = context.User.Identity.Name;
var impersonatedUser = await userManager.FindByNameAsync(userName);
var impersonatedIdentity = await userManager.CreateIdentityAsync(impersonatedUser, DefaultAuthenticationTypes.ApplicationCookie);
impersonatedIdentity.AddClaim(new Claim("UserImpersonation", "true"));
impersonatedIdentity.AddClaim(new Claim("OriginalUsername", originalUsername));
var authenticationManager = context.GetOwinContext().Authentication;
authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, impersonatedIdentity);
}
Extension method to detect impersonation:
public static bool IsImpersonating(this IPrincipal principal)
{
if (principal == null)
{
return false;
}
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal == null)
{
return false;
}
return claimsPrincipal.HasClaim("UserImpersonation", "true");
}
Use the previous code like this:
if(HttpContext.Current.User.IsImpersonating())
{
// do my stuff for admins
}
And revert back.
public async Task RevertImpersonationAsync()
{
var context = HttpContext.Current;
if (!HttpContext.Current.User.IsImpersonating())
{
throw new Exception("Unable to remove impersonation because there is no impersonation");
}
var originalUsername = HttpContext.Current.User.GetOriginalUsername();
var originalUser = await userManager.FindByNameAsync(originalUsername);
var impersonatedIdentity = await userManager.CreateIdentityAsync(originalUser, DefaultAuthenticationTypes.ApplicationCookie);
var authenticationManager = context.GetOwinContext().Authentication;
authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, impersonatedIdentity);
}
The marked answer is the 'right' way to do it I believe. I just wanted to post another way which works as well. This way the admin logs in using the user's email address, but they insert "___" in front of it (this isn't really needed, but I wanted the admins to have to do something different). They then use the Master Password (which is hard-coded into the Account Controller, should be stored somewhere where it can be easily changed really). The Login POST method (in the Account Controller) is then changed to:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
if ((model.Password == MasterPassword) && model.Email.StartsWith("___"))
{
var user = UserManager.FindByEmail(model.Email.Replace("___", ""));
if (user != null)
{
await SignInManager.SignInAsync(user, true, model.RememberMe);
return RedirectToLocal(returnUrl);
}
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
else
{
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);
}
}
}
Not as slick as using impersonation, but works nonetheless.

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