DuendeSoftware / IdentityServer

The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core
https://duendesoftware.com/products/identityserver
Other
1.49k stars 350 forks source link

Simplify Multi-tenancy #31

Open leastprivilege opened 3 years ago

leastprivilege commented 3 years ago

While everything is already doable with various extensibility points etc - it would be nice to have this as a 1st class citizen

per tenant

ghstahl commented 3 years ago

I have a multi-tenancy token service built on top of IDS. I rely on Scoped services to lock me into the tenant and the current tenant's client. For my client config I have extra stuff over what IDS's Client has. One of those is

        public string Issuer { get; set; }

For my use-case I wanted the Issuer to be configurable at the Client level.

To get it to work I had to make my own;

public class MyDefaultTokenService : DefaultTokenService
{
        public override async Task<Token> CreateAccessTokenAsync(TokenCreationRequest request)
        {
            ...
            var client = request.ValidatedRequest.Client as ClientExtra;
            var issuer = client.Issuer;

            if (string.IsNullOrWhiteSpace(issuer))
            {
                issuer = ContextAccessor.HttpContext.GetIdentityServerIssuerUri();
            }
            ...
        }
}

The more I think about it, an IIssuerProvider would be more suitable for DefaultTokenService to have and then it allows extensibility anywhere in the Scope.

When we think about this can we make sure that Issuer, and things like it can come from any level of the configuration? Per Tenant, Per Client, etc.

natelaff commented 3 years ago

Do you want feedback on this, or do you pretty much have your plan in place already?

leastprivilege commented 3 years ago

We haven't planned anything yet. So -yes ;)

maulik-modi commented 3 years ago

@leastprivilege and @brockallen , Nowadays, lot of projects use this package https://www.finbuckle.com/MultiTenant/Docs/Authentication

natelaff commented 3 years ago

Yes, Finbuckle is great, and I use (and contribute to) it. I do not think there is any need to replicate features from that package, but to make it easier in IS for packages like that to work harmoniously.

There really isn't much that needs to happen on IS from my experience other than guidance. I know the argument has been "there are a bunch of ways to do multi tenancy" and I suppose that's true to a degree. But just getting the guidance up to the point of here are two simple paths you could take, arc values or Uri, and show them in the context of even the simplest use case of database per tenant.

If you wanted to extend it, some sugar methods/options such as the ability to make things tenant aware would be helpful. things like the ability to modify routing based on if/where a tenant name would be in the address and options such as whether to include the tenant's name as part of the issuer if it's in the path.

But again, if there is guidance there, even the above isn't necessary.

leastprivilege commented 3 years ago

Thanks! Looks interesting - we have some other ideas as well. But will definitely have a closer look and see how it aligns with our plans.

maulik-modi commented 3 years ago

@natelaff , Per Tenant discovery endpoint? Per Tenant signing credential? Per Tenant Domain cookie? are some design choices.

natelaff commented 3 years ago

Yes the discovery endpoint I was considering as part of routing. In IS4 I currently implement my own IEndpointRouter to use the tenant.

The cookie is easy enough to accomplish with Finbuckle. The per tenant signing would be interesting though not something I have a requirement for.

maulik-modi commented 3 years ago

@natelaff , Multiple issuers are part of Enterprise edition which is the most expensive option.

dgioulakis commented 3 years ago

I've been able to achieve much of this just by using Autofac's Multitenancy DI. https://github.com/autofac/Autofac.AspNetCore.Multitenant

Just an idea for whomever is looking for this capability now.

brockallen commented 3 years ago

Related: https://github.com/DuendeSoftware/IdentityServer/issues/344

brockallen commented 3 years ago

To review: https://github.com/Finbuckle/Finbuckle.MultiTenant/issues/374

maulik-modi commented 2 years ago

@Cephei , How many of these were you able to implement?

dgioulakis commented 2 years ago

@maulik-modi @tmeers Hey, sorry for the delay; just got back to work today.

My needs for multi-tenancy are most likely unique and different to how Duende should implement multi-tenancy. Duende would likely be more interested in creating a pure multi-tenant solution like what you're describing. This would be similar to how Auth0, Okta, Ping, etc work.

