I have implemented a custom RoleStore and a custom UserStore for my project that is using ASP.NET 5, MVC 6, EF 7, and Identity 3. However - I can't quite figure out how to configure identity to use my custom RoleStore and custom UserStore instead of the usual offering. How can I reconfigure the system to use my custom classes?
PS: I also have custom User and Role class.
Solution
Here's what I ended up doing. First, I uninstalled the 'Identity Entity Framework' package from my project. This sent a few things missing, so I re-implemented them (read: copied them from here), and put them in a 'Standard' namespace to indicate they hadn't been customised. I now have a 'Security' namespace that contains the following:
Standard
IdentityRole.cs
IdentityRoleClaim.cs
IdentityUser.cs
IdentityUserClaim.cs
IdentityUserLogin.cs
IdentityUserRole.cs
BuilderExtensions.cs
IdentityDbContext.cs
Resources.resx
Role.cs
RoleStore.cs
User.cs
UserStore.cs
The items shown in bold contain project specific functionality.
The code that allows me to use the custom stores is in the 'BuilderExtensions' file, which contains the following class:
public static class BuilderExtensions
{
public static IdentityBuilder AddCustomStores<TContext, TKey>(this IdentityBuilder builder)
where TContext : DbContext
where TKey : IEquatable<TKey>
{
builder.Services.TryAdd(GetDefaultServices(builder.UserType, builder.RoleType, typeof(TContext), typeof(TKey)));
return builder;
}
private static IServiceCollection GetDefaultServices(Type userType, Type roleType, Type contextType, Type keyType)
{
var userStoreType = typeof(UserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType);
var roleStoreType = typeof(RoleStore<,,>).MakeGenericType(roleType, contextType, keyType);
var services = new ServiceCollection();
services.AddScoped(
typeof(IUserStore<>).MakeGenericType(userType),
userStoreType);
services.AddScoped(
typeof(IRoleStore<>).MakeGenericType(roleType),
roleStoreType);
return services;
}
}
This then allows me to write the following in my Startup.cs file:
services.AddIdentity<User, Role>()
.AddCustomStores<PrimaryContext, string>()
.AddDefaultTokenProviders();
And the custom store will be used. Note that PrimaryContext is the name of my whole-project DbContext. it inherits from IdentityDbContext.
Discussion
I could have probably kept the 'Identity Entity Framework' package and saved myself duplicating the contents of the 'Standard' namespace, but I chose not to so that I can keep my identifiers short and unambiguous.
Check out this section
Reconfigure application to use new storage provider in Overview of Custom Storage Providers for ASP.NET Identity
Specifically "If the default storage provider was included in your project, you must remove the default provider and replace it with your provider."
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new YourNewUserStore(context.Get<ExampleStorageContext>()));
...
}
Related
I want to create a migration for existing entities.
I have a DataContext class
public class DataContext : DbContext
{
public DataContext()
{
}
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
public DbSet<AppUser> Users { get; set; }
}
And I added it to the services in IServiceCollection extension method -
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<DataContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly(typeof(DataContext).Assembly.FullName));
});
return services;
}
}
When I try to add a migration from Entity Framework command tool -
"dotnet ef migrations add InitialCreate" such error occurs:
No database provider has been configured for this DbContext. A
provider can be configured by overriding the 'DbContext.OnConfiguring'
method or by using 'AddDbContext' on the application service provider.
If 'AddDbContext' is used, then also ensure that your DbContext type
accepts a DbContextOptions object in its constructor and
passes it to the base constructor for DbContext.
Can't really understand what's wrong with my code. It actually works by overriding OnConfiguring method but I need a solution for this kind of approach. Any ideas?
Try to remove the default public constructor (the parameterless one).
When you execute the CLI command you have some additional options like --project and --startup-project. By specifying them it will enable the use of the host builder to create your DB context, and you don't need this parameterless constructor.
The ef cli is using reflection to look into your project and then executes the host builder setup in order to have a service builder. From there it will instantiate the DB context. If it doesn't work. It will fall back first to a design time factory. And finally will use the default constructor (which is happening in this case).
I am using EF Core Code first and I have an issue when using multiple DB providers (SQL Server and MySql).
Even when I choose to use MySql DB provider, SQL server migration files are used.
Check the sample project
In my case, I use the same migration for both providers but sometimes I need to do some changes to migration files manually. for example added annotation for both providers (or change the type of some fields like varchar to nvarchar).
Id = table.Column<int>(nullable: false)
.Annotation("MySql:ValueGeneratedOnAdd", true)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
For me it's work fine
Writing provider-specific migrations, database initializers, and services with Entity Framework core can be a difficult task. Use AdaptiveClient to greatly simplify the process. AdaptiveClient is a utility that works with Autofac for provisioning a service layer against multiple database providers or transports. AdaptiveClient.EntityFrameworkCore is an add-on that includes utilities for working with Entity Framework Core. In a nutshell here is how it AdaptiveClient works:
IMigrationContext is a placeholder interface that allows you to associate your DbContext with a specific provider (MSSQL, MySql, etc) for the purpose of creating a migration.
IDbContextOptions is a placeholder interface that allows you to associate an implementation of DbContextOptions that is specific to your provider.
RegistrationHelper is a utility that simplifies registering your components with Autofac. RegisterMigrationContext is a method you can call to easily register your provider-specific migration context.
To create provider-specific migrations you create a class for each database provider you want to target. These classes derive from your DbContext and implement IMigrationContext (which has no members):
public class MyDbContext_MSSQL : MyDbContext, IMigrationContext
{
public MyDbContext_MSSQL(DbContextOptions options) : base(options)
{
}
}
public class MyDbContext_MySQL : MyDbContext, IMigrationContext
{
public MyDbContext_MySQL(DbContextOptions options) : base(options)
{
}
}
The examples above are complete - you do not have to write any additional code. You do not need to create a separate DbContext for each provider (unless you wish to do so). The reason you need to create a class for each provider is because EF reflects on your assembly to find the correct DbContext when you run dotnet ef migrations add....
Create classes that wrap DbContextOptions and implement IDbContextOptions:
public class DbContextOptions_MSSQL : IDbContextOptions
{
public DbContextOptions Options { get; set; }
public DbContextOptions_MSSQL(string connectionString)
{
DbContextOptionsBuilder builder = new DbContextOptionsBuilder();
builder.UseSqlServer(connectionString);
Options = builder.Options;
}
}
public class DbContextOptions_MySQL : IDbContextOptions
{
public DbContextOptions Options { get; set; }
public DbContextOptions_MySQL(string connectionString)
{
DbContextOptionsBuilder builder = new DbContextOptionsBuilder();
builder.UseMySql(connectionString);
Options = builder.Options;
}
}
Use the AdaptiveClient RegistrationHelper to register your classes with Autofac:
registrationHelper.RegisterMigrationContext<Database.Db_MSSQL>(API_Name.MyAPI, DataBaseProviderName.MSSQL);
registrationHelper.RegisterMigrationContext<Database.Db_MySQL>(API_Name.MyAPI, DataBaseProviderName.MySQL);
registrationHelper.RegisterDbContextOptions<DbContextOptions_MSSQL>(DataBaseProviderName.MSSQL);
registrationHelper.RegisterDbContextOptions<DbContextOptions_MySQL>(DataBaseProviderName.MySQL);
In the code above API_Name is just a constant that resolves to a simple string like "MyApplicationName". Same with DataBaseProviderName.MSSQL and .MySQL. They are string constants that resolve to "MSSQL" or "MySQL".
Now, here is the most important part: Just as you register the components of your application using keys such as "MSSQL" or "MySQL", you also register the connection strings for your application using those same constants.
This allows Autofac to resolve the correct provider-specific or transport-specific components based on nothing more than the connection string currently in use for application. You can read up on the entire process here.
You can see the complete working example in the Zamagon Demo. The demo illustrates migrations, database initializers, and drop-and-recreate scenarios for integration testing.
I am trying to build a library that has core and extensions packages like Entity Framework and its database providers.
What I am trying to do is when I register that library with dependency injection, I want to give specific implementation as a parameter.
Think EF. In order to use sql provider on EF we need to register it with SQL provider passed as option parameter like the following.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"]);
});
I would like to build similar structure. Lets say my framework will provide film producer. It will have producer.core package for framework related classes and two extensions package called Producer.Extensions.Hollywood and Producer.Extensions.Bollywood.
If I want to use Hollywood provider, I need to install core package and Hollywood extension package. On registration it should look like
services.AddFilmProducer(options =>
{
options.UseHollywoodProducer();
});
I could not find even a keyword that will point me a direction. I tried to read entity framework's source code but it is too complicated for my case.
Is there anyone who could point me a direction?
Thanks in advance.
I'm not sure if I completely understand your requirements, but DI and extensions are an easy thing in .net core.
Let's say you want this in your Startup.cs
services.AddFilmProducer(options =>
{
options.UseHollywoodProducer();
});
To implements this, create your library and add a static extension class
public static class FilmProducerServiceExtensions
{
public static IServiceCollection AddFilmProducer(this IServiceCollection services, Action<ProducerOptions> options)
{
// Create your delegate
var producerOptions = new ProducerOptions();
options(producerOptions);
// Do additional service initialization
return services;
}
}
where your ProducerOptions implementation might look like
public class ProducerOptions
{
public void UseHollywoodProducer()
{
// Initialize hollywood
}
public void UseBollywoodProducer()
{
// Initialize bollywood
}
}
If you wish to use the passed ProducerOptions in your service, there are two ways to do it. Either use dependency injection again, or directly access the service by using service provider in your extension method
var serviceProvider = services.BuildServiceProvider()
IYourService service = sp.GetService<IYourService>();
And now you have the original Use piece of initialization working.
Hope it helps.
Edit:
To clarify. To inject your options in the service, you can use
services.Configure(ProducerOptions);
in your extension method, and pass to your service constructor via
public YourService(IOptions<ProducerOptions>)
You can then simplify or complicate your options as much as you want.
A useful link for this kind of extensions might be the CORS repository for .net core: https://github.com/aspnet/CORS
Edit after comments:
I think I've got it now. You want packages to extend and implement specific options, kind of like what serilog does with different sinks. Piece of cake.
Scrap the ProducerOptions implementation.
Lets say you have a base package with initial empty structures (BaseProducer library) and an interface
public interface IProducerOptions
{
// base method signatures
}
Your service extension now becomes
public static class FilmProducerServiceExtensions
{
public static IServiceCollection AddFilmProducer(this IServiceCollection services, Action<IProducerOptions> options)
{
// Do additional service initialization
return services;
}
}
Now you create a new package with specific "Hollywood producer" options and you want to extend the base option set
public static class HollyWoodExtensions
{
public static void UseHollywoodProducer(this IProducerOptions options)
{
// Add implementation
}
}
Create as many packages and IProducerOptions extensions as you like, and the added methods will start appearing in your Startup.cs
services.AddFilmProducer(options =>
{
options.UseHollywoodProducer();
});
I am trying to seed some roles to my identity db context, when I initially create the database.
For that I tried to implement the code like it was stated here:
https://stackoverflow.com/a/29547994/985798
I tried this in the ConfigureServices-method inside my Startup-class:
public void ConfigureServices(IServiceCollection services)
using this snippet:
var rolestore =
new Microsoft.AspNetCore.Identity.EntityFrameworkCore.
RoleStore<Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole>(identityContext);
var roleManager = new Microsoft.AspNetCore.Identity.RoleManager
<Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole>(rolestore);
//use rolemanager to insert roles etc.
The problem is, that it seems to not work in an fresh Core-project, created with Visual Studio 2017.
It gives me the following build error:
CS7036 There is no argument given that corresponds to the required
formal parameter 'roleValidators' of
'RoleManager.RoleManager(IRoleStore,
IEnumerable>, ILookupNormalizer,
IdentityErrorDescriber, ILogger>,
IHttpContextAccessor)'
Even if I use the other overload (with null values for the other parameters), the RoleManager seems to have no "Create" method anymore.
So, I am stuck at this point. What do I need to do? Has something changed in the ASP.NET MVC Core implementation of the rolemanager? Do I use something wrong?
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
public static class Seed
{
public static void Initialize(IServiceProvider provider)
{
var _context = provider.GetRequiredService<ApplicationDbContext>();
var userManager = provider.GetRequiredService<UserManager<ApplicationUser>>();
var roleManager = provider.GetRequiredService<RoleManager<IdentityRole>>();
}
}
Then below the majority of the code in the in the Configure() in startup.cs
Seed.Initialize(app.ApplicationServices);
HTH (hope that helps).
I am having problems creating a custom UserStore using dependency injection when creating an ApplicationUserManager using the OWIN request pipeline.
Background
I am trying to migrate the user functionality in our web application from using the SimpleMembership to the new ASP.NET Identity. When starting a new MVC 5 project, the default implementation of the single page application uses ASP.Identity, using Entity Framework to implement the UserStore functionality.
In my case, we are already using NHibernate as the ORM, and using ninject to implement the unit of work pattern so that we had one NHibernate session per request, and I wanted to make the ASP.Identity work with our existing framework.
To this end, I created a custom UserStore, which could be created by injecting the relevant repositories/nhibernate session, etc. This could then be injected into the Controller's constructor using Ninject, rather than using the default implementation's GetOwinContext functionality.
In order to do this, I had commented out the following line in the ConfigureAuth(IAppBuilder app) method of the Startup, which by default creates the UserManager class:
// app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Instead, I used the NinjectWebCommon created when installing the Ninject.Web.Common.Webhost nuget package to create the relevant bindings.
This implementation worked fine with some of the UserManager operations, but with some operations, such as ResetPasswordAsync, it fails because the default ApplicationUserManager implementation is not called, and so the UserTokenProvider in the UserManager class is never set:
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// 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 in here.
manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is: {0}"
});
manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is: {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
Therefore, the UserTokenProvider is not set.
Problem
I want to use the OWIN pipeline, because Visual Studio's default implementation of the ApplicationUserManager class injects the IDataProtectionProvider in its Create callback method. However, I also want to create my UserStore using dependency Injection, and I do not know how to create a UserStore within this method using dependency injection.
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
// WANT TO CREATE THE USER STORE USING NINJECT DEPENDENCY INJECTION HERE
// var userStore = ...
var manager = new ApplicationUserManager(userStore);
}
I have tried to get around this limitation by using the Ninject.Web.Common.OwinHost nuget package and creating the kernel within the Startup class.
public void ConfigureAuth(IAppBuilder app)
{
// Setup
app.UseNinjectMiddleware(CreateKernel);
}
However, the Ninject.Web.Common.OwinHost does not expose its Kernel, so I am unable to use service location pattern to inject the values into my custom UserStore in the Create callback.
I have also tried to create a singleton Kernel, and register this using app.CreatePerOwinContext(CreateKernel) with the relevant delegate, so I could later access the Kernel, but when I call context.Get() it just returns null.
Question
How can I register a callback function with CreatePerOwinContext to create a custom UserManager which uses a custom UserStore, and then use Ninject to create the custom UserStore using dependency injection in the Create callback, so that I also have access to the IdentityFactoryOptions which Owin uses to inject the user token provider?
For info:
It is possible to register the kernel as a singleton so that the same kernel can be used by the ninject middleware and also registered within the owin context.
public static StandardKernel CreateKernel()
{
if (_kernel == null)
{
_kernel = new StandardKernel();
_kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
_kernel.Load(Assembly.GetExecutingAssembly(), Assembly.Load("Super.CompositionRoot"));
}
return _kernel;
}
The callback function app.CreatePerOwinContext(ApplicationUserManager.Create), will call the ApplicationUserManager.Create rather than register it to be called at a later point during the setup. Therefore, the CreateKernel function needs to be registered before the ApplicationUserManager's Create callback or you will get a null reference exception if you try to get the kernel from the owin context within that method.
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(CreateKernel);
app.UseNinjectMiddleware(CreateKernel);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
This will allow you to access the kernel to create a custom UserStore within the ApplicationUserManager's Create callback:
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var kernel = context.Get<StandardKernel>();
var userStore = kernel.Get<IUserStore<User, int>>();
var manager = new ApplicationUserManager(userStore);
//...
}
I know that in general dependency injection should be favoured over service location, but in this context I couldn't see a way around it - unless anybody has any better suggestions?
This will allow you to use Ninject to implement the unit of work patter leveraging Ninject's InRequestScope().OnDeactivation functionality. I'm aware that the UserManager class has a per request lifetime, but didn't know the most the most appropriate way to commit any outstanding transactions on request finish.
Note This was for WebApi (using System.Web.Http)
Okay so I kind of cheated by using stuff from System.Web which is the namespace we're suppose to be weening ourselves off of, but while its still used, why not.
Firstly, I use some helpers from this SO question:
Configuring Ninject with Asp.Net MVC & Web Api
Within which, the resolver is registered with System.Web's global configuration. Thus, I just go grab it when I need it:
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var repository = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver
.GetService(typeof(Data.Repositories.UserRepository)) as Data.Repositories.UserRepository;
var manager = new ApplicationUserManager(repository);
...
Note: I use the term Repository over Store since it matches the well-known pattern, more understandable to most people.
And the Startup.Auth looks like this, I basically move the Ninject init into here so its done in time:
public void ConfigureAuth(IAppBuilder app)
{
// Dependency Injection
Evoq.AppName.Configuration.Ninject.NinjectHttpContainer.RegisterAssembly();
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
...
I did also use a method similar to the OP where I 'attached' a callback to get the IKernel but while this keeps it all OWINy, the problem with this approach is that you have to call owinContextThing.Get<IKernel>() which means referencing Ninject deeper in my code.
There were ways around it, but it started getting more complex than my solution above.
Additional Note
This is the Identity Framework code that registers the callback. Note the call to app.GetDataProtectionProvider which is essentially the thing we originally needed to make a UserTokenProvider.
/// <summary>
/// Registers a callback that will be invoked to create an instance of type T that will be stored in the OwinContext which can fetched via context.Get
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="app"></param>
/// <param name="createCallback"></param>
/// <returns></returns>
public static IAppBuilder CreatePerOwinContext<T>(this IAppBuilder app, Func<IdentityFactoryOptions<T>, IOwinContext, T> createCallback) where T : class,IDisposable {
if (app == null) {
throw new ArgumentNullException("app");
}
if (createCallback == null) {
throw new ArgumentNullException("createCallback");
}
app.Use(typeof(IdentityFactoryMiddleware<T, IdentityFactoryOptions<T>>),
new IdentityFactoryOptions<T>() {
DataProtectionProvider = app.GetDataProtectionProvider(),
Provider = new IdentityFactoryProvider<T>() {
OnCreate = createCallback
}
});
return app;
}
I've looked and looked and reflected the libs and cannot find that method! If I knew how that worked, potentially we could find another way to create a token, i.e. wouldn't need the options instance.