Finbuckle.MultiTenant icon indicating copy to clipboard operation
Finbuckle.MultiTenant copied to clipboard

[Question] MultiTenancy without stores?

Open Thijs153 opened this issue 1 year ago • 5 comments

Hey @AndrewTriesToCode, I’m relatively new to this library and I’m enjoying it so far, but I have a question: Is it somehow possible to implement MultiTenancy without specifying/implementing any stores?

I’m using Keycloak to specify my tenants (realms). I’ve already implemented the authentication part (custom) and I’m not trying to use ‘WithPerTenantAuthentication’ here. The tenant name/identifier can be retrieved from the token, so that’ll be my strategy.

As for the TenantInfo, I just need one with the id/identifier set to this claim value, without any additional settings. I don’t really like having to specify all the different tenant names in the config. Hence the question.

So again, is it somehow possible to have a sort of ‘tenant resolver’ that returns a Tenant with the id/identifier set to a value in a token, without having to specify/configure a store?

I hope this makes sense, thanks!

Thijs153 avatar Apr 08 '24 20:04 Thijs153

Hi, I see what you mean. I recommend a very basic store that does nothing but implement IMultiTenantStore<TTenantInfo>.TryGetByIdentifier to return a new tenant info object with the identifier passed into it. It doesn't actually look anything up, just makes the tenant info instance and returns it. Then you would register it using WithStore<T>.

AndrewTriesToCode avatar Apr 09 '24 02:04 AndrewTriesToCode

Thanks for the quick reply. Your suggestion works for me 👍

For anyone coming across this issue, I've implemented the store like this:

public sealed class KeycloakTenantStore : IMultiTenantStore<TenantInfo>
{
    public async Task<TenantInfo?> TryGetByIdentifierAsync(string identifier)
        => await Task.FromResult(new TenantInfo { Id = identifier, Identifier = identifier, });

    public async Task<TenantInfo?> TryGetAsync(string id)
        => await Task.FromResult(new TenantInfo { Id = id, Identifier = id, });
    
    public Task<bool> TryAddAsync(TenantInfo tenantInfo)
        => throw new NotImplementedException();
    
    public Task<bool> TryUpdateAsync(TenantInfo tenantInfo)
        => throw new NotImplementedException();

    public Task<bool> TryRemoveAsync(string identifier)
        => throw new NotImplementedException();
    
    public Task<IEnumerable<TenantInfo>> GetAllAsync()
        => throw new NotImplementedException();
}

The issue can be closed in my opinion.

Thijs153 avatar Apr 09 '24 06:04 Thijs153

Nice. It might make sense to add this to the library for testing and certain use cases. Would you be up for submitting it as a PR?

AndrewTriesToCode avatar Apr 09 '24 13:04 AndrewTriesToCode

@AndrewTriesToCode, sure. How would you like me to call this new 'store'? And is it just creating a new store, creating an extension method for it, and that's is? Or do I need to do more things?

Thijs153 avatar Apr 10 '24 07:04 Thijs153

Hi, adding the store to the Finbuckle.MultiTenant stores folder, adding an extension method to MultiTenantBuilder and adding a short section to the stores Docs is what I’d recommend. I think calling it the Echo store makes sense— just gives back what you put in.

AndrewTriesToCode avatar Apr 10 '24 13:04 AndrewTriesToCode