Here there is a solution by Scott Brady, you can read explanation there i just write code here with a bit of updates.
Solution:
Add a new property to your user to determine tenants.
public class ApplicationUser : IdentityUser {
public int TenantId { get; set; }
}
Then you need to customize your UserStore
to make it aware of your new user property.
public class ApplicationUserStore<TUser> : UserStore<TUser>
where TUser : ApplicationUser {
public ApplicationUserStore(DbContext context, int tenantId)
: base(context) {
TenantId = tenantId
}
public int TenantId { get; set; }
public override Task CreateAsync(TUser user) {
if (user == null) {
throw new ArgumentNullException("user");
}
user.TenantId = this.TenantId;
return base.CreateAsync(user);
}
public override Task<TUser> FindByEmailAsync(string email) {
return this.GetUserAggregateAsync(u => u.Email.ToUpper() == email.ToUpper()
&& u.TenantId == this.TenantId);
}
public override Task<TUser> FindByNameAsync(string userName) {
return this.GetUserAggregateAsync(u => u.UserName.ToUpper() == userName.ToUpper()
&& u.TenantId == this.TenantId);
}
public override Task<IdnUser> FindByIdAsync(long userId)
{
return this.GetUserAggregateAsync(u => u.Id == userId && u.TenantId == this.tenantId);
}
}
and also you need to customize IdentityDbContext
to support multitenancy.
public class ApplicationUserDbContext<TUser> : IdentityDbContext<TUser>
where TUser : ApplicationUser {
public ApplicationUserDbContext(string nameOrConnectionString)
: base(nameOrConnectionString) {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
var user = modelBuilder.Entity<TUser>();
user.Property(u => u.UserName)
.IsRequired()
.HasMaxLength(256)
.HasColumnAnnotation("Index", new IndexAnnotation(
new IndexAttribute("UserNameIndex") { IsUnique = true, Order = 1}));
user.Property(u => u.TenantId)
.IsRequired()
.HasColumnAnnotation("Index", new IndexAnnotation(
new IndexAttribute("UserNameIndex") { IsUnique = true, Order = 2 }));
}
protected override DbEntityValidationResult ValidateEntity(
DbEntityEntry entityEntry, IDictionary<object, object> items) {
if (entityEntry != null && entityEntry.State == EntityState.Added) {
var errors = new List<DbValidationError>();
var user = entityEntry.Entity as TUser;
if (user != null) {
if (this.Users.Any(u => string.Equals(u.UserName, user.UserName)
&& u.TenantId == user.TenantId)) {
errors.Add(new DbValidationError("User",
string.Format("Username {0} is already taken for AppId {1}",
user.UserName, user.TenantId)));
}
if (this.RequireUniqueEmail
&& this.Users.Any(u => string.Equals(u.Email, user.Email)
&& u.TenantId == user.TenantId)) {
errors.Add(new DbValidationError("User",
string.Format("Email Address {0} is already taken for AppId {1}",
user.UserName, user.TenantId)));
}
}
else {
var role = entityEntry.Entity as IdentityRole;
if (role != null && this.Roles.Any(r => string.Equals(r.Name, role.Name))) {
errors.Add(new DbValidationError("Role",
string.Format("Role {0} already exists", role.Name)));
}
}
if (errors.Any()) {
return new DbEntityValidationResult(entityEntry, errors);
}
}
return new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
}
}
And then you can create seprate user manager for each tenant like this:
User1:
public class AppCustomerManager : UserManager<ApplicationUser>
{
public AppCustomerManager(IUserStore<ApplicationUser> store)
: base(store)
{
UserTokenProvider = new PhoneNumberTokenProvider<ApplicationUser >();
}
public static AppCustomerManager Create(IdentityFactoryOptions<AppCustomerManager> options, IOwinContext context)
{
var appDbContext = context.Get<ApplicationUserDbContext>();
//here you define your tenant by passing an int to userstore
var appUserManager = new AppCustomerManager(new ApplicationUserStore(appDbContext, 1));
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
appUserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(6)
};
}
return appUserManager;
}
}
User2:
public class AppDeliveryManager : UserManager<ApplicationUser>
{
public AppDeliveryManager(IUserStore<ApplicationUser> store)
: base(store)
{
UserTokenProvider = new PhoneNumberTokenProvider<ApplicationUser >();
}
public static AppDeliveryManager Create(IdentityFactoryOptions<AppDeliveryManager> options, IOwinContext context)
{
var appDbContext = context.Get<IdnDbContext>();
//here you define your tenant by passing an int to userstore
var appUserManager = new AppDeliveryManager(new ApplicationUserStore(appDbContext, 2));
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
appUserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(6)
};
}
return appUserManager;
}
}
Update:
var appUserManager = new AppDeliveryManager(new ApplicationUserStore(appDbContext, tenantId));
and tenantId can be the Id of each tenant that signed up.