6

I am using ASP.Net Identity and wanted to add the ApplicationUserManager service to all of my custom controllers by following this article: How to plug my Autofac container into ASP. NET Identity 2.1

This works perfectly in my controllers, but not when I try to create a token by calling localhost:xxxx/token on my API. Below is the method called, however the context.OwinContext.GetUserManager returns null.

I have tried injecting the ApplicationUserManager into the ApplicationOAuthProvider, but was not able to successfully. Can you please point me in the right direction?

Edit: 10/15

Okay, so I have gotten a bit further, but I am still stuck.I was able to initialize the classes with the following:

    var x = new DatabaseContext();
    var store = new UserStore<ApplicationUser>(x);
    var options = new IdentityFactoryOptions<ApplicationUserManager>()
    {
        DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ApplicationName")
    };

    builder.Register<DatabaseContext>(c => x);
    builder.Register<UserStore<ApplicationUser>>(c => store).AsImplementedInterfaces();
    builder.Register<IdentityFactoryOptions<ApplicationUserManager>>(c => options);
    builder.RegisterType<ApplicationUserManager>();

    builder.Register<ApplicationOAuthProvider>(c => new ApplicationOAuthProvider("self", new ApplicationUserManager(store, options))).As<IOAuthAuthorizationServerProvider>();

This allowed me to pass the ApplicationUserManager into my ApplicationOAuthProvider's constructor. In the Startup.Auth configuration, I initialize the Provider with the following:

OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = (IOAuthAuthorizationServerProvider)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IOAuthAuthorizationServerProvider)),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

This gets me closer to a solution, but still has two problems.

The first is when I call /token on the API the userManager.FindAsync(context.UserName, context.Password) returns a null value, but userManager.FindByEmailAsync(context.UserName) returns the correct user. My initial thought is the password is wrong,but I made sure it was the same password I registered with.

The second issue, is if I call register on my AccountController, and then call /token, I get a Cannot access a disposed object.Object name: 'UserStore' error. So I assume this means I am not initializing the ApplicationOAuthProvider correctly in my Bootstrapper file.

Any guidance would be greatly appreciated. Thanks!

SpruceMoose
  • 9,737
  • 4
  • 39
  • 53
ajpetersen
  • 639
  • 8
  • 17
  • did you get any joy with this? I have the exact same issue where I cannot inject UserManager. I am using Unity but the principle is the same. – Raj Nov 20 '14 at 10:19
  • I haven't found a complete solution and unfortunately I haven't had much luck here. I created a UserService and injected the ApplicationUserManager into that service and then injected the UserService into the ApplicationOAuthProvider. Last I checked I wasn't receiving the disposed object error, but I was still getting a null value when calling FindAsync() on the UserManager. I hope this helps. – ajpetersen Nov 21 '14 at 03:26
  • yup, Im stuck at exactly the same place. Have you tried doing this without any injection? I have been trying but cant seem to get hold of the UserStore object which is very weird. – Raj Nov 21 '14 at 15:58

1 Answers1

2

i finally find the solutions first solution : first: change your bootstrap autofac class you should add singleInstance(); to avoid Per-Request Dependencies error [No Scope with a Tag Matching ‘AutofacWebRequest’]

   builder.RegisterType<DatabaseContext>().AsSelf().SingleInstance();
   builder.Register<IdentityFactoryOptions<ApplicationUserManager>>(c => new IdentityFactoryOptions<ApplicationUserManager>() { DataProtectionProvider = new DpapiDataProtectionProvider("your app name") });
   builder.RegisterType<ApplicationUserManager>().AsSelf().SingleInstance();
   // to resolve applicationUserManager  
   builder.Register(c=>new ApplicationOAuthProvider(c.Resolve<ApplicationUserManager>())).AsImplementedInterfaces().SingleInstance();
   builder.Register(c => new UserStore<ApplicationUser>(c.Resolve<DatabaseContext>())).AsImplementedInterfaces().SingleInstance();
   builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();

