Checking tenant information in microservices
https://softwareengineering.stackexchange.com/questions/395071
-
28-02-2021 - |
Question
I am currently trying my hand at a microservices architecture for the first time, and I am looking to put together a multi-tenant application built on a this architecture. Tenants are created with their own subdomain, and the tenant owner can create further user accounts linked to that tenant
I currently have the identity api set up, and was thinking of composing the rest a bit like the following:
The Gateways are intended to be implemented as Backend-For-Frontend and would aggregate data as necessary to satisfy the client request to that gateway.
In the identity API, I use the SaasKit middleware to check the subdomain and get tenant details. I was wondering what would be the best approach to apply this tenant discovery across the rest of the services? I am wary of creating a coupling that would undermine the autonomy of microservices. Would I do my tenant discovery in the gateways and pass the tenant ID to the microservices when requests are made to the services, should I be holding local copies of tenant information in each service, or should I use SaasKit in each service and call out to the identity API in each service to get tenant information if its not already cached?
EDIT: To add some context on to how tenants are created; The tenants are created via an API call from a separate system which provides a JWT created by a central authentication service separate to this. Users are also created this way, but the users created here are authenticated here rather than the 'other' authentication service
La solution 2
The implementation I used relied on SaaS kit purely for the Identity service to use the domain detected for authenticating against the correct tenant. From when I the JWT passed to the the gateway and in turn to the underlying services contained a tid field for the tenant ID
I then used that in my Entity Framework context. As an example:
public class MyDbContext : DbContext
{
private readonly ITenantProvider _tenantProvider;
private Guid? TenantId => _tenantProvider.GetTenantId();
public MyDbContext(DbContextOptions<MyDbContext> options, ITenantProvider tenantProvider) : base(options)
{
_tenantProvider = tenantProvider;
}
public DbSet<MyModel> MyModels { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<MyModel>().HasQueryFilter(s => TenantId != null && s.TenantId == TenantId);
}
}
And implemented by ITenantProvider like so. I was checking for both tid and the url for tenantid as the default for JWT is to use the URL if you add a tid property, as described here:
public class HttpContextTenantProvider : ITenantProvider
{
private readonly HttpContext _httpContext;
public HttpContextTenantProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContext = httpContextAccessor.HttpContext;
}
public Guid? GetTenantId()
{
if (_httpContext?.User == null)
{
return null;
}
var tenantIdClaim = _httpContext.User.Claims.FirstOrDefault(c => c.Type.Equals("http://schemas.microsoft.com/identity/claims/tenantid", StringComparison.InvariantCultureIgnoreCase) || c.Type.Equals("tid", StringComparison.InvariantCultureIgnoreCase));
if (tenantIdClaim == null)
{
return null;
}
return Guid.Parse(tenantIdClaim.Value);
}
}
Autres conseils
Since you are using micro-services I am guessing that you are using token based authentication/authorization which means that you have a signed token in which you can securely pass data from a client to a service and from service to service to authorize requests. If not I would suggest to do so because this will enable you to:
Capture/Recognize the tenant information in the API Gateway through the identity service. The identity service would return the auth token which will include the tenant id.
There is no need to keep local copies of tenant ids or make extra calls to the identity server since the tenant id is already in the token
In order to acquire specific tenant configurations/access rights, caching may be required to cache data per tenant for quick retrieval.