I have a Core 2.0 project that has reached the point where I need to start linking users to the data I have. It started as a one user test bed that now requires me to have multiple per user.
User Model:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<Balance> Balances { get; set; }
}
Balance model:
public virtual ApplicationUser User { get; set; }
...
ApplicationDbContext.cs:
//Users to balances
builder.Entity<ApplicationUser>()
.HasMany(b => b.Balances)
.WithOne(u => u.User);
In my code, when creating a new Balance, I cannot figure out how to link my current logged in user:
var myNewBalance = new Balance();
myNewBalance.User = User;
^ boom
How do I relate Balances to the currently logged in user?
The User property you've highlighted in your question is a ClaimsPrincipal that belongs to the ControllerBase class. The User property on your Balance class is an ApplicationUser, which is not compatible with ClaimsPrincipal. You already know this, but I'm just making it explicit in order to explain what you need to do.
As you are using ASP.NET Core Identity here, you can use the UserManager class, which is available using dependency injection. UserManager contains a function, GetUserAsync, that can convert your ClaimsPrincipal into the corresponding ApplicationUser.
Here's some code:
public class SomeController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public SomeController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> SomeAction()
{
var myNewBalance = new Balance();
myNewBalance.User = await _userManager.GetUserAsync(User);
return View(); // etc, etc.
}
}
The key points are the use of the generic UserManager that uses ApplicationUser and then the invocation of GetUserAsync using the User (ClaimsPrincipal) property.
I am using Identity in a project that has extended properties In ApplicationUser class as OrganisationId.
I am using ApplicationUserManager to read user details.
here is my ApplicationUser class:
public class ApplicationUser
: IdentityUser<int, ApplicationUserLogin,
ApplicationUserRole, ApplicationUserClaim>, IUser<int>
{
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
userIdentity.AddClaim(new Claim("OrganisationId", OrganisationId.ToString()));
return userIdentity;
}
public int OrganisationId { get; set; }
}
And in the AccountController class I inject the ApplicationUserManager object.
Then user manager object gives me a way to find whether a user in role or not.
var isUserAdmin = await _userManager.IsInRoleAsync(userId, adminRoleName);
But what I need is a way to find all users whom has admin rights in an organisation.
Something like this:
_userManager.Users.Where(u=>u.OrganisationId=1 && u.Roles.Contains(adminRole))
But this doesnt work as Roles is collection of ApplicationUserRole.
Any idea how I could manage to get all the admin users in an organisation?
Just use:
u.Roles.Any(m => m.RoleId == adminRole.Id)
Instead.
Currently have ApplicationUser class with some custom properties, like:
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public List<Content> Content { get; set; }
}
I'd like to get the current logged user with the list of related data (Content property).
In my controller, if I put:
Applicationuser user = await _userManager.GetUserAsync(HttpContext.User);
I get the logged user, but without any related data.
But, if I retrieve the current user using the ApplicationDbContext, like below, I can retrieve the related data:
ApplicationUser user = await _userManager.GetUserAsync(HttpContext.User);
ApplicationUser userWithContent = _context.Users.Include(c => c.Content).Where(u => u.Id == user.Id).ToList();
But this doesn't appear correctly for me!
Any idea?
checking the source code of [UserManager][1], GetUserAsync will end up calling FindByIdAsync, which will be provided by an IUserStore implementation. Looking the source code in the question, very likely using EFCore as the IUserStore implementation.
In case of EFCore, it was mention here that Find cannot be combined with include, so I guest what've you done to do eager loading in your question, may actually correct.
If you need those properties in every request, there's a better way to get them without query the database in each request.
You can store those properties as Claims by writing your own IUserClaimsPrincipalFactory<TUser> and reading them from HttpContext.User
public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
{
public CustomUserClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor)
: base(userManager, optionsAccessor)
{
}
protected async override Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
{
ClaimsIdentity identity = await base.GenerateClaimsAsync(user);
identity.AddClaim(new Claim("Name", user.Name));
for (int i = 0; i < user.Content.Count; i++)
{
string content = Newtonsoft.Json.JsonConvert.SerializeObject(user.Content[i]);
identity.AddClaim(new Claim("Content", content));
}
return identity;
}
}
Also you need to register it to service collection
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();
You can access these properties with this code
string name = httpContext.User.FindFirstValue("Name");
List<Content> contents = new List<Content>();
foreach (Claim claim in httpContext.User.FindAll("Content"))
{
Content content = Newtonsoft.Json.JsonConvert.DeserializeObject<Content>(claim.Value);
contents.Add(content);
};
I am planning to implement MVC 5.0 ASP.Net identity in a brand new application. I referred to the Microsoft article http://go.microsoft.com/fwlink/?LinkID=317594 to add the customer profile information in a separate table other than the identity tables.
However according my requirement, I would like to store the customer profile information in a separate database in order to segregate the user identity information and customer profile information in database level. The Identity uses single data store while creating user and the profile information, whereas I need to set two different store for the user and profile information. Do Anyone have any suggestions on this?
You could simply write a custom UserStore class and extend default UserStore class. Consider this simple example:
public class ApplicationUser : IdentityUser
{
// other codes
// Add your extra profile information
// By Adding NotMapped attribute EF omits this and dose not puts in Identity's table
[NotMapped]
public Profile Profile { get; set; }
}
public class Profile
{
public int ID { get; set; }
public string ExtraData { get; set; }
// other properties
}
Now we need custom User Store to put and fetch data from 2 DB
public class MyUserStore : UserStore<ApplicationUser>
{
public MyUserStore(DbContext context)
: base(context)
{
// other implementation for second DB
}
public override Task CreateAsync(ApplicationUser user)
{
// save Profile object to separate DB
_mySecondDB.Save(User.Id, user.Profile);
return base.CreateAsync(user);
}
public override Task UpdateAsync(ApplicationUser user)
{
// same pattern as CreateAsync
}
public override Task DeleteAsync(ApplicationUser user)
{
// same pattern as CreateAsync
}
public override async Task<ApplicationUser> FindByIdAsync(string userId)
{
var user = await base.FindByIdAsync(userId);
user.Profile = _mySecondDB.FindProfileByUserId(userId);
return user;
}
public override Task<ApplicationUser> FindByNameAsync(string userName)
{
// same pattern as FindByIdAsync
}
}
Now you just need to inject your custom User Store in Identity pipeline. To do so change ApplicationUserManager.Create static method in App_Start\IdentityConfig.cs like this:
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(
new MyUserStore(context.Get<ApplicationDbContext>()));
// other codes
}
I need ability to change password for user by admin. So, admin should not enter a current password of user, he should have ability to set a new password. I look at ChangePasswordAsync method, but this method requires to enter old password. So, this method is not appropriate for this task. Therefore I have made it by the following way:
[HttpPost]
public async Task<ActionResult> ChangePassword(ViewModels.Admin.ChangePasswordViewModel model)
{
var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
var result = await userManager.RemovePasswordAsync(model.UserId);
if (result.Succeeded)
{
result = await userManager.AddPasswordAsync(model.UserId, model.Password);
if (result.Succeeded)
{
return RedirectToAction("UserList");
}
else
{
ModelState.AddModelError("", result.Errors.FirstOrDefault());
}
}
else
{
ModelState.AddModelError("", result.Errors.FirstOrDefault());
}
return View(model);
}
it works, but theoretically we can receive error on AddPasswordAsync method. So, old password will be removed but new is not set. It's not good. Any way to do it in "one transaction"?
PS. I seen ResetPasswordAsync method with reset token, seems, it's more safe (because can't be unstable situation with user) but in any case, it does by 2 actions.
EDIT: I know the OP requested an answer which performs the task in one transaction but I think the code is useful to people.
All the answers use the PasswordHasher directly which isn't a good idea as you will lose some baked in functionality (validation etc).
An alternative (and I would assume the recommended approach) is to create a password reset token and then use that to change the password. Example:
var user = await UserManager.FindByIdAsync(id);
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP#ssw0rd");
This method worked for me:
public async Task<IHttpActionResult> changePassword(UsercredentialsModel usermodel)
{
ApplicationUser user = await AppUserManager.FindByIdAsync(usermodel.Id);
if (user == null)
{
return NotFound();
}
user.PasswordHash = AppUserManager.PasswordHasher.HashPassword(usermodel.Password);
var result = await AppUserManager.UpdateAsync(user);
if (!result.Succeeded)
{
//throw exception......
}
return Ok();
}
ApplicationUserManager is the class generated by the ASP.NET Template.
Which means, you can edit it and add any functionality it doesn't have yet. The UserManager class has a protected property named Store which stores a reference to the UserStore class (or any subclass of it, depending on how you configured your ASP.NET Identity or if you use custom user store implementations, i.e. if you use different database engine like MySQL).
public class AplicationUserManager : UserManager<....>
{
public async Task<IdentityResult> ChangePasswordAsync(TKey userId, string newPassword)
{
var store = this.Store as IUserPasswordStore;
if(store==null)
{
var errors = new string[]
{
"Current UserStore doesn't implement IUserPasswordStore"
};
return Task.FromResult<IdentityResult>(new IdentityResult(errors) { Succeeded = false });
}
if(PasswordValidator != null)
{
var passwordResult = await PasswordValidator.ValidateAsync(password);
if(!password.Result.Success)
return passwordResult;
}
var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
await store.SetPasswordHashAsync(userId, newPasswordHash);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
}
The UserManager is nothing else than a wrapper to the underlying UserStore. Check out IUserPasswordStore interface documentation at MSDN on available Methods.
Edit:
The PasswordHasher is also a public property of the UserManager class, see interface definition here.
Edit 2:
Since some people naively believe, you can't do password validation this way, I've updated it. The PasswordValidator property is also a property of UserManager and its as simple as adding 2 lines of code to add password validation too (which wasn't an requirement of the original question though).
In .net core 3.0
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, password);
I think that the solution is much easier
Generate the passwordToken,
Reset the password with the generated Token...
public async Task<IdentityResult> ResetPasswordAsync(ApplicationUser user, string password)
{
string token = await userManager.GeneratePasswordResetTokenAsync(user);
return await userManager.ResetPasswordAsync(user, token, password);
}
This is just a refinement on the answer provided by #Tseng. (I had to tweak it to get it to work).
public class AppUserManager : UserManager<AppUser, int>
{
.
// standard methods...
.
public async Task<IdentityResult> ChangePasswordAsync(AppUser user, string newPassword)
{
if (user == null)
throw new ArgumentNullException(nameof(user));
var store = this.Store as IUserPasswordStore<AppUser, int>;
if (store == null)
{
var errors = new string[] { "Current UserStore doesn't implement IUserPasswordStore" };
return IdentityResult.Failed(errors);
}
var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
await store.SetPasswordHashAsync(user, newPasswordHash);
await store.UpdateAsync(user);
return IdentityResult.Success;
}
}
Note: this applies specifically to a modified setup that uses int as the primary keys for users and roles. I believe it would simply be a matter of removing the <AppUser, int> type args to get it to work with the default ASP.NET Identity setup.
public async Task<IActionResult> ChangePassword(ChangePwdViewModel usermodel)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _userManager.FindByIdAsync(userId);
var result = await _userManager.ChangePasswordAsync(user, usermodel.oldPassword, usermodel.newPassword);
if (!result.Succeeded)
{
//throw exception......
}
return Ok();
}
public class ChangePwdViewModel
{
[DataType(DataType.Password), Required(ErrorMessage ="Old Password Required")]
public string oldPassword { get; set; }
[DataType(DataType.Password), Required(ErrorMessage ="New Password Required")]
public string newPassword { get; set; }
}
Note : here UserId i am retrieving from Current logged User.
For ASP.NET Core 3.1 users, this is a modernized iteration of the excellent answers provided by #Tseng and #BCA.
PasswordValidator is no longer a property on UserManager - instead, the property is an IList PasswordValidators. Furthermore, Identity now has a protected UpdatePasswordHash method that changes the password for you without needing to directly access the UserStore, which eliminates the need to manually hash and save the password anyway.
UserManager also has a public property, bool SupportsUserPassword, which replaces the need to test if Store implements IUserPasswordStore (internally, this is exactly what UserManager does in the SupportsUserPassword getter).
Since UpdatePasswordHash is protected, you do still need to extend the base UserManager. Its signature is:
protected Task<IdentityResult> UpdatePasswordHash(TUser user, string newPassword, bool validatePassword)
where validatePassword represents whether or not to run password validation. This does not default to true, unfortunately, so it needs to be supplied. The implementation looks like this:
public async Task<IdentityResult> ChangePasswordAsync(ApplicationUser user, string newPassword)
{
if (!SupportsUserPassword)
{
return IdentityResult.Failed(new IdentityError
{
Description = "Current UserStore doesn't implement IUserPasswordStore"
});
}
var result = await UpdatePasswordHash(user, newPassword, true);
if (result.Succeeded)
await UpdateAsync(user);
return result;
}
As before, the first order of business is to ensure the current UserStore supports passwords.
Then, simply call UpdatePasswordHash with the ApplicationUser, the new password, and true to update that user's password with validation. If the update was successful, you still have to save the user so call UpdateAsync.
If you don't have user's current password and still want to change the password. What you could do instead remove user's password first and then add the new password. This way you will be able to change user's password without needing current password of that user.
await UserManager.RemovePasswordAsync(user);
await UserManager.AddPasswordAsync(user, model.Password);
public async Task<ActionResult> ResetUserPassword(string id, string Password)
{
// Find User
var user = await context.Users.Where(x => x.Id == id).SingleOrDefaultAsync();
if (user == null)
{
return RedirectToAction("UserList");
}
await UserManager.RemovePasswordAsync(id);
// Add a user password only if one does not already exist
await UserManager.AddPasswordAsync(id, Password);
return RedirectToAction("UserDetail", new { id = id });
}
Yes, you are correct. ResetPassword through token is a preferred approach.
Sometime back, I created a complete wrapper over .NET Identity and code can be found here. It might be helpful for you. You can also find nuget here. I also explained the library in a blog here. This wrapper is easily consumable as nuget and create all required configs during installation.