I have trouble using MembershipReboot with the new ASP MVC5 template and Autofac. I have used the default MVC5 template to set up the site and then tried to wire up the MembershipReboot framework as a replacement for the ASP Identity framework that ships with the template.
This issue I am having is trying to resolve an IOwinContext from the Autofac container. Here is my wiring in the Startup class (cut down to basics). This is the wiring used in the samples for the MembershipReboot Owin application (except there he uses Nancy).
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.Register(c => new DefaultUserAccountRepository())
.As<IUserAccountRepository>()
.As<IUserAccountQuery>()
.InstancePerLifetimeScope();
builder.RegisterType<UserAccountService>()
.AsSelf()
.InstancePerLifetimeScope();
builder.Register(ctx =>
{
**var owin = ctx.Resolve<IOwinContext>();** //fails here
return new OwinAuthenticationService(
MembershipRebootOwinConstants.AuthenticationType,
ctx.Resolve<UserAccountService>(),
owin.Environment);
})
.As<AuthenticationService>()
.InstancePerLifetimeScope();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
ConfigureAuth(app);
app.Use(async (ctx, next) =>
{
using (var scope = container.BeginLifetimeScope(b =>
{
b.RegisterInstance(ctx).As<IOwinContext>();
}))
{
ctx.Environment.SetUserAccountService(() => scope.Resolve<UserAccountService>());
ctx.Environment.SetAuthenticationService(() => scope.Resolve<AuthenticationService>());
await next();
}
});
}
And here is my controller with the dependency specified in the controller constructor.
public class HomeController : Controller
{
private readonly AuthenticationService service;
public HomeController(AuthenticationService service)
{
this.service = service;
}
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
It seems I need to wrap my Autofac container in an AutofacDependencyResolver in order for the MVC framework to use the container to resolve components. This is the only major difference from the Nancy Owin sample and my use in MVC5.
When I do this, it then seems (from my Tracing) as if the dependency is being resolved without first going through the OWIN middleware stack so the IOwinContext is never registered.
What am I doing wrong here?
UPDATE:
Brock, your new sample works perfectly when I migrate the configuration over to my project. Just for my understanding, it seems that this line in your new sample registers the current OwinContext with the container and that is what was missing previously.
builder.Register(ctx=>HttpContext.Current.GetOwinContext()).As<IOwinContext>();
Is that
There's a newer sample that does DI with AutoFac for MVC:
https://github.com/brockallen/BrockAllen.MembershipReboot/blob/master/samples/SingleTenantOwinSystemWeb/SingleTenantOwinSystemWeb/Startup.cs
See if this helps.
If you don't want to use HttpContext.Current you could do something like this:
app.Use(async (ctx, next) =>
{
// this creates a per-request, disposable scope
using (var scope = container.BeginLifetimeScope(b =>
{
// this makes owin context resolvable in the scope
b.RegisterInstance(ctx).As<IOwinContext>();
}))
{
// this makes scope available for downstream frameworks
ctx.Set<ILifetimeScope>("idsrv:AutofacScope", scope);
await next();
}
});
This is what we're doing internally for some of our apps. You'd need to wire up your Web API service resolver to look for "idsrv:AutofacScope". Tugberk has a post on this:
http://www.tugberkugurlu.com/archive/owin-dependencies--an-ioc-container-adapter-into-owin-pipeline
Related
setup with Asp.net & .Net Core 3.1, I have upgraded a previous Identity system using UserName/Password with Roles to use Windows Authentication.
I have created a ClaimsTransformation which gets the windows Identity and creates a new ClaimsPrincipal with the users associated roles. This part is working
My startup.cs looks like this (some parts removed)
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IClaimsTransformation, KiwaClaimsTransformation>();
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddAuthorization();
...
services.AddControllers();
services.AddControllersWithViews()
.AddSessionStateTempDataProvider();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
{
...
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
// catch all for not found
endpoints.MapControllerRoute("NotFound", "{*url}",
new {controller = "Error", action = "ResourceNotFound"});
});
...
}
The ClaimsTransformation looks like this
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var identity = (ClaimsIdentity)principal.Identity;
if (identity == null) return principal;
var userName = _config["LoginUserName"];
if (userName == null)
{
userName = identity.Name;
if (userName == null) return principal;
}
// need to go and build the Roles claims for the user based on the User Name as a lookup on User table
var claims = new List<Claim>
{
new Claim(#"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", userName, "Name")
};
claims.AddRange(_userLookup.GetUserRolesByNetworkId(userName)
.Select(role => new Claim(ClaimTypes.Role, role)));
//The claim identity uses a claim with the claim type below to determine the name property.
// Get User Roles from database and add to list of claims.
var newClaimsIdentity = new ClaimsIdentity(claims, "Kerberos", "", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
return new ClaimsPrincipal(new ClaimsPrincipal(newClaimsIdentity));
}
I have a basic HomeController which looks like this
public class HomeController : Controller
{
private readonly LoggedOnUser _loggedOnUser;
public HomeController(LoggedOnUser loggedOnUser)
{
_loggedOnUser = loggedOnUser;
}
[Authorize]
[HttpGet]
public IActionResult Index()
{
// check and make sure the user is allowed in
if (!_loggedOnUser.IsValidKiwaUser)
{
return RedirectToActionPermanent("NotAuthorised");
}
return View();
}
[Authorize]
public IActionResult OperationResults()
{
ViewBag.Title = (string)TempData["Title"];
string jsonString = (string)TempData["OperationResults"];
if (string.IsNullOrWhiteSpace(jsonString))
{
return RedirectToPage("/Error/NoResults");
}
return View(JsonConvert.DeserializeObject<List<OperationResult>>(jsonString));
}
public IActionResult NotAuthorised()
{
return View();
}
All of the Controllers have [Authorize(Role="...")], and the Authorisation is happening correctly and the Roles are added as claims via the ClaimsTransformation.
The issue i am having is that if i hit the root of the Website (debugging this is https://localhost:44391), then the routing sends me to the NotAuthorised page on the controller??? It should be default go to https://localhost:44391/Home/index as defined in the default Endpoint.
If I type in https://localhost:44391/Home/index it works and shows the correct main landing page, but if i do NOT include the https://localhost:44391/Home/index in its entirety then it comes back as unauthorized.
Am i missing something here? Also can i turn
I eventually found the issue. During the transition to change over to Windows Authentication, i had left the cookie support i the product. But what this had done was store the starting page as being the NotAuthorised page. Clearing the cookie (and subsequently removing the cookie support from the app), fixed the issue and the Roles were evaluated all the time. Hence why I used a lookup (memory Cache) for accessing the user and their claims - as it gets called for all User requests
Oh by the way. The check for _loggedOnUser.IsValidKiwaUser in the HomeController/Index is actually no longer required if you use this as an example
I'm trying to access session storage in a razor view for a .net core 2.0 project. Is there any equivalent for #Session["key"] in a .net 2.0 view? I have not found a working example of how to do this - I am getting this error using the methods I have found:
An object reference is required for the non-static field, method, or propery HttpContext.Session
View:
#using Microsoft.AspNetCore.Http
[HTML button that needs to be hidden/shown based on trigger]
#section scripts {
<script>
var filteredResults = '#HttpContext.Session.GetString("isFiltered")';
</script>
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(30);
});
services.AddMvc();
// Added - uses IOptions<T> for your settings.
// Added - replacement for the configuration manager
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//exception handler stuff
//rewrite http to https
//authentication
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
You can do dependency injection in views, in ASP.NET Core 2.0 :)
You should inject IHttpContextAccessor implementation to your view and use it to get the HttpContext and Session object from that.
#using Microsoft.AspNetCore.Http
#inject IHttpContextAccessor HttpContextAccessor
<script>
var isFiltered = '#HttpContextAccessor.HttpContext.Session.GetString("isFiltered")';
alert(isFiltered);
</script>
This should work assuming you have the relevant code in the Startup.cs class to enable session.
public void ConfigureServices(IServiceCollection services)
{
services.AddSession(s => s.IdleTimeout = TimeSpan.FromMinutes(30));
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
To set session in a controller, you do the same thing. Inject the IHttpContextAccessor to your controller and use that
public class HomeController : Controller
{
private readonly ISession session;
public HomeController(IHttpContextAccessor httpContextAccessor)
{
this.session = httpContextAccessor.HttpContext.Session;
}
public IActionResult Index()
{
this.session.SetString("isFiltered","YES");
return Content("This action method set session variable value");
}
}
Use Session appropriately. If you are trying to pass some data specific to the current page, (ex : Whether the grid data is filtered or not , which is very specific to the current request), you should not be using session for that. Consider using a view model and have a property in that which you can use to pass this data. You can always pass these values to partial views as additional data through the view data dictionary as needed.
Remember, Http is stateless. When adding stateful behavior to that, make sure you are doing it for the right reason.
put this at the top of the razor page
#using Microsoft.AspNetCore.Http;
then you can easily access session variables like that
<h1>#Context.Session.GetString("MyAwesomeSessionValue")</h1>
if you get null values , make sure you include that in your Startup.cs
& make sure that options.CheckConsentNeeded = context is set to false
For more information about CheckConsentNeeded check this GDPR
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
//options.CheckConsentNeeded = context => true;
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set session timeout value
options.IdleTimeout = TimeSpan.FromSeconds(30);
options.Cookie.HttpOnly = true;
});
}
Also make sure you are adding app.UseSession(); to your app pipeline in Configure function
for more info about Sessions in Asp.net Core check this link Sessions in Asp.net Core
tested on .net core 2.1
As others have mentioned, I think the real solution here is not to do this at all. I thought about it, and while I have a good reason for using the session, since the razor tags are only useful for the initial page load anyway it makes more sense to just populate the view model in the controller with the stored session values.
You can then pass the view model with the current session values to your view, and access your model instead. Then you don't have to inject anything into your view.
below code worked for me in .net 6
in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromDays(1);
});
services.AddMvc().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSession();
}
in controller
[HttpPost]
public async Task<IActionResult> GetData([FromBody] IncomignRequest request)
{
if (request?.UserId != null)
{
HttpContext.Session.SetString("CurrentUser", request.UserId);
return Json(true);
}
else
return Json(false);
}
in HTML
#using Microsoft.AspNetCore.Http
#inject IHttpContextAccessor HttpContextAccessor
<script>
var user = #Json.Serialize(#HttpContextAccessor.HttpContext.Session.GetString("CurrentUser"))
</script>
I'm trying to setup a project that uses both MVC and Web API via OWIN and I'm having trouble getting Autofac to take a effect.
Here's how I'm initializing Web API:
public partial class Startup
{
public static void ConfigureWebApi(IAppBuilder app)
{
var config = BuildHttpConfiguration();
var container = AutoFacConfig.BuildContainer();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
private static HttpConfiguration BuildHttpConfiguration()
{
var config = new HttpConfiguration();
// Response formatter config
config.Formatters.Remove(
GlobalConfiguration.Configuration.Formatters.XmlFormatter);
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
// Setup Web API error reporting
var customErrors = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
IncludeErrorDetailPolicy errorDetailPolicy;
switch (customErrors.Mode)
{
case CustomErrorsMode.RemoteOnly:
errorDetailPolicy
= IncludeErrorDetailPolicy.LocalOnly;
break;
case CustomErrorsMode.On:
errorDetailPolicy
= IncludeErrorDetailPolicy.Never;
break;
case CustomErrorsMode.Off:
errorDetailPolicy
= IncludeErrorDetailPolicy.Always;
break;
default:
throw new ArgumentOutOfRangeException();
}
config.IncludeErrorDetailPolicy = errorDetailPolicy;
config.MapHttpAttributeRoutes();
SwaggerConfig.ConfigureSwagger(config);
return config;
}
}
The BuildContainer() method looks like the following. This method is used to build the container for both MVC and Web API:
public static IContainer BuildContainer()
{
var builder = new ContainerBuilder();
// Register your MVC controllers.
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.PropertiesAutowired();
builder.RegisterApiControllers(typeof(MvcApplication).Assembly);
// OPTIONAL: Register model binders that require DI.
builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
builder.RegisterModelBinderProvider();
// OPTIONAL: Register web abstractions like HttpContextBase.
builder.RegisterModule<AutofacWebTypesModule>();
// OPTIONAL: Enable property injection in view pages.
builder.RegisterSource(new ViewRegistrationSource());
// OPTIONAL: Enable property injection into action filters.
builder.RegisterFilterProvider();
// Bind the core types
Core.Infrastructure.AutoFacConfig.BuildContainer(builder);
builder.RegisterType<Postal.EmailService>().As<Postal.IEmailService>();
// Effectively auto-wires the anything with an interface within Web assembly infrastructure folder
builder.RegisterAssemblyTypes(typeof(IJwtHelper).Assembly)
.Where(t => t.Namespace != null && t.Namespace.StartsWith("MyApp.Web.Infrastructure") && t.GetInterfaces().Any())
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
// Set the dependency resolver to be Autofac.
return builder.Build();
}
I have Web API controllers setup in an area and I had everything working with standard MVC controllers. I would like to use Web API controllers for where it's appropriate, but every time I try to request a controller based on ApiController I get the error:
An error occurred when trying to create a controller of type 'XxxController'. Make sure that the controller has a parameterless public constructor.
-- Edit --
The API area config looks like the following. (Side note: I know this isn't the "proper" way to configure Web API. I include the {action} because I feel there are too many controller files otherwise.)
public class ApiAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "Api"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.MapHttpRoute(
"Api_default",
"Api/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
The authentication controller looks like this:
public class AuthenticationController : MyAppApiJwtController
{
private readonly IUserRepository _userRepository;
private readonly IAppSettingsHelper _appSettingsHelper;
private readonly IJwtHelper _jwtHelper;
private readonly IDeviceRepository _deviceRepository;
private readonly IINLDataService _inlDataService;
public AuthenticationController(IUserRepository userRepository, IAppSettingsHelper appSettingsHelper, IJwtHelper jwtHelper, IDeviceRepository deviceRepository, IINLDataService inlDataService)
{
_userRepository = userRepository;
_appSettingsHelper = appSettingsHelper;
_jwtHelper = jwtHelper;
_deviceRepository = deviceRepository;
_inlDataService = inlDataService;
}
[HttpPost]
[AllowAnonymous]
public LoginResponseModel Login(LoginModel model)
{
...
}
}
The MyAppApiJwtController looks like this:
public class MyAppApiJwtController : ApiController
{
internal IAuthenticationManager AuthenticationManager
{
get { return Request.GetOwinContext().Authentication; }
}
private JwtUserIdentity _currentJwtUser;
public JwtUserIdentity CurrentJwtUser
{
get
{
if (_currentJwtUser != null)
return _currentJwtUser;
if (User == null)
return null;
_currentJwtUser = new JwtUserIdentity((ClaimsIdentity)User.Identity);
return _currentJwtUser;
}
}
}
-- Edit 2 --
The URL I'm attempting to use is http://localhost:20630/api/authentication/login
-- Edit 3 --
The MVC configuration looks like the following. This is called just before the Web API configuration:
public partial class Startup
{
public static void ConfigureMvc()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
MvcConfig.RegisterRoutes(RouteTable.Routes);
MvcConfig.ValueConfig();
BundleConfig.RegisterBundles(BundleTable.Bundles);
AutomapperConfig.Configure();
JsonConfig.Configure();
AutoFacConfig.ConfigureContainer();
}
}
ASP.NET Web API does not support MVC areas by default.
This code:
public class ApiAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "Api"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.MapHttpRoute(
"Api_default",
"Api/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
Will instruct the framework to map any route starting with Api to MVC controllers, but, at the same time, such controllers exists only for Web API. This conflict directly relates to the exception thrown when the Dependency Resolver tries to create an instance of the controller (the exception message may be misleading).
What could be happening:
MVC is executed first, and tries to map your route api/authentication/login. This URI matches your AreaRegistration, so it will try to route the request to the AuthenticationController inside your Api area.
MVC asks the Dependency Resolver to create an instance of the above controller, that must inherit from Controller (that's because we are in the MVC context).
The resolver does not have a registration for a MVC Controller that is called AuthenticationController (the AuthenticationController inherits from ApiController), and it returns null (because that's the expected behavior of the IServiceProvider.GetService method).
MVC then reverts to its default implementation for creating the controller, but finds that AuthenticationController class does not have a parameterless constructor. An exception is thrown.
Please try by removing this area declaration: it is not useful (add your api prefix inside RoutePrefix or Route attributes for your controllers/actions) and works only for MVC, while you are defining Web API as an OWIN middleware.
Reference:
ASP.Net WebAPI area support
I have created a project that uses Entity Framework 6. This project is a data layer that is used in multiple other projects, for example an MVC website and a Web API.
When a user changes something in the MVC website, it is stored through the data layer in the database. But the Web API project does not detect these changes.
My DbContext is injected in the controllers with Ninject. I tried Disposing the DbContext in the controller's Dispose method, which didn't work. Should I Refresh() the database context after it has been injected in the controller's constructor? Or is there another way to keep the DbContext in sync with the database?
Fwiw, here is the OWIN Startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
config.DependencyResolver = new NinjectResolver(NinjectWebCommon.CreateKernel());
app.UseWebApi(config);
}
}
And here is the Ninject code:
public static class NinjectWebCommon
{
public static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
GlobalConfiguration.Configuration.DependencyResolver = new WebApiContrib.IoC.Ninject.NinjectResolver(kernel);
// Register Services:
kernel.Bind<MyDbContext>().To<MyDbContext>().InRequestScope().WithConstructorArgument("disableOrgFilter", true);
return kernel;
}
}
I'm developing a multi tenant application. For now what I want to achieve is obtaining a unique instance of HttpContext per tenant.
Each tenant has its own database.
All tenants share same functionality (There aren't any tenant X specific controllers)
There is a master database for querying all tenants (So before accessing tenant's settings (eg:connectionString) there must be at least one hit to master database).
Tenant is identified from RequestContext RouteData.Values["tenant"]
Here is some simplified code so please do not focus on the architecture:
Route url pattern:{tenant}/{controller}/{action}/{id}
Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
// Delegates for BuildSessionFactory and GetSession are declared under Application_Start()
// Registers SessionFactory to be Singletone per Tenant
builder.Register(BuildSessionFactory).As<ISessionFactory>().InstancePerTenant();
builder.Register(GetSession).As<ISession>().InstancePerRequest();
// This is the module responsible for mapping HttpContextBase to HttpContextWrapper and other mvc specific abastractions
builder.RegisterModule(new AutofacWebTypesModule());
var container = builder.Build();
// Build multitenant container
var mtc = new MultitenantContainer(new RouteDataTenantIdentificationStrategy("tenant"), container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc));
}
private ISessionFactory BuildSessionFactory(IComponentContext context)
{
return new NHibernateConfiguration().BuildConfiguration().BuildSessionFactory();
}
private ISession GetSession(IComponentContext context)
{
return context.Resolve<ISessionFactory>().OpenSession();
}
HomeController sample:
public class HomeController : Controller
{
private readonly ISession _session;
private readonly HttpContextBase _context;
public HomeController(ISession session, HttpContextBase context)
{
_session = session;
_context = context;
}
public ActionResult Index()
{
// Setting up HttpContext.Session["testSessionKey"] only for tenant1
if (_context.Request.RequestContext.RouteData.Values["tenant"] as string == "tenant1")
{
_context.Session.Add("testSessionKey","Some value specific to tenant1 only");
}
using (var transaction = _session.BeginTransaction(IsolationLevel.ReadCommitted))
{
var tenants = _session.Query<Tenant>().ToList();
transaction.Commit();
return View(tenants);
}
}
}
The Workflow:
Remark: The code above ignores the true existance of the tenant. It just satisfies the dependencies according to the RouteDataTenantIdentificationStrategy("tenant") class definition (identification by RouteData nothing exceptional).
Now when I'm accessing the url: /tenant1/Home/Index a HttpContext.Session['testSessionKey'] is added.
When I try to acces something like /otherTenant/Home/Index the HttpContext.Session['testSessionKey'] is still there.
Question 1:
How to achieve Unique HttpContext per tenant using Dependency Injection with Autofac?
Question 2:
How to achieve something more than a HttpContext? Lets say a WorkContext which includes the tenant's HttpContext
If something is unclear please ask and I'll provide necessary clarifications. Thank you!
You can do this if you push the customization of the context values to the registration of HttpContextBase. Instead of using the AutofacWebTypesModule, use your own registration:
builder.Register(c =>
{
var httpContext = new HttpContextWrapper(HttpContext.Current);
var strategy = c.Resolve<ITenantIdentificationStrategy>();
// Update the context here
return httpContext;
}).As<HttpContextBase>()
.InstancePerRequest();
That means you should also have your tenant ID strategy registered in the container, but that's pretty easy.
(Sorry for the short snippet; I'm on my phone traveling.)