Finbuckle / Finbuckle.MultiTenant

Finbuckle.MultiTenant is an open-source multitenancy middleware library for .NET. It enables tenant resolution, per-tenant app behavior, and per-tenant data isolation.
https://www.finbuckle.com/multitenant
Apache License 2.0
1.3k stars 265 forks source link

Schema per Tenant isolation #104

Closed chernihiv closed 4 years ago

chernihiv commented 5 years ago

I have noticed that implementation of 'IAuthenticationSchemeProvider' shares the same schema across all tenants. It would be terrific to have dynamic schemas (e.i. external providers) configured separately for each tenants (on runtime).

May you say if it is on roadmap?

chernihiv commented 5 years ago

I would like to inherit from MultiTenantAuthenticationSchemeProvider and override standard behavior, but it's internal. As far as I understand, it's easy to change making _schemes per tenant specific. Are there any pitfalls?

AndrewTriesToCode commented 5 years ago

Hi @chernihiv, it should already be be pulling the schemes per tenant--that is why I made that class. I just wrote a unit test to confirm it works and merged into master.

If you want each tenant to have a different DefaultScheme, ChallendgeScheme, etc per tenant:

  1. Call WithRemoteAuthentication after AddMultiTenant (this registers the `IAuthenticationSchemeProvider).
  2. Call WithPerTenantOptions<AuthenticationOptions>(...) after AddMultiTenant and set the AuthenticationOptions how you want them. It might look like this:
...WithPerTenantOptions<AuthenticationOptions>((options, tenantInfo) =>
{
     // Set scheme for the tenant from Items collection.
    options.ChallengeScheme = tenantInfo.Items["challengeScheme"];
});

Internally MultiTenantAuthenticationSchemeProvider is using an IOptions<AuthenticationOptions> and each time it gets the value of the options it is per-tenant because the interface implementation internally uses MultiTenantOptionsCache (due to the WithPerTenantOptions call earlier).

If you are not seeing this behavior please send me a sample and I can take a look at it.

chernihiv commented 5 years ago

Hi @achandlerwhite It could be my fault, I don't know how Authentication and Finbuckle.MultiTenant work under hood well. However, I try to add different external providers for different tenants. Let it be: A, B, C tenants. I use AuthenticationSchemeProvider.AddScheme(..) to add new schema on demand. Configurations could be changed anytime runtime for any tenant. E.g.

Having configured schemas for A and B tenants as described above, AuthenticationSchemeProvider.GetAllSchemesAsync() returns 3 schema for each of tenants, even for C.

chernihiv commented 5 years ago

AFAIK, WithPerTenantOptions<AuthenticationOptions> allows to override configuration of DefaultScheme, DefaultAuthenticateScheme, DefaultSignInScheme, and so on per tenant. But in my case I need to configure list of Schemes (e.g. Facebook, Google, etc..) per Tenant.

AndrewTriesToCode commented 5 years ago

Hi @chernihiv --sorry for the slow reply.

You could add a different scheme for each tenant, but with Finbuckle I went with an options customization approach instead because I didn't want to have multiple schemes that were essentially the same scheme.

I would recommend that you add a single scheme for each type of provider in your ConfigureServices. Each type also has its own options such as OpenIdConnectOptions, FacebookOptions or GoogleOptions and you can customize each of these per-tenant.

That being said, usually the website that is forwarding them to authentication externally will have its own registered Client ID and Client Secret that would not change per tenant--so I'm not sure how useful customizing these options would be. The exception is OpenIdConnectOptions or OAuthOptions which a tenant might want to change to point to their own server.

AndrewTriesToCode commented 5 years ago

I want to add more to directly address your question-- if you want to explicitly control which authentication methods are available to a tenant you will need to store which schemes are applicable somewhere--maybe add a List<string> to the tenantInfo Items collection for each tenant so it knows which authentication options to show the user? You would then tell ASP.NET Core to challenge with the appropriate scheme, HttpContext.ChallengeAsync("tenantScheme1") or similar.

And to be clear--you could definitely use your approach of separate schemes registered for each tenant. Finbuckle won't be of much help there though--you'll have to register completely separate Options for each scheme per tenant.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.