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!
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With