ASP.NET Identity change password - c#

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.

Related

What is 'Xsrf check when linking' in ASP.Net Identity/OWIN [duplicate]

In my MVC 5 web app I have this (in AccountController.cs):
// Used for XSRF protection when adding external sign ins
private const string XsrfKey = "XsrfId";
and
public string SocialAccountProvider { 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, SocialAccountProvider);
}
How exactly is it being used for protection?
Should I set the value of XsrfKey to something more random?
Take a look at ManageController methods LinkLogin and LinkLoginCallback:
//
// POST: /Manage/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 AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
}
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
These are the methods that handle linking of external accounts (i.e. Google, Facebook, etc.). The flow goes like this:
User clicks "Link Account" button, which calls a POST to LinkLogin method.
LinkLogin returns ChallengeResult object, with callback url set to LinkLoginCallback method.
ChallengeResult.ExecuteResult is called by MVC framework, calls IAuthenticationManager.Challenge, which causes a redirect to the specific external login provider (let's say: google).
User authenticates with google, then google redirects to callback url.
The callback is handled with LinkLoginCallback. Here, we want to prevent XSRF and verify that the call was initiated by a user, from a page served by our server (and not by some malicious site).
Normally, if it was a simple GET-POST sequence, you would add a hidden <input> field with an anti-forgery token and compare it with a corresponding cookie value (that's how Asp.Net Anti-Forgery Tokens work).
Here, the request comes from external auth provider (google in our example). So we need to give the anti-forgery token to google and google should include it in the callback request. That's exactly what state parameter in OAuth2 was designed for.
Back to our XsrfKey: everything you put in AuthenticationProperties.Dictionary will be serialized and included in the state parameter of OAuth2 request - and consequentially, OAuth2 callback. Now, GetExternalLoginInfoAsync(this IAuthenticationManager manager, string xsrfKey, string expectedValue) will look for the XsrfKey in the received state Dictionary and compare it to the expectedValue. It will return an ExternalLoginInfo only if the values are equal.
So, answering your original question: you can set XsrfKey to anything you want, as long as the same key is used when setting and reading it. It doesn't make much sense to set it to anything random - the state parameter is encrypted, so no one expect you will be able to read it anyway.
Just leave it as is:
As the name of the member states it is a key:
private const string XsrfKey = "XsrfId";
It is defined in this manner to avoid "magic numbers" and then is used a little down in the scaffold code:
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);
}
The value of the dictionary item is then set to the UserId property in the above code by using the XsrfKey member as the key.
IOW the code is already setting the XSRF dictionary item to the value of the user ID in the snippet. If you change the XsrfKey members value to anything else you will cause problems down the line, since the expected key "XsrfId" will have no value set.
If by changing it to something more random you are implying to change the value and not they key of the dictionary, or in other words, not set it to the user id then please see the following for an explanation of the anti forgery token inner workings.
http://www.asp.net/mvc/overview/security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages

ASP.NET Identity 2.0: How to rehash password

