2

OK, this is going to be quite a big and open question(s)

I want to extend Identity Server 4 and Microsoft Identity to be used for multi-tenancy applications.

This is what I've done so far...

Extended the Identity Role, in this example, I've added a description property

public class CustomRole : IdentityRole
{
    public CustomRole(string roleName, string description) : base(roleName)
    {
        Description = description;
    }

    public string Description { get; set; }
}

Extended the Identity User

public class CustomUser : IdentityUser
{
    public string FirstName { get; set; }

    public string Surname { get; set; }

    public int TenantId { get; set; }
}

I then have a Tenant entity

public class Tenant
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string TenantCode { get; set; }

    ... plus extra fields
}

Extended the SignInManager class to use a new tenant lookup service

public class ApplicationSignInManager<TUser> : SignInManager<TUser> where TUser : CustomUser
{
    private readonly UserManager<TUser> userManager;
    private readonly ITenantService tenantService;

    public ApplicationSignInManager(UserManager<TUser> userManager,
                                    IHttpContextAccessor contextAccessor,
                                    IUserClaimsPrincipalFactory<TUser> claimsFactory,
                                    IOptions<IdentityOptions> optionsAccessor,
                                    ILogger<SignInManager<TUser>> logger,
                                    IAuthenticationSchemeProvider schemes,
                                    IUserConfirmation<TUser> confirmation,
                                    ITenantService tenantService) :
        base(userManager,
             contextAccessor,
             claimsFactory,
             optionsAccessor,
             logger,
             schemes,
             confirmation)
    {
        this.userManager = userManager ?? throw new ArgumentNullException(nameof(userManager));
        this.tenantService = tenantService ?? throw new ArgumentNullException(nameof(tenantService));
    }

    public async Task<SignInResult> PasswordSignInAsync(string tenantCode, string userName, string password, bool isPersistent, bool lockoutOnFailure)
    {
        var tenant = await tenantService.GetByCodeAsync(tenantCode);

        if (tenant.HasNoValue)
            return SignInResult.Failed;

        var user = await userManager.FindByNameAsync(userName);

        if (user == null)
            return SignInResult.Failed;

        if (user.TenantId == tenant.Value.Id)
            return await PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure);
        else
            return SignInResult.Failed;
    }
}

I then register for the new classes in the program class

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity<CustomUser, CustomRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager<ApplicationSignInManager<CustomUser>>()
    .AddDefaultTokenProviders();

Now, this all works fine, but...

Here are my questions:

  1. How can this work with different applications that are multi-tenancy? e.g. What if application 1 has a tenant record like above but application 2 needs to have an address or some other option saved against the tenant?

  2. Similar question with the users. What if application 2 needs to store an age or old address value for example against the user?

  3. What about when the delegation of creating the users is up to the application itself? i.e. One of the tenants wants to create users for access to their part of the system

  4. With the above questions, should the Identity Server be split into one instance per application to allow for different options, or should different tables/databases be created for each application? e.g. Application 1 has its own tenant and user table. I'm not sure which way to go. Or is there a much better approach?

I've searched the internet for examples and I only find very similar ways to what I've already done. Any help or examples would be greatly appreciated.

Thanks

Sun
  • 4,458
  • 14
  • 66
  • 108

1 Answers1

0

IdentityServer does not care that much about users or how they are stored and the picture below shows how IdentityServer is related to the user database

enter image description here

I would create one instance of IdentityServer and then based on the client authenticating, I would either keep a separate database for each client/tenant or one shared database if the user data is similar.

what the database looks like is not that important to IdentityServer. All it wants from the user database is a set of claims.

It is much simpler if all the clients and API's that rely on IdentityServer only have one IS to trust and depend on.

Tore Nestenius
  • 16,431
  • 5
  • 30
  • 40