I have written two very nicely supported WebAPI based upon this this tutorial. Now the thing is that I have not had the need for IoC/DI yet, but I'm writing a Web API and trying to incorporate it from the ground up.
I have a good handle of the repository/service/unit of work pattern, but I am getting errors when porting over the Identity piece of it all. Specifically the on startup with the OAuthConfiguration classes as follows:
Error
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Vender.Providers.AuthorizationServerProvider' can be invoked with the available services and parameters:
Cannot resolve parameter 'Vender.Services.ApplicationClientService appClientsService' of constructor 'Void .ctor(Vender.Services.ApplicationClientService, Vender.Data.Context.ApplicationUserManager)'.
Method With Exception (it gives the aforementioned error when it tries to resolve the two providers)
public static void ConfigTokenGeneration(IAppBuilder app, IContainer container)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = ApiSettings.AllowInsecureHttp,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(ApiSettings.AccessTokenValidTime),
Provider = container.Resolve<IOAuthAuthorizationServerProvider>(),
AccessTokenFormat = new JwtFormatProvider(ApiSettings.ApiAddress),
RefreshTokenProvider = container.Resolve<IAuthenticationTokenProvider>()
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
}
Class with Exception
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
private readonly ApplicationClientService _appClientsService;
private readonly ApplicationUserManager _userManager;
public AuthorizationServerProvider(ApplicationClientService appClientsService,
ApplicationUserManager userManager)
{
_appClientsService = appClientsService;
_userManager = userManager;
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrets once obtain access tokens.
context.Validated();
context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
var client = _appClientsService.FindApplicationClient(context.ClientId);
if (client == null)
{
context.SetError("invalid_clientId", $"Client '{context.ClientId}' is not registered in the system.");
return Task.FromResult<object>(null);
}
if (client.ApplicationType == ApplicationTypes.NativeConfidential)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
if (client.Secret != Hash.FromString(clientSecret))
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin") ?? "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
IDictionary<string, string> userData = new Dictionary<string, string>();
var user = await _userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "apiError.invalidUsernameOrPassword");
return;
}
var oAuthIdentity = await user.GenerateUserIdentityAsync(_userManager, "JWT");
var userProps = new AuthenticationProperties(userData);
var ticket = new AuthenticationTicket(oAuthIdentity, userProps);
context.Validated(ticket);
/*
// Get userManager to test credentials against
//
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
// find user by username first
ApplicationUser user = await userManager.FindByEmailAsync(context.UserName);
if (user != null)
{
var validCredentials = await userManager.FindAsync(context.UserName, context.Password);
if (await userManager.IsLockedOutAsync(user.Id))
{
context.SetError("invalid_grant", "This account has been locked out. Please come back later.");
return;
}
else if (await userManager.GetLockoutEnabledAsync(user.Id) && validCredentials == null)
{
// Record the failure which also may cause the user to be locked out
await userManager.AccessFailedAsync(user.Id);
string message;
if (await userManager.IsLockedOutAsync(user.Id))
{
message = "apiError.accountLockout";
}
else
{
int accessFailedCount = await userManager.GetAccessFailedCountAsync(user.Id);
int attemptsLeft = Convert.ToInt32(ConfigurationManager.AppSettings["MaxFailedAccessAttemptsBeforeLockout"].ToString()) - accessFailedCount;
message = string.Format("apiError.lockoutAttempts{0}", attemptsLeft);
}
context.SetError("invalid_grant", message);
return;
}
else if (validCredentials == null)
{
context.SetError("invalid_grant", "Invalid username or password, please try again.");
return;
}
else
{
if (!user.EmailConfirmed)
{
context.SetError("invalid_grant", "Email has not yet been confirmed.");
return;
}
user.LastLoginDateUtc = System.DateTime.UtcNow;
user.SuccessLoginCount++;
var loginInfo = new LoginTracker();
loginInfo.ApplicationUser = user;
loginInfo.LoginTimeUtc = DateTime.UtcNow;
}
}
else
{
context.SetError("invalid_grant", "Invalid username or password, please try again.");
return;
}
*/
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
}
AutoFac Builder
private static IContainer RegisterServices(ContainerBuilder builder)
{
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// EF HomeCinemaContext
builder.RegisterType < ApplicationDbContext>()
.As<DbContext>()
.InstancePerRequest();
builder.RegisterType<DbFactory>()
.As<IDbFactory>()
.InstancePerRequest();
builder.RegisterType<UnitOfWork>()
.As<IUnitOfWork>()
.InstancePerRequest();
builder.RegisterGeneric(typeof(EntityBaseRepository<>))
.As(typeof(IEntityBaseRepository<>))
.InstancePerRequest();
builder.RegisterType<ApplicationUserManager>()
.SingleInstance();
builder.RegisterType<AddressService>()
.As<IAddressService>()
.InstancePerRequest();
builder.RegisterType<ApplicationClientService>()
.As<IApplicationClientService>()
.InstancePerRequest();
builder.RegisterType<RefreshTokenService>()
.As<IRefreshTokenService>()
.InstancePerRequest();
builder.RegisterType<AuthorizationServerProvider>()
.AsImplementedInterfaces<IOAuthAuthorizationServerProvider, ConcreteReflectionActivatorData>().SingleInstance();
builder.RegisterType<RefreshTokenProvider>()
.AsImplementedInterfaces<IAuthenticationTokenProvider, ConcreteReflectionActivatorData>().SingleInstance();
Container = builder.Build();
return Container;
}
App Manager Class
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(ApplicationDbContext identityContext)
: base(new UserStore<ApplicationUser>(identityContext))
{
}
public ApplicationUserManager(IUserStore<ApplicationUser> store, IDataProtectionProvider dataProtectionProvider)
: base(store)
{
UserValidator = new UserValidator<ApplicationUser>(this)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
UserLockoutEnabledByDefault = false;
//DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
//MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
}
Application Client Service Class
public class ApplicationClientService : IApplicationClientService
{
private readonly IEntityBaseRepository<ApplicationClient> _appClientRepository;
private readonly IUnitOfWork _unitOfWork;
public ApplicationClientService(IEntityBaseRepository<ApplicationClient> appClientRepository,
IUnitOfWork unitOfWork)
{
_appClientRepository = appClientRepository;
_unitOfWork = unitOfWork;
}
public async Task<bool> CreateApplicationClientAsync(ApplicationClient newToken)
{
_appClientRepository.Add(newToken);
return await _unitOfWork.CommitAsync() > 0;
}
public Task<ApplicationClient> FindApplicationClientAsync(string clientId)
{
var refreshToken = _appClientRepository.GetAll().FirstOrDefault(x => x.ClientId == clientId);
return Task.FromResult(refreshToken);
}
public ApplicationClient FindApplicationClient(string clientId)
{
var refreshToken = _appClientRepository.GetAll().FirstOrDefault(x => x.ClientId == clientId);
return refreshToken;
}
}
Application Client Service Interface
public interface IApplicationClientService
{
Task<bool> CreateApplicationClientAsync(ApplicationClient newToken);
ApplicationClient FindApplicationClient(string clientId);
}
Now I've googled til my wits end and have not found anything particularly useful, and I was wondering what I need to do here to fix my interface/classes so they can be resolved when the oAuthServerOptions get instantiated.
i solve the issue use this implementation for AutoFac Builder you should make all single instance because the pre-request dependency error
var builder = new ContainerBuilder();
var config = GlobalConfiguration.Configuration;
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().SingleInstance();
builder.RegisterType<DbFactory>().As<IDbFactory>().SingleInstance();
builder.RegisterType<ApplicationDbContext>().AsSelf().SingleInstance();
// rest of code as singleInstance
builder.Register<IdentityFactoryOptions<ApplicationUserManager>>(c => new IdentityFactoryOptions<ApplicationUserManager>() { DataProtectionProvider = new DpapiDataProtectionProvider("Elhag.WebAPI") });
builder.RegisterType<ApplicationUserManager>().AsSelf().SingleInstance();
builder.Register(c=>new AuthorizationServerProvider(c.Resolve<ApplicationUserManager>())).AsImplementedInterfaces().SingleInstance();
builder.Register(c => new UserStore<ApplictionUser>(c.Resolve<ApplicationDbContext>())).AsImplementedInterfaces().SingleInstance();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();
IContainer container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
return container;
and you can resolve all dependency you need but don't user container directly according to autofac website you should use container lifeTimeScope so i will use container.BeginLifetimeScope().Resolve< your type >();
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = ApiSettings.AllowInsecureHttp,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(ApiSettings.AccessTokenValidTime),
Provider = container.BeginLifetimeScope().Resolve<IOAuthAuthorizationServerProvider>(),
AccessTokenFormat = new JwtFormatProvider(ApiSettings.ApiAddress),
RefreshTokenProvider = container.BeginLifetimeScope().Resolve<IAuthenticationTokenProvider>()
};
i test and work fine... hope it help