Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Injector get current principal in WebAPI using OWIN

Is there anyway to access the current principal and before the request gets to the controller using Simple Injector? I am using OWIN and Asp.net identity.

I have a DbContext that I inject into my controllers, but this context will get it's connection string based off the authenticated user. This is what I have so far,

container.RegisterWebApiRequest<TenantDbContext>();
container.RegisterWebApiRequest<ITenantConnectionStringProvider>(() => new TenantConnectionStringProvider(container));

Then in my TenantConnectionStringProvider I have this,

var request = container.GetCurrentHttpRequestMessage();
var principal = request.GetRequestContext().Principal as ClaimsPrincipal;

But the principal has no claims. I realized the claims are only available after the controller has been created. Does this mean it's just not possible because this step comes before the controller is created?

Edit: This is basically what the rest of the code does:

WebApi Controller

    public CampaignsController(TenantDbContext context, ILog log)
    {
        this.campaignService = campaignService;
        this.log = log;
    }

Tenant context(just inherits from DbContext from EF):

    public TenantDbContext(ITenantConnectionStringProvider provider)
        : base(provider.GetConnectionString())
    {
    }

After messing around a bit, I was able to do this, but it feels very hacky.. I added an OWIN middleware that happens after authentication. I'm not sure why but I have all the authenticated users information here, but when it goes to the TenantConnectionStringProvider, none of this info is available on the HttpRequestMessage.

        app.Use(async (context, next) =>
        {
            using (container.BeginExecutionContextScope())
            {
                CallContext.LogicalSetData("Claims", context.Authentication.User.Claims);
                var request = (OwinRequest)context.Request;
                await next();
            }
        });

Then in my TenantConnectionStringProvider I just did this,

    public string GetConnectionString()
    {
        var context = (IEnumerable<Claim>)CallContext.LogicalGetData("Claims");
        return "test";//get claim from context to get the connection string
    }
like image 325
Steveadoo Avatar asked Feb 01 '16 23:02

Steveadoo


2 Answers

You can register a Func<ClaimsPrincipal> (factory) and inject it to your TenantConnectionStringProvider class :

public class TenantConnectionStringProvider : ITenantConnectionStringProvider
{
    private readonly Func<ClaimsPrincipal> _claimsPrincipalFactory;
    public TenantConnectionStringProvider(Func<ClaimsPrincipal> claimsPrincipalFactory)
    {
        _claimsPrincipalFactory = claimsPrincipalFactory;            
    }

    public void TestMethod()
    {
        // Access the current principal
        var principal = _claimsPrincipalFactory();
    }
}

Registrations should look like that (not sure...):

// Register your types, for instance using the scoped lifestyle:
container.RegisterSingleton<Func<ClaimsPrincipal>>(() =>
{
    // Not sure of which one to use.
    //return (ClaimsPrincipal)HttpContext.Current.User;
    //return (ClaimsPrincipal)Thread.CurrentPrincipal;
    return (ClaimsPrincipal)Context.User;
});
container.RegisterWebApiRequest<ITenantConnectionStringProvider, TenantConnectionStringProvider>();
like image 151
Thomas Avatar answered Oct 20 '22 01:10

Thomas


Does this mean it's just not possible because this step comes before the controller is created?

Definitely the way you've created it. I'm not completely familar with the simple-injector but I bet using Lazy<> might be of benefit:

var request = container.GetCurrentHttpRequestMessage();
var principal = new Lazy<ClaimsPrincipal>(() => 
  {
    return request.GetRequestContext().Principal as ClaimsPrincipal;
  });

Not tested, but when you actually need the principal value (i'm assume sometime later, in a different method in the same class) you can use principal.Value and it would then run and retrieve your ClaimsPrincipal. Granted these are huge assumptions because there is no code to see how everything is wired up. If you can provide how you get your connectionstring for the dbcontext (how that is all wired up) I might be able to give you a complete solution.

like image 37
Erik Philips Avatar answered Oct 19 '22 23:10

Erik Philips