How to read user claims from Automapper Value Resolver

sergiobarriel

Sergio Barriel

Posted on June 10, 2021

How to read user claims from Automapper Value Resolver

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();
}
Enter fullscreen mode Exit fullscreen mode

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>());
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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));
}
Enter fullscreen mode Exit fullscreen mode

Notice: This post was published originally on Medium platform on april 17, 2018

💖 💪 🙅 🚩
sergiobarriel
Sergio Barriel

Posted on June 10, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related