I am migrating users from a legacy user store to ASP.NET Identity 2.0 in my ASP.NET 5.0 web application. I have a means of verifying legacy hashes, but I want to upgrade them at login-time to ASP.NET Identity 2.0 hashes.
I've created a custom IPasswordHasher that is able to detect and verify legacy hashes, and return PasswordVerificationResult.SuccessRehashNeeded at the appropriate time. (If it detects that the hash is not legacy, it simply falls through to the built-in ASP.NET Identity hash verification.)
However, returning PasswordVerificationResult.SuccessRehashNeeded doesn't seem to cause ASP.NET Identity to actually do anything. Is there a configuration option somewhere that would cause the system to re-hash the passwords when IPasswordHasher returns this result?
If the answer is no to the above, then is it recommended that I simply re-hash and update the user manually? Where would I do this? I don't see any place at the controller level where I can see the PasswordVerificationResult.
I'm new to ASP.NET Identity so I'm sure I'm missing something simple. Thank you in advance for any pointers.
It seems rehashing mechanism is not implemented in the built-in user manager. But hopefully you could easily implemented. consider this:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
protected override async Task<bool> VerifyPasswordAsync(
IUserPasswordStore<ApplicationUser, string> store,
ApplicationUser user, string password)
{
var hash = await store.GetPasswordHashAsync(user);
var verifyRes = PasswordHasher.VerifyHashedPassword(hash, password);
if (verifyRes == PasswordVerificationResult.SuccessRehashNeeded)
await store.SetPasswordHashAsync(user, PasswordHasher.HashPassword(password));
return verifyRes != PasswordVerificationResult.Failed;
}
}
If you have implemented IPasswordHasher correctly, when returning a PasswordVerificationResult.SuccessRehashNeeded result, ASP.NET Core Identity will call the HashPassword method automatically for you, successfully authenticating the user and updating the hash in the database.
The class would look something like this:
public class PasswordHasherWithOldHashingSupport : IPasswordHasher<ApplicationUser>
{
private readonly IPasswordHasher<ApplicationUser> _identityPasswordHasher;
public PasswordHasherWithOldHashingSupport()
{
_identityPasswordHasher = new PasswordHasher<ApplicationUser>();
}
public string HashPassword(ApplicationUser user, string password)
{
return _identityPasswordHasher.HashPassword(user, password);
}
public PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
{
var passwordVerificationResult = _identityPasswordHasher.VerifyHashedPassword(user, hashedPassword, providedPassword);
if (passwordVerificationResult == PasswordVerificationResult.Failed)
{
/* Do your custom verification logic and if successful, return PasswordVerificationResult.SuccessRehashNeeded */
passwordVerificationResult = PasswordVerificationResult.SuccessRehashNeeded;
}
return passwordVerificationResult;
}
}

Eager Loading using UserManager with EF Core

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

ClaimsPrincipalPermission vs ClaimsAuthorization Attributes use different information, which is best to use

I'm trying to implement Claims Base Authorization on individual webapi's... However I don't want the user claims to be tightly coupled to the webapi. Using ClaimsPrincipalPermissions I get the desired decoupling as I can assign users to resources in the database. For example
Webapi Method
[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Manage", Resource = "Roles")]
public async Task<IHttpActionResult> GetRole(string Id)
{
var role = await AppRoleManager.FindByIdAsync(Id);
if (role != null)
{
return Ok(ModelFactory.Create(role));
}
return NotFound();
}
Using Custom AuthorizationManager
public class AuthorizationManager : ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
//return base.CheckAccess(context);
string resource = context.Resource.First().Value;
string action = context.Action.First().Value;
if (action == "Manage" && resource == "Roles")
{
// check for roles
//context.Principal.IsInRole("Admin");
bool hasRolesManagement = context.Principal.HasClaim("ManageRoles", "True");
return hasRolesManagement;
}
return false;
}
}
I can do the appropriate checks for a particular logged in user checking their claims against the resource they are hitting. However I can't kick back the appropriate unauthorized response back to the user in this fashion. Another example I found is below where the webapi is as follows
[ClaimsAuthorization(ClaimType="ManageRoles", ClaimValue="True")]
[Route("")]
public IHttpActionResult Get()
{
return Ok();
}
Then using the custom claims authorization attribute
public class ClaimsAuthorizationAttribute : AuthorizationFilterAttribute
{
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
public override Task OnAuthorizationAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
{
var principal = actionContext.RequestContext.Principal as ClaimsPrincipal;
if (!principal.Identity.IsAuthenticated)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
return Task.FromResult<object>(null);
}
if (!(principal.HasClaim(x => x.Type == ClaimType && x.Value == ClaimValue)))
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
return Task.FromResult<object>(null);
}
//User is Authorized, complete execution
return Task.FromResult<object>(null);
}
}
This gives me access to the actionContext to send back an appropriately statusCoded response.
THE BIG Caveat to the second approach is to introduce accessability to the api method for a new claim I have to recompile the code.
So my question is how can I get the flexibilty of the first approach where each api is designated as a resource and the claims are assigned in the database to a resource, but still get the flexibility of the second approach where I have access to the action context and be able to return a properly status coded response? We don't want to have to recompile the code to allow additional roles/claims access to a webapi resource.
I'm new to claims so I don't have a thorough understanding of all the concepts yet so please tell me if I'm missing something.
According to the documentation for ClaimsPrincipalPermission.Demand
"If the current principal is not authorized for the specified action
on the specified resource, a SecurityException is thrown; otherwise,
execution proceeds."
The solution would then be to create an exception handler in your solution and return an UnAuthorized Http response. (I really thought ASP.Net did that by default, but I have never checked/reflected over that :)

