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.31k stars 267 forks source link

Use of claim strategy and Identity types #613

Open devalot76 opened 1 year ago

devalot76 commented 1 year ago

Hi, I'm struggling with adding multi-tenancy in my ASP.NET 6 MVC app. I use standard Identity and cookie based authentication. I have a separate DbContext for managing the tenants wich derives from EFCoreStoreDbContext<MyTenantInfo>

This is how I configured Finbuckle:

builder.Services.AddMultiTenant<MyTenantInfo>()
    .WithEFCoreStore<MultiTenantStoreDbContext, MyTenantInfo>()
    .WithClaimStrategy()
    .WithPerTenantAuthentication();

I want to use just the claim to resolve my tenant, no route or host whatsever. I understand that, in this scenario, the tenant isn't known until after the user has logged in, so, after several attempts, the only way I found to make this works is to derive my ApplicationDbContext from MultiTenantIdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>, as per documentation, in order to avoid having the tenant filter applied on Identity types because this way they are not configured as multitenant. Now, I wonder if this is the correct way of doing it, if that can be a security risk here and if there is a better (proper) way. Thank you.

AndrewTriesToCode commented 1 year ago

hi @devalot76

Question for you. Suppose a user is able to log in from a tenant-neutral endpoint. Based solely on their login form (username/password) how would you know which tenant they belonged to?

One option is to actually have your Identity dbcontext not be multitenant and have all users be the same from Identity's perspective, then you can add user claims to each user to capture which tenant (or tenants in a many-to-many situation) they belong to. This is a more advanced use case that the fully isolated approach in the docs and sample projects. It still raises the same question when they register though -- which tenant are they a part of when they register?

HDScarpe commented 1 year ago

@AndrewTriesToCode Hi. I'm also approaching this way at the moment. I want to use just the claim to resolve my tenant without having to use tenant in url slug. when I login to the form then I will regex the user email to identify the tenant ( eg : user1@root , user2@tenant1 ) and then I succeeded to Authenticator. However, when adding swagger to the identityserver duende (Implement flow), when redirecting to the client, it cannot identify the tenant. I want when the authen is successful and there is jwt, how can it save the previously defined tenant and when redirecting the client url, it can get the previously saved tenant

HDScarpe commented 1 year ago

Do you have a sample repo that login tenant with identityserver and swagger Implicit flow or mvc , client ? . I get tenant identification error when redirecting client url in identityserver

HDScarpe commented 1 year ago

@AndrewTriesToCode I have a question that using WithBasePathStrategy, when redirecting client url is also null tenant like claimStrategy? Are you using WithBasePathStrategy and before redirecting the client url you saved the previous tenant.

AndrewTriesToCode commented 1 year ago

In my approach I'm using the base path for every call and for every callback and redirect. I use an option on the base path strategy that sets the ASP.NET base path to include the tenant for all app logic after the multitenant middleware. Then IdentityServer doesn't even see the tenant part of the url and all of it's generated links and such work as normal. This means that the tenant will have to use their own specific url as the OpenID Connect Authority.

This simplifies things because no need to deal with corner cases with claims based tenant strategy on the Identity Server when a user isn't logged in.

devalot76 commented 1 year ago

In my approach I'm using the base path for every call and for every callback and redirect.

Can you point me to some sample code about this approach, please?

AndrewTriesToCode commented 1 year ago

@devalot76 here's the code from the last time I played with this: https://github.com/AndrewTriesToCode/MultiTenantIdentityServer4