I'm using windows authentication and IClaimsTransformation in my Blazor ServerApp (.NET 6.0)
I don't know why the ClaimsTransformerService runs 4 times per user login or per page refresh. Is there any way to prevent this?
The following code basically tries to check if a username is available in a database and if it is, record the login event in another table.
namespace MyServerApp.Services
{
public class ClaimsTransformerService : IClaimsTransformation
{
private IUserService _userService;
private ILoginRecordService _loginRecordService;
public ClaimsTransformerService(IUserService userService, ILoginRecordService loginRecordService)
{
_userService = userService;
_loginRecordService = loginRecordService;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var user = await _userService.GetUser(principal.Identity.Name.ToLower());
((ClaimsIdentity)principal.Identity).AddClaim(new Claim(ClaimTypes.Role, "MyUser"));
return principal;
}
}
}
I've read this thread and it didn't help.
The Microsoft documentation states that the TransformAsync() method may get called multiple times. From the docs:
The IClaimsTransformation interface can be used to add extra claims to the ClaimsPrincipal class. The interface requires a single method TransformAsync. This method might get called multiple times. Only add a new claim if it does not already exist in the ClaimsPrincipal. A ClaimsIdentity is created to add the new claims and this can be added to the ClaimsPrincipal.
So you should write your AsyncMethod defensively and only add claims if they do not already exist:
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (!principal.HasClaim(claim => claim.Type == ClaimTypes.Role && claim.Value == "MyUser"))
{
var user = await _userService.GetUser(principal.Identity.Name.ToLower());
((ClaimsIdentity)principal.Identity).AddClaim(new Claim(ClaimTypes.Role, "MyUser"));
}
return principal;
}
I am not very experienced on C# and ASP, and I am not sure I am taking the right approach to this problem.
This is a brief representation of it:
I have several endpoints related to an user but with different parameters (eg. string or UserDTO)
Here are a couple of them:
[AllowAnonymous]
[HttpPut("{username}")]
// I want to pass [FromBody]UserUpdateDTO to the filter
[ServiceFilter(typeof(UserChangeManagerFilter<UserUpdateDTO>))]
public ActionResult<InternalStatus> UpdateUser([FromBody]UserUpdateDTO userDto)
{
...
}
[AllowAnonymous]
[HttpDelete("{username}")]
// I want to pass {username} to the filter
[ServiceFilter(typeof(UserChangeManagerFilter<string>))]
public ActionResult<InternalStatus> DeleteUser(string username)
{
...
}
Now, I need to execute some code after each action is executed. For this, I have created a generic filter
My problem is that the filter needs to know the user details, so the PUT endpoint has to pass the UserDTO element to the filter, and the DELETE endpoint has to pass the username. I don't know how to do that exactly, but researching, I have seen that using a generic sercive filter was an option. So I created this one:
namespace ActionFilters.ActionFilters
{
public class UserChangeManagerFilter<T> : IActionFilter
{
private UserInfo _user;
public UserChangeManagerFilter(UserUpdateDTO userContext)
{
_user.Username = userContext.Username;
_user.Firstname = userContext.FirstName;
...
}
public UserChangeManagerFilter(string username)
{
_user.Username = username;
_user.Firstname = "";
...
}
public void OnActionExecuting(ActionExecutingContext context)
{
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do whatever I need to do here
}
}
I am adding the service in Startup.cs as below:
services.AddScoped<UserChangeManagerFilter<UserUpdateDTO>>();
services.AddScoped<UserChangeManagerFilter<string>>();
When I execute the PUT endpoint, I get
System.InvalidOperationException: No constructor for type 'ActionFilters.ActionFilters.UserChangeManagerFilter`1[xxx.Services.UserUpdateDTO]' can be instantiated using services from the service container and default values.
When I execute the DELETE endpoint I get
System.InvalidOperationException: No constructor for type 'ActionFilters.ActionFilters.UserChangeManagerFilter`1[System.String]' can be instantiated using services from the service container and default values.
I know this is not a direct answer to your question but how about using a non generic filter and then using the OnActionExecuting to inspect the model being passed and extracting the data as needed depending on the type of object received:
public void OnActionExecuting(ActionExecutingContext context)
{
var userDto = actionContext.ActionArguments.Values.OfType<UserUpdateDTO>().Single();
if(userDto !=null)
{
_user.Username = userContext.Username;
_user.Firstname = userContext.FirstName;
...
}else
{
//look for the string value
}
}
I'm facing an issue while working with web api azure ad authentication
I'm having controller like below, the one which having giving proper response, But the one which having customauthorization roles throwing error as "Authentication has been for this request".
[RoutePrefix("api/hospitals")]
public class hospitals : ApiController
{
[Route("GetAll")]
[HttpGet]
[Authorize]
public async Task<IEnumerable<Hospitals>> GetAll()
{
// return ok;
}
[Route("Getbeds")]
[HttpGet]
[SmAuthorize(Constants.Roles.Admin,
Constants.Roles.HotSpitalAdmin,
Constants.Roles.QA)]
public async Task<IEnumerable<Hospitals>> Getbeds()
{
// return ok;
}
}
The Getbeds method is throwing an error as "Authorization has been request".
Please find me Custom attribute class as well
public class SmAuthorizeAttribute : AuthorizeAttribute
{
public SmAuthorizeAttribute(params string[] roles)
{
this.Roles = string.Join(",", roles.Select(s => s.Trim()).ToArray());
}
}
Can anyone help on this ?
You can refer to this SO question's answer by Derek Greer for Dot Net core, additionally I will reiterate the answer below -
The approach recommended by the ASP.Net Core team is to use the new policy design which is fully documented here. The basic idea behind the new approach is to use the new [Authorize] attribute to designate a "policy" (e.g. [Authorize( Policy = "YouNeedToBe18ToDoThis")] where the policy is registered in the application's Startup.cs to execute some block of code (i.e. ensure the user has an age claim where the age is 18 or older).
The policy design is a great addition to the framework and the ASP.Net Security Core team should be commended for its introduction. That said, it isn't well-suited for all cases. The shortcoming of this approach is that it fails to provide a convenient solution for the most common need of simply asserting that a given controller or action requires a given claim type. In the case where an application may have hundreds of discrete permissions governing CRUD operations on individual REST resources ("CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder", etc.), the new approach either requires repetitive one-to-one mappings between a policy name and a claim name (e.g. options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));), or writing some code to perform these registrations at run time (e.g. read all claim types from a database and perform the aforementioned call in a loop). The problem with this approach for the majority of cases is that it's unnecessary overhead.
While the ASP.Net Core Security team recommends never creating your own solution, in some cases this may be the most prudent option with which to start.
The following is an implementation which uses the IAuthorizationFilter to provide a simple way to express a claim requirement for a given controller or action:
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] {new Claim(claimType, claimValue) };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly Claim _claim;
public ClaimRequirementFilter(Claim claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
if (!hasClaim)
{
context.Result = new ForbidResult();
}
}
}
[Route("api/resource")]
public class MyController : Controller
{
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[HttpGet]
public IActionResult GetResource()
{
return Ok();
}
}
Part of this answer for .NET Framework-
Recommended Custom Attribute class:
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
private readonly PermissionAction[] permissionActions;
public CustomAuthorize(PermissionItem item, params PermissionAction[] permissionActions)
{
this.permissionActions = permissionActions;
}
protected override Boolean IsAuthorized(HttpActionContext actionContext)
{
var currentIdentity = actionContext.RequestContext.Principal.Identity;
if (!currentIdentity.IsAuthenticated)
return false;
var userName = currentIdentity.Name;
using (var context = new DataContext())
{
var userStore = new UserStore<AppUser>(context);
var userManager = new UserManager<AppUser>(userStore);
var user = userManager.FindByName(userName);
if (user == null)
return false;
foreach (var role in permissionActions)
if (!userManager.IsInRole(user.Id, Convert.ToString(role)))
return false;
return true;
}
}
}
I'm trying to add a new policy based authorization for a certain user who needs to execute certain actions of a controller that requires rights. I'm new with Policy based authorization but I followed all the instructions on this post, and seems pretty simple.
In my Startup.cs and after the AddMvc() method I have:
services.AddAuthorization(options =>
{
options.AddPolicy("AgentsActivityReport ", policy => policy.RequireUserName("AnaR"));
});
Then, In my controller action I have:
[Authorize(Policy = "AgentsActivityReport")]
public ActionResult AgentsActivity()
{
//some code
}
However, when I launch the application, I receive the following error:
InvalidOperationException: The AuthorizationPolicy named:
'AgentsActivityReport' was not found.
I have also readed a few other threads/posts such as:
Claim Based And Policy-Based Authorization With ASP.NET Core 2.1
reported issue
And everything seems pretty much correct. Any thougts?
Based on this post, I was able to declare and allow my user to invoke an action of a controller by using a requirement. Since my condition has to be, "allow certain roles OR a certain user named AnaR", I had to put that logic into the AuthorizationHandler.cs
Startup.cs
services.AddAuthorization(options => {
options.AddPolicy("ReportActivityPolicy", policy =>
{
policy.AddRequirements(new UserNameRequirement("AnaR"));
});
});
services.AddSingleton<IAuthorizationHandler, AgentsActivityAuthorizationHandler>();
And then, in a separate file:
public class AgentsActivityAuthorizationHandler : AuthorizationHandler<UserNameRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserNameRequirement requirement)
{
if (context.User.IsInRole("Administrator") || context.User.IsInRole("Manager") || context.User.Identity.Name == requirement.UserName)
{
context.Succeed(requirement);
}
return Task.FromResult(0);
}
}
public class UserNameRequirement : IAuthorizationRequirement
{
public UserNameRequirement(string username)
{
this.UserName = username;
}
public string UserName { get; set; }
}
Then, in my controller, the following:
[Authorize(Policy = "ReportActivityPolicy")]
public ActionResult AgentsActivity()
{
//code of your controller.
}
Hope it helps!
It seems that you register a policy named "AgentsActivityReport " with a white space at the end in the middleware but annotate the controller without the whitespace "AgentsActivityReport".
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.