Temporarily Changing Identity with WebApi 2

I have a WebApi controller that initially authenticates as a specific WebApi user. Subsequent accesses to the web api will pass a user that operations should be performed as, without having to actually authenticate as that user.
I have some services/managers that perform functions as those proper users as part of an MVC project. I now want to use those services and managers with the WebApi project, but I don't want to have to pass the user around.
I'm hoping I can temporarily change the identity of the Web Api call after the user passed in the Web Api call has been validated, but I want to make sure that when the call is complete, the cookie returned is for the validation of the WebApi user, not the end user that is represented as a part of the call.
My question is, what can I do to temporarily change the identity to the validated user in the call, and then change back to the web api identity?
Loosely using code from the links in the post, I created a IDisposable object that would temporarily change the identity.
Usage is:
try
{
using(new Impersonate(userManager, userName))
{
/* do your stuff as userName */
}
}
catch (ImpersonateException) {}
The Impersonate class is as follows:
public class Impersonate : IDisposable
{
private UserManager<ApplicationUser> userManager;
public Impersonate(UserManager<ApplicationUser> userManager, string userName)
{
this.userManager = userManager;
if (ValidateUser(userName))
{
this.ImpersonateUser(userName);
}
else
{
throw new ImpersonateException("Current user does not have permissions to impersonate user");
}
}
private bool ValidateUser(string userName)
{
/* validate that the current user can impersonate userName */
}
public void Dispose()
{
this.RevertImpersonation();
}
private void ImpersonateUser(string userName)
{
var context = HttpContext.Current;
var originalUsername = context.User.Identity.Name;
var impersonatedUser = this.userManager.FindByName(userName);
var impersonatedIdentity = impersonatedUser.GenerateUserIdentity(this.userManager, "Impersonation");
impersonatedIdentity.AddClaim(new Claim("UserImpersonation", "true"));
impersonatedIdentity.AddClaim(new Claim("OriginalUsername", originalUsername));
var impersonatedPrincipal = new ClaimsPrincipal(impersonatedIdentity);
context.User = impersonatedPrincipal;
Thread.CurrentPrincipal = impersonatedPrincipal;
}
private void RevertImpersonation()
{
var context = HttpContext.Current;
if (!ClaimsPrincipal.Current.IsImpersonating())
{
throw new ImpersonationException("Unable to remove impersonation because there is no impersonation");
}
var originalUsername = ClaimsPrincipal.Current.GetOriginalUsername();
var originalUser = this.userManager.FindByName(originalUsername);
var originalIdentity = originalUser.GenerateUserIdentity(this.userManager);
var originalPrincipal = new ClaimsPrincipal(originalIdentity);
context.User = originalPrincipal;
Thread.CurrentPrincipal = originalPrincipal;
}
}
This differs the linked code in that it only sets the identity temporarily, so doing having to SignIn/SignOut is not required.
Also, since the bulk of the work is done in the constructor, I had to remove the Async aspects that the linked code uses. There might be a way around it, but I'm not experienced enough with Async, or patient enough to bother.

Categories

Resources