aspnet / Identity

[Archived] ASP.NET Core Identity is the membership system for building ASP.NET Core web applications, including membership, login, and user data. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
1.96k stars 870 forks source link

ASP.Net Identity not being injected as expected #1898

Closed replaysMike closed 6 years ago

replaysMike commented 6 years ago

I've run into an issue where controllers can't be instantiated because my custom UserManager and SignInManager aren't injected (latest 2.1.2):

InvalidOperationException: Unable to resolve service for type 'TestAuthentication.Data.Identity.ApplicationUserManager' while attempting to activate 'TestAuthentication.Controllers.DefaultController'.

I had this working in another project and I don't know what's causing it. I created a brand new test project which duplicates the issue at https://github.com/replaysMike/TestAuthentication

// Controller.cs
[Produces("application/json")]
[AllowAnonymous]
public class DefaultController : Controller
{
    public DefaultController(ApplicationUserManager userManager)
    {
    }

    [HttpGet]
    [Route("api/test")]
    [ProducesResponseType(typeof(string), 200)]
    public IActionResult TestMethod()
    {
        return Ok("Welcome");
    }
}

// Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services
        .AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        //.AddControllersAsServices();  // tell asp.net to use LightInject to create controllers

    services.AddDbContext<AuthenticationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("AuthenticationConnection")));

    services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
    {
        // User settings
        options.User.RequireUniqueEmail = true;
    })
    .AddEntityFrameworkStores<AuthenticationDbContext>()
    .AddUserStore<ApplicationUserStore>()
    .AddUserManager<ApplicationUserManager>()
    .AddSignInManager<ApplicationSignInManager>()
    .AddDefaultTokenProviders();

    services.AddScoped<UserStore<ApplicationUser, ApplicationRole, AuthenticationDbContext, int>,ApplicationUserStore>();
    services.AddScoped<UserManager<ApplicationUser>, ApplicationUserManager>();
    services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();
    services.AddScoped<ApplicationUserStore>();
    services.AddScoped<ApplicationUserManager>();
    services.AddScoped<ApplicationSignInManager>();

    services.AddRouting();

    // use lightinject to instantiate controllers
    var serviceProvider = Container.CreateServiceProvider(services);
    //var userStore = serviceProvider.GetRequiredService<IUserStore<ApplicationUser>>(); // error
    return serviceProvider;
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStaticFiles();
    app.UseCookiePolicy();

    app.UseMvc();
}

and other files:

public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, AuthenticationDbContext, int>
{
    public ApplicationUserStore(AuthenticationDbContext context) : base(context)
    {
    }

    public async Task<ApplicationUser> FindByTemporaryLoginTokenAsync(string token)
    {
        var tokenGuid = Guid.Empty;
        var bytes = Convert.FromBase64String(token);
        tokenGuid = new Guid(bytes);

        var user = await Context.Users
            .Where(x =>
                x.TemporaryTwoFactorLoginToken.Equals(tokenGuid)
                && x.DateTemporaryTwoFactorLoginTokenExpiresUtc > DateTime.UtcNow
            )
            .FirstOrDefaultAsync();

        return user;
    }
}

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(ApplicationUserStore store, IOptions<IdentityOptions> optionsAccessor, 
        IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators, 
        IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer, 
        IdentityErrorDescriber errors, IServiceProvider services, 
        ILogger<UserManager<ApplicationUser>> logger)
        : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    {
    }
}

public class ApplicationSignInManager : SignInManager<ApplicationUser>
{
    public ApplicationSignInManager(
        ApplicationUserManager userManager,
        IHttpContextAccessor contextAccessor,
        IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
        IOptions<IdentityOptions> optionsAccessor,
        ILogger<SignInManager<ApplicationUser>> logger,
        IAuthenticationSchemeProvider schemes)
    : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes)
    {
    }
}

[Table("Users")]
public class ApplicationUser : IdentityUser<int>
{
}

public class ApplicationRole : IdentityRole<int>
{
}
replaysMike commented 6 years ago

This appears to be to do with LightInject upon further investigation. When I comment out the LightInject provider and return a standard MS provider I can access the ApplicationSignInManager instance.

I dug into the LightInject provider and it does seem to be aware of the type, it looks registered to me. I'm taking this over to the LightInject guys to see why this isn't working right.