I'll try to be succinct in describing my requirements for multi-tenancy. My company was acquired by a larger org within the past year. Since then, we've acquired four other companies & product suites. So it's been my task to create a new identity foundation and unify all products that have disparate user stores, hostnames, APIs. The best example I can provide that everyone here can probably relate-to is how Google & Youtube work. e.g. When you login to Google or gmail, you are automatically logged into youtube.com as well. And vice-versa.

So for us, when you login to Product 1/domain 1, you seamlessly login to all products N in our ecosystem - across domain boundaries. There is no SSO mechanism for this kind of functionality that browsers natively support - mainly due to browser cookie security policies. Hence the reason Google is pushing for browsers to adopt & implement First-Party-Sets. see: https://developer.chrome.com/docs/privacy-sandbox/first-party-sets/

Therefore, when I wrote earlier in this thread of multi-tenancy, I should have been more clear in my intent. Our IdentityServer solution is only administered by our organization and is not being used to create a true multi-tenant identity platform for un-related business entities. Each "tenant" for us, is owned by our parent organization and we likely want to share some of IdSrv's configuration between tenants.

To circle back to and answer your original question, we simply don't have a need for all your check boxes to be checked to meet our requirements for multi-tenancy - just a few, however, the project is still underway and I expect to refine the solution and possibly add more functionality you described.

Required/Implemented

Desired; Plan-to-Implement

Not Implemented/No Plans To

Other Items

So this is quite different than what most people here may be interested in when talking about adding multi-tenancy to Duende.

Unfortunately, I think to turn IdentityServer into a truly multi-tenant identity solution it may need to find a way to completely host all ASP.Net services (IServiceCollection) in a multi-tenant DI fashion. The only way I've seen cleanly to do this is using Autofac's multi-tenancy support (see https://autofac.readthedocs.io/en/latest/integration/aspnetcore.html#using-a-child-scope-as-a-root), however, that means hosting multiple Kestrels on different ports. I've yet to see a clean solution for hosting ASP.NET on a single port with all IServiceCollection being resolved with some kind of tenant-registration & resolver.

I've only briefly looked at Finbuckle, but perhaps it may help in some regard, though I would prefer to see Duende rollout their own implementation instead of an additional external lib dependency. It looks like Finbuckle supports loading tenants dynamically from a DB, but I think it would be necessary to plug-in IdSrv's DynamicProviders Per-Finbuckle-Tenant.

VictorioBerra commented 2 years ago

Just would like to add, with Duende's Identity Provider store, and Managed Keys store, that might check a couple boxes when using something like Finbuckle per-tenant databases. I have not tried yet but with more of Duende's config existing in the DB, and more configuration in the DB, the cleaner the path is.

chihabhajji commented 2 years ago

i dont see a need to customize IS4 to include per tenant dicovery point as it can be achieved by injecting a query or header param depending on your Saaskit or finbuckle multitenant strategy, as for the authentication it can be achieved through the per tenantoption , but i do see a need for documentation on how to integrate them

b1tzer0 commented 2 years ago

Wanted to check in to see if this is still targeting the 7.0 release? We are starting brand new development with IS and need to implement Multi-Tenant (Clients, User Stores, Scopes, etc). Honestly, at this point I don't know what all the requirements are because those above me don't know what they need. The main thing I know is that Users must have their own user store.

brockallen commented 2 years ago

The main thing I know is that Users must have their own user store.

This one is the easy one, and since that's mainly your code it's not something IdentityServer needs to do much about.

need to implement Multi-Tenant (Clients, User Stores, Scopes, etc).

If you really find you have this requirement, let us know please. Most people don't, at least in my experience.

brockallen commented 2 years ago

For reference: https://github.com/DuendeSoftware/Support/issues/147

martinb69 commented 2 years ago

After having read several treads, I could not find a solution for the PersistedGrantDbContext operational store. I noticed that the database is required during app.UseIdentityserver() and at this point there is no HttpContext and no valid tenant. Is there a a possibility to have a per-tenant operational store or are there plans to support this?