second: in Startup.cs will remove GlobalConfiguration.configuration.DependencyResolver because its give null always .. so i will use autofac container resolver but should use it from lifetimescope ,this container returned from your bootstrap autofac configure method

OAuthOptions = new OAuthAuthorizationServerOptions
    {
        TokenEndpointPath = new PathString("/Token"),
        Provider = container.BeginLifetimeScope().Resolve<IOAuthAuthorizationServerProvider>(),
        AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
        AllowInsecureHttp = true
    };

third: in your ApplicationOAuthProvider class will add constructor take applicationUserManager as parameter

this fix my null error after two days google search and can't find the answer, hope it help.

second solution: because SingleInstance() not good for Enterprise applications so you can use InstancePerRequest(); for all registerTypes

   builder.RegisterType<DatabaseContext>().AsSelf().InstancePerRequest();
   builder.Register<IdentityFactoryOptions<ApplicationUserManager>>(c => new IdentityFactoryOptions<ApplicationUserManager>() { DataProtectionProvider = new DpapiDataProtectionProvider("your app name") });
   builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest();
   // to resolve applicationUserManager  
   builder.Register(c=>new ApplicationOAuthProvider(c.Resolve<ApplicationUserManager>())).AsImplementedInterfaces().InstancePerRequest();
   builder.Register(c => new UserStore<ApplicationUser>(c.Resolve<DatabaseContext>())).AsImplementedInterfaces().InstancePerRequest();
   builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();

in Startup.cs

 OAuthOptions = new OAuthAuthorizationServerOptions
    {
        TokenEndpointPath = new PathString("/Token"),
       // will instantiate new one to avoid Single Instance for resolving
       Provider = new CustomOAuthProvider(new ApplicationUserManager(new UserStore<Entities.ApplicationUser>(new DataContext()),
        AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
        AllowInsecureHttp = true
    };

CustomOAuthProvider class

using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
using System.Threading.Tasks;

public class CustomOAuthProvider:OAuthAuthorizationServerProvider
{
    private ApplicationUserManager _appUserManager;
    public CustomOAuthProvider(ApplicationUserManager appUserManager)
    {
        this._appUserManager = appUserManager;
    }



    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var allowedOrigin = "*";

        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });


        var userManager = new ApplicationUserManager(new    Microsoft.AspNet.Identity.EntityFramework.UserStore<AppUser>(new      Data.DataContext()),new IdentityFactoryOptions<ApplicationUserManager>(),new Data.Repositories.SettingRepository(new Data.Infrastructure.DbFactory()));

       AppUser user = await userManager.FindAsync(context.UserName, context.Password);

        if (user == null)
        {
            context.SetError("invalid_grant", "Invalid username or password.");
            return;
        }
        if (!user.IsActive)
        {
            context.SetError("invalid_activation", "Inactive account, contact support.");
            return;
        }

        if (!user.EmailConfirmed)
        {
            context.SetError("invalid_grant", "User did not confirm email.");
            return;
        }


        ClaimsIdentity oAuthIdentity = await userManager.GenerateUserIdentityAsync(user, "JWT");

        AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, null);
        context.Validated(ticket);

    }


    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        if (context.ClientId == null)
        {
            context.Validated();

        }

        return Task.FromResult<object>(null);
    }



}
SpruceMoose
  • 9,737
  • 4
  • 39
  • 53
Hisham
  • 1,279
  • 1
  • 17
  • 23
  • Worked for me but not ideal as now I have coded a dependency on my IoC. Did you ever explore other options? – Ben Hall Nov 03 '16 at 15:48
  • i updated the way to resolve provider and work great you can check. – Hisham Nov 04 '16 at 00:23
  • the second solution is not injecting anything?!? Just creating new objects, so what is the purpose of using IoC? – emp Mar 20 '18 at 13:25
  • creating new object not solving the problem it gives you another empty instance – Hisham Mar 23 '18 at 06:17