Accessing Identity and HttpContext Info using Dependency Injection in .NET 5

If you have been using .NET 5 (or by its more recent moniker of .NET Core), then you know that Dependency Injection is rather important in this new ecosystem. Thankfully, the development team made it incredibly easy to use.

One common scenario that I have seen arise since its release has been something to the extent of :

"How can I inject Identity or User information via D.I. in .NET 5?"

This could be a common use case for systems that require identity information for server-level transactions (e.g. when creating a new entity, you might want to know who performed this action or likewise if you wanted to audit who accessed a particular record in your system).

This post will cover implementing such as a service that you can easily use or extend within your applications.

What you have probably tried already, but didn't work.

The most common place that you'll see developers go to access the current user might be as simple as the User.Identity property that is exposed from the HttpContext. However, if you are reading this post, I'm sure that you have attempted something like the following :

// Example of a controller
public FooController(FooContext context)  
{
       // Injecting your data context (a common-use case)
       _context = context;
       // Attempting to resolve the current user
       _user = HttpContext.User?.Identity?.Name;
}

You'll quickly find that the actual HttpContext doesn't exist within the constructor for the controller (and isn't created until further down the pipeline).

So in order for us to actually access anything within the HttpContext, we will need to explicitly inject it. Thankfully, that isn't too difficult to do.

Building a Service to Inject the HttpContext

For D.I. to work, we will need to build a service that can inject the HttpContext that we need into the pipeline so that we can access properties of it.

We can do this by creating a simple class that already injects that information into it behind the scenes via a IHttpContextAccessor object as seen below :

public class UserResolverService  
{
    private readonly IHttpContextAccessor _context;
    public UserResolverService(IHttpContextAccessor context)
    {
        _context = context;
    }

    public string GetUser()
    {
       return await _context.HttpContext.User?.Identity?.Name;
    }
}

What this will do is inject the HttpContext object from a request into this UserResolverService, which will store the context and expose a method called GetUser() that will return the current name of the user.

This might be used within a repository if you needed to store the username that was accessing a particular record as follows :

public class FooRepository : IFooRepository  
{
      private FooContext _context;
      private string _currentUser;
      public FooRepository(FooContext context, UserResolverService userService)
      {
          _context = context;
          _currentUser = userService.GetUser();
      }

      public void DoWork()
      {
           var widget = new Widget(){
                Id = 42,
                Title = "The Answer",
                Author = "Deepthought",
                CreatedBy = _currentUser
           };

           _context.Widgets.Add(widget);
           _context.SaveChanges();
      }
}

And that's basically it.

Extending the Service

If you had some additional properties that existed on the ApplicationUser object (the abstraction that Entity Framework uses by default), then you might want to extend this service to pass along the entire object itself.

Doing this would require a very minor change, as you would simply need to inject your UserManager object into this new service :

public class UserResolverService  
{
      private readonly IHttpContextAccessor _context;
      private readonly UserManager<ApplicationUser> _userManager;
      public UserResolverService(IHttpContextAccessor context, UserManager<ApplicationUser> userManager)
      {
            _context = context;
            _userManager = userManager;
      }
      public async Task<ApplicationUser> GetUser()
      {
            return await _userManager.FindByEmailAsync(_context.HttpContext.User?.Identity?.Name);
      }
}

This is obviously just a starting point for building a service that leverages the existing context (enabling all sorts of context-sensitive logic), but hopefully it will pique your interest in developing your own services to pass around.

comments powered by Disqus