autofac / Autofac.WebApi

ASP.NET Web API integration for Autofac
Other
36 stars 27 forks source link

Weird User.Identity in a InstancePerRequest resolution #11

Closed tillig closed 8 years ago

tillig commented 8 years ago

From @toben on November 9, 2015 14:34

It's weird, when I am in my ApiController, HttpContext.Current.User.Identity is good. IsAuthenticated is true, name is the username, etc... But, before that, when I am in a resolution of an "InstancePerRequest" injection, HttpContext.Current.User.Identity is weird and all fields are null or empty...

            builder.Register(c => new HttpContextWrapper(HttpContext.Current))
                .As<HttpContextBase>()
                .InstancePerRequest();

This is the code which inject HttpContext.Current in other injections. How that can be possible ?

Copied from original issue: autofac/Autofac#692

tillig commented 8 years ago

Can you put together a small reproduction showing the issue and post it somewhere? I've never seen the behavior you describe and we can't troubleshoot it just based on the description.

tillig commented 8 years ago

Also, I'm going to move this to the Autofac.WebApi repo since we track issues for individual integrations in the respective repositories.

toben commented 8 years ago

Ok there is 4 parts in my code :

Configuration

            builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
            builder.RegisterModule(new DefaultTestModule());
            var container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
            app.UseAutofacMiddleware(container);
            app.UseAutofacWebApi(config);
            app.UseWebApi(config);

Injection


            builder.Register(c => (ClaimsIdentity) HttpContext.Current.GetOwinContext().Authentication.User.Identity)
                .As<ClaimsIdentity>()
                .InstancePerRequest();
            builder.Register(c => new HttpContextWrapper(HttpContext.Current))
                .As<HttpContextBase>()
                .InstancePerRequest();

Utilisation

 var httpContext = c.Resolve<HttpContextBase>();
            if (httpContext == null)
            {
                return null;
            }
            var context = c.Resolve<TestContext>();
            var tenantProvider = c.Resolve<ITenantProvider>();
            var tenant = tenantProvider.GetTenant(httpContext);
 var identity = (ClaimsIdentity)context.User.Identity;
            List<Claim> claims = identity.Claims.ToList();
            var claim = claims.Where(c => c.Type == "TenantId").FirstOrDefault();
            if (claim == null)
            {
                throw new WrongTokenException("Cannot resolve tenantid");
            }
            return Guid.Parse(claim.Value);

In the utilisation, identity is always empty (name, claims, etc...) but when I go in my ApiController...I can do that

if (string.IsNullOrWhiteSpace(_UserId))
                {
                    var identity = (ClaimsIdentity)User.Identity;
                    List<Claim> claims = identity.Claims.ToList();
                    var claim = claims.Where(c => c.Type == "UserId").FirstOrDefault();
                    if (claim == null)
                    {
                        throw new WrongTokenException("Cannot resolve userid");
                    }
                    _UserId = claim.Value;
                }
                return _UserId;

The identity is correct.

tillig commented 8 years ago

The reason I need a reproduction and not just some snippets of code is that I need to see when in the app things are happening. For example, your authentication process - the thing that sets the identity on the HttpContext - may be running after your "utilisation" section, which would explain why it's null: it hasn't been set yet.

But I can't tell from just snippets. I need a full working repro.

toben commented 8 years ago

Sorry, it would be really hard to make a reproduction from my code without showing important stuff regarding my company. Just one thing got my attention, when you said : " For example, your authentication process - the thing that sets the identity on the HttpContext - may be running after your "utilisation" section, which would explain why it's null: it hasn't been set yet.". The thing is I have nothing in my code which set the Identity, I think Owin takes care of it. I will try to check that and if I don't resolve that, I will try to give you a real reproduction.

cwoolumtechnossus commented 8 years ago

AFAIK The part of Owin that sets the identity occurs after the constructor for the controller fires. User info is not available until the Controller method is hit.

PilotBob commented 5 years ago

AFAIK The part of Owin that sets the identity occurs after the constructor for the controller fires. User info is not available until the Controller method is hit.

Has anybody been able to find a way to get the current user while autofac is creating the api controller? I've tried to get to the RequestContext, but Identity is always just an empty generic claim principle with a null auth type, and not our Bearer authed principle. Without being able to do this, I can't inject our db context into api controllers because it needs the user/tenant info to get a connection string.

It's strange that when injecting the EF db context into MVC controllers using WS-Fed owin auth the user is in HttpContext.Current.User but not when creating webapi2 controllers.

xwizard102 commented 5 years ago

@PilotBob have you found a workaround to inject db context with the right user identity?

PilotBob commented 5 years ago

@PilotBob have you found a workaround to inject db context with the right user identity?

No. We ended up not using constructor injection in our api controllers. We use the Containter.Resolve method in the action.

I think this is because our API auth is passive. I expect this would work if it was active. But since our api is shared by our MVC controllers where ws-fed is the active auth that's not an option. I think the only way to do it would be to make the api a separate app. But, I haven't tried to, or wanted to go down that road.