How to read user claims from Automapper Value Resolver
Sergio Barriel
Posted on June 10, 2021
Sometimes we need to send to our services objects that include existing data in the user’s claims.
To avoid fattening controllers, let’s see how to make Automapper do this work for us.
In our example, we will try to create a company and only receive the name and description, but obviously, we are interested in knowing which user has made this action.
The model that exposes the API is CreateCompany and the DTO that we send to the service is CreateCompanyDto. Both models are the same, except the last one, which incorporates a property that stores the user’s identifier.
Step 1: Install Nuget packages
To make this solution to work correctly, we will need these two packages:
Step 2: Register IHttpContextAccessor interface into DI container
Why? because IHttpContextAccessor is not registered by default for performance reasons. You can read on Github a thread with a discussion about it.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddAutoMapper(configuration =>
{
configuration.AddProfile<CompanyProfile>();
});
services.AddMvc();
}
Step 3: Create Automapper profile
On our Automapper profile, we will specify which "custom value solver" we will use to recover the desired value of the user's claims
public class CompanyProfile : Profile
{
public CompanyProfile()
{
CreateMap<CreateCompany, CreateCompanyDto>()
.ForMember(destination => destination.UserId, option => option.ResolveUsing<UserIdResolver>());
}
}
Step 4: Create "Custom Value Resolver"
We must inject IHttpContextAccessor interface on class constructor so that afterwards, the "Resolve" method is able to read the property.
In this case, we read "NameIdentifier" claim as our user identifier, but we can read anything.
public class UserIdResolver : IValueResolver<object, object, string>
{
private readonly IHttpContextAccessor _contextAccessor;
public UserIdResolver(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public string Resolve(object source, object destination, string destinationMember, ResolutionContext context)
{
return _contextAccessor.HttpContext.User.Claims
.Where(x => x.Type == ClaimTypes.NameIdentifier)
.Select(c => c.Value).SingleOrDefault();
}
}
Step 5: Create controller and mapping data
Now, we will be able to map both classes in a single line and this is because Automapper will be in charge of recovering the value that we need from the claims.
[HttpPost]
[Authorize]
public async Task<IActionResult> CreateCompanyAsync(CreateCompany request)
{
var mapped = _automapper.Map<CreateCompanyDto>(request);
return Ok(await _companyService.CreateCompanyAsync(mapped));
}
Notice: This post was published originally on Medium platform on april 17, 2018
Posted on June 10, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.