Injecting runtime dependency in unity - unity-container

My application is based on ASP.NET WebAPI and I am using Unity for DI.
My ResourceProvider class has a dependency of type string culturename.
As culturename would be different in every web request.
How should I register ResourceProvider in UnityConfig to be able to pick new culturename for each webrequest.
public ResourceProvider(string cultureName)
{
this.cultureName = cultureName;
}
While registering ResourceProvider
container.RegisterType<IResourceProvider, ResourceProvider>(new PerRequestLifetimeManager());

Related

How to call IdentityServer UserManager method from web API?

I Need to initiate the password reset from my web api. I have identity server application and web api applications separately. I use OIDC client to communicate with identity server. With this client I can only call signin, signout and some standard methods.
What I need is to generate password reset token and get the reset token in API.
I have tried to include 'Microsoft.Extensions.Identity.Core' in my webapi core layer which have all the entities. But I get
Unable to resolve service for type
Microsoft.AspNetCore.Identity.IUserStore1[Application.Core.Entities.User]
while attempting to activate 'Microsoft.AspNetCore.Identity.UserManager
code
private readonly UserManager<User> _userManager;
public AccountController(UserManager<User> userManager)
{
_userManager = userManager;
}
private async Task<string> GeneratePasswordResetLinkAsync(User user)
{
string token = await _userManager.GeneratePasswordResetTokenAsync(user);
return token;
}
I've also tried adding below code in startup Injection
services.AddScoped<UserManager<User>, UserManager<User>>();
What is the proper way of injecting UserManager in my web api ?
You need to configure asp.net core identity in the Startup class of you web api. Unfortunately you can not simply call services.AddIdentity(... in ConfigureServices because behind the scenes a cookie-based authentication scheme is registered and set as the default challenge scheme, as you can see in the code here for asp.net core 2.2
or here for asp.et core 3.1.
Thus the solution I end up with is to copy & update AddIdentity method like this:
For ASP.NET CORE 2.2:
public static IdentityBuilder AddIdentityForWebApi<TUser, TRole>(
this IServiceCollection services,
Action<IdentityOptions> setupAction)
where TUser : class
where TRole : class
{
// Hosting doesn't add IHttpContextAccessor by default
services.AddHttpContextAccessor();
// Identity services
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
services.TryAddScoped<UserManager<TUser>>();
services.TryAddScoped<SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>>();
if (setupAction != null)
{
services.Configure(setupAction);
}
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
}
For ASP.NET CORE 3.1:
static IdentityBuilder AddIdentityForWebApi<TUser, TRole>(
this IServiceCollection services,
Action<IdentityOptions> setupAction)
where TUser : class
where TRole : class
{
// Hosting doesn't add IHttpContextAccessor by default
services.AddHttpContextAccessor();
// Identity services
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
services.TryAddScoped<UserManager<TUser>>();
services.TryAddScoped<SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>>();
if (setupAction != null)
{
services.Configure(setupAction);
}
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
}
Then you should call AddIdentityForWebApi in the Startup of your web api application.
This will register the UserManager and it will now be injected in your controller's constructor.
Then you should configure Data Protection API (DPAPI) properly so that the token generated by your web api (when you call _userManager.GeneratePasswordResetTokenAsync... from your question) could be unprotected by your identity server application.
So I set the "DPAPI application name" across the two applications, in both ConfigureServices methods:
services.AddDataProtection()
.dataProtectionBuilder.SetApplicationName("YOUR_DPAPI_APPLICATION_NAME");
For production, in a web farm environment, you will have to share the DPAPI keys. Depending on your situation you have several options.
Please refer to the official documentation for more details on how to configure DPAPI.
I have tested on Kestrel running Windows, I am not sure about IIS.
The injection code does not look correct. Try changing it to
services.AddScoped<UserManager<Application.Core.Entities.User>>();

DI Container in asp net MVC project- where do you register DAL\BLL classes?

using Web API 2 with Unity IOC. I'm trying to figure out where can I register the types used across the whole solution.
In Asp.Net Web API all types registering is done in a class, called UnityConfig
public class UnityConfig
{
public static void Register(HttpConfiguration config)
{
var container= new UnityContainer();
container.RegisterType<IUsersService, UsersService>(); // BLL type
container.RegisterType<IRolesService, RolesService>(); // BLL type
container.RegisterType<IUnitOfWork, UnitOfWork>(); // DAL type
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
which is then configured when the application first starts by the Global.asax.
PROBLEM IS - as long as its just the BLL services, everything is fine, application is supposed to be referencing BLL anyway.
but when DataAccessLayer repositories, which I also want to inject, come into play, this forces my main project, the "application layer" to have reference to the DAL project. a dependency which I don't want.
Where is my failure in the understanding of the concept ?

Get Current Principal as my Custom Application User in ASP.Net Core Identity

In previous versions of ASP.NET, if I wanted to have a custom class as my current logged in user, what I did was: I let the FormsAuthentication module do its work, and then, in the PostAuthenticateRequest event I replaced the current Principal (HttpContext.Current.User) with my custom principal object that I fetched from the database (with some caching for performance).
How can I achieve the same in ASP.NET Identity? I have my own ApplicationUser (not the default that comes with the EntityFramework ASP.NET Identity) and my own UserStore.
In every authenticated request, I have the HttpContext.User as a ClaimsPrincipal object. Is there a way to replace that with my CustomClaimsPrincipal?
Is there another, better way, to retrieve the current ApplicationUser instance based on the current ClaimsPrincipal?
If you have your own IUserStore you can implement IUserClaimStore to customize the claims identity which is passed to the claims principal.
If you need to replace the default claims principal you should implement the IUserClaimsPrincipalFactory and pass your implementation to the SignInManager and register the configured manager to your owin context.
It should look like this along the lines.
(Assuming you are using ASP.NET Core Identity, for Identity v2 the interfaces and constructors may differ!)
class CustomClaimsFactory<TUser> : Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<TUser>
where TUser : class
{
public Task<ClaimsPrincipal> CreateAsync(TUser user)
{
// create and return your custom principal
}
}
class OwinStartup
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(CreateSignInManager);
}
Microsoft.AspNetCore.Identity.SignInManager CreateSignInManager()
{
UserManager manager; // the manager that uses your custom IUserStore
IHttpContextAccessor accessor; // I don't know where to get it from...
var factory = new CustomClaimsFactory();
return new SignInManager(manager, accessor, factory, null, null, null);
}
}
For ASP.Net Core the OWIN-like startup configuration is done via dependency injection.

Autofac OWIN TestServer and HttpContext

I am trying to setup Integration tests with my IIS Hosted WebAPI 2.2 application. I use Autofac for DI and I am using the new ASP.net Identity stack which uses OWIN. I am running into an issue with Autofac where the HttpContext class is always null. Here is how I am setting up my base integration test class-
[TestClass]
public class TestBase
{
private SimpleLifetimeScopeProvider _scopeProvider;
private IDependencyResolver _originalResolver;
private HttpConfiguration _configuration;
public TestServer Server { get; private set; }
[TestInitialize]
public void Setup()
{
Server = TestServer.Create(app =>
{
//config webpai
_configuration = new HttpConfiguration();
WebApiConfig.Register(_configuration);
// Build the container.
var container = App_Start.IocConfig.RegisterDependencies(_configuration);
_scopeProvider = new SimpleLifetimeScopeProvider(container);
//set the mvc dep resolver
var mvcResolver = new AutofacDependencyResolver(container, _scopeProvider);
_originalResolver = DependencyResolver.Current;
DependencyResolver.SetResolver(mvcResolver);
//set the webapi dep resolvers
_configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(_configuration);
app.UseAutofacMvc();
});
}
[TestCleanup]
public void Cleanup()
{
// Clean up the fake 'request' scope.
_configuration.Dispose();
DependencyResolver.SetResolver(_originalResolver);
_scopeProvider.EndLifetimeScope();
Server.Dispose();
}
}
When a simple test starts, I get an ArgumentNullException "Value cannot be null" httpContext. Which if I track down into the autofac code, I think it is coming from this extension method -
public static class AutofacMvcAppBuilderExtensions
{
internal static Func<HttpContextBase> CurrentHttpContext = () => new HttpContextWrapper(HttpContext.Current);
/// <summary>
/// Extends the Autofac lifetime scope added from the OWIN pipeline through to the MVC request lifetime scope.
/// </summary>
/// <param name="app">The application builder.</param>
/// <returns>The application builder.</returns>
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public static IAppBuilder UseAutofacMvc(this IAppBuilder app)
{
return app.Use(async (context, next) =>
{
var lifetimeScope = context.GetAutofacLifetimeScope();
var httpContext = CurrentHttpContext();
if (lifetimeScope != null && httpContext != null)
httpContext.Items[typeof(ILifetimeScope)] = lifetimeScope;
await next();
});
}
}
loacted in the Core/Source/Autofac.Integration.Mvc.Owin/AutofacMvcAppBuilderExtensions.cs file. Is there a problem with my setup, or a proper way to use Autofac in integration tests with a WebApi application using IIS Host and OWIN Middleware?
It appears you already asked this as an issue over on the Autofac project. I'll copy/paste the answer here (though in the future it'd probably be better to go with one or the other and not both).
Part of the awesomeness of OWIN-only apps is that you don't need HttpContext anymore. Nothing is tied to that; instead, it's all HttpContextBase and things that are separate from the legacy IIS. Like, in Web API, the current context is always shipped around with the HttpRequestMessage - there's no global static HttpContext.Current because that's legacy stuff.
Thus, when you run unit tests with an OWIN test host, you can expect there to not be an HttpContext.Current. It's decoupled from all that.
MVC can't run as OWIN-only because the libraries are tightly coupled to the legacy IIS/ASP.NET stack. Trying to test MVC stuff using an OWIN-only test server is going to give you trouble like this. That will change with the new ASP.NET 5.0 coming out with the new Visual Studio.
If you need to test MVC in an integrated way, there isn't a way to do that with OWIN right now. You have to fire up IIS Express.
Finally, I do see that you're missing the Web API middleware for OWIN (the actual Microsoft Web API middleware). That might give you other problems down the line.
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(_configuration);
app.UseAutofacMvc();
// You're missing this:
app.UseWebApi(config);

