11

If you are in a controller context, you can access the current authenticated User. Take an article such as Get the current user, within an ApiController action, without passing the userID as a parameter .

However, if my controller calls a service (same assembly), but here I don't have the controller context.

What is the best way to actually get the authenticated user?

Lars Holdgaard
  • 9,496
  • 26
  • 102
  • 182
  • 1
    What stops you from being able to pass the current authenticated User into your service? – Kirk Larkin Aug 27 '17 at 18:11
  • @KirkLarkin Makes my method signatures pretty damn nitty-gritty. Does not seem that clean. And lets say you want to expose the API and allow it without an actual customer using it, then it's not really clean. – Lars Holdgaard Aug 27 '17 at 18:34
  • In which case, it seems your question might be more about how to handle optional authentication rather than the specifics of ASP.NET Web API. As soon as you concern yourself with having the ability to only sometimes have a User, having a service that automatically detects whether there is a User or just has one passed in is likely not going to make a big difference. – Kirk Larkin Aug 27 '17 at 18:43
  • take a look at this tutorial if useful: https://stormpath.com/blog/10-minute-asp-net-user-authentication –  Aug 30 '17 at 10:33

1 Answers1

12

You can create an intermediate service to provide that functionality

public interface IPrincipalProvider {
    IPrincipal User { get; }
}

public class WebApiPrincipalProvider : IPrincipalProvider {
    public IPrincipal User { 
        get {
            return HttpContext.Current != null
                ? HttpContext.Current.User 
                : null;
        }
    }
}

and inject it into the dependent service context

public class MyService : IService {
    private readonly IPrincipalProvider provider;

    public MyService(IPrincipalProvider provider) {
        this.provider = provider;
    }

    public MyModel MyServiceMethod() {
        var currentUser = provider.User;
        var name = currentUser.Identity.Name;

        //...use user....

        return model;
    }
}

Finally make sure abstraction and implementation are registered with DI container in composition root of main application so that when service is injected into controller it would also be able to access current request's user.

[Authorize]
public class MyController : ApiController {
    public readonly IService service;

    public MyController (IService service) {
        this.service = service;
    }

    [HttpGet]
    public IHttpActionResult MyGetActiom() {
        var model = service.MyServiceMethod();
        return Ok(model);
    }
}

When Identity framework authenticates a user the user principal is then set for the current context.

If hosted in IIS you can tap into HttpContext to access the user like in the example provided earlier. MVC and Web API basically do something similar to populate Controller.User and ApiController.User.

If self hosting there are other ways to access it.

That fact is that once authenticated, the user is available. Encapsulate it behind an abstraction and you can injected where ever it is needed outside of a controller.

Asp.net Core introduced something similar IHttpContextAccessor which allowed service classes to access the current HttpContext out side of controllers

public interface IHttpContextAccessor {
    HttpContext HttpContext { get; }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472