Autofac with MVC4: controller does not have a default constructor

I've been working with Autofac in MVC3 and love it. Now I am trying to implement it with MVC4.
I installed the pre-release versions of Autofac MVC4 and Autofac WebApi through the Package Manager Console (Install-Package Autofac.Mvc4 -Pre and Install-Package Autofac.WebApi -Pre)
I adjusted my IoC container as following:
private static void SetAutofacContainer()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerHttpRequest().InstancePerApiRequest();
builder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>().InstancePerHttpRequest().InstancePerApiRequest();
builder.RegisterType<RepositoryWrapper>().As<RepositoryWrapper>().InstancePerHttpRequest().InstancePerApiRequest();
builder.RegisterType<ServiceWrapper>().As<ServiceWrapper>().InstancePerHttpRequest().InstancePerApiRequest();
// Repositories
builder.RegisterAssemblyTypes(typeof(UserRepository).Assembly).Where(t => t.Name.EndsWith("Repository")).AsImplementedInterfaces().InstancePerHttpRequest().InstancePerApiRequest();
// Services
builder.RegisterAssemblyTypes(typeof(UserService).Assembly).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerHttpRequest().InstancePerApiRequest();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
When I run the application (by accessing the API controller) I get the exception:
"Controllers.UserController' does not have a default constructor"
The controller looks like this:
namespace Controllers
{
[Authorize]
public class UserController : ApiController
{
private ServiceWrapper _services;
public UserController(ServiceWrapper services)
{
_services = services;
}
// GET api/user/{userrequest}
public IQueryable<User> Get(UserRequest request)
{
if (ModelState.IsValid)
{
'...
}
}
}
Am I missing something? Did I not set it up right? Any help would be greatly appreciated!
Update
My API controller are within a separate project in the same solution. If I place the API controller in my main MVC project, it works. Could someone please enlighten me on how to get Autofac to register the API controllers in my API project?
With the RegisterApiControllers method you tell Autofac where (in which assembly) it should look for your ApiControllers
So the following call:
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
Registers the ApiControllers from the current assembly (project).
If you have ApiControllers also in a different project you need to use it like this:
builder.RegisterApiControllers(typeof(UserController).Assembly);
Which means: register all the ApiController form the assembly (project) where the UserController lives. So you only need one RegisterApiControllers per assembly even if you have multiple ApiController in an assembly (project).

Resources