7

I am not sure if I used the correct title but I was unable to think of a better way to describe it. It could be more of a design question.

I have a multi-tenant DB where one user can belong to one or more entities. I authenticate the user with his/her credentials by calling the /token endpoint.

After I receive a token I call my own end point (using the token) to get the list of available entities for this user and then allow this user to set his current entity in a memory cache. I then use this in memory cache to look up the entity/tenant ID for all following requests to know which entity/tenant the user is "logged into" when calling the DB.

Ideally I would like to eliminate the need for the memory cache in order to make my application more stateless by including the entity/tenant ID as a claim in the token but I only know this ID after the user has authenticated and selected his/her entity. I obviously can't change or add to the claims after the token is issued but is there an alternative design to implement this kind of behavior?

I considered possibly using a sub domain per tenant but technically this is more difficult to setup and maintain. I also considered prompting the user to enter the entity he/she wishes to log in to as free text with their credentials but this isn't ideal.

Has anyone faced this challenge before?

2 Answers 2

10

I agree with you in the fact that the tenant selection must be done in a stateless way, in order to avoid stateful interaction in a REST-based architecture. The caching approach may lead to many pitfalls and introduces a strong dependency on a session-based interaction.

I can think of two main options to make the tenant selection stateless:

  1. URI based selection: you could add to you API a tenant parameter, shared between all your controllers. A mapping similar to this: api/{tenantId}/{controller}/{id} could allow you to switch between tenants in a simple way client-side. This can be done using both the convention-based routing:

    routes.MapHttpRoute(
        name: "API Default",
        routeTemplate: "api/{tenantId}/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
    

    Or the attribute-based routing, using RoutePrefixAttribute on your controllers:

    [RoutePrefix("api/{tenantId}/myentities")]
    public class EntityController : ApiController
    {
        [Route("")]
        public IHttpActionResult GetAllEntities(string tenantId)
        {
            //...
    
  2. Header based selection: you could consider adding a custom HTTP header, which will contain information about the selected tenant: e.g. X-Tenant-Selection: TenantId, then you could read this Header inside a custom Filter, or in a OwinMiddleware executed before Web API, and set a context variable (or even a user claim that will last for the current request) to be used inside your controllers.

Of course in both scenarios you have to validate that the current user can access to the selected tenant before returning data.

Frankly I would exclude the possibility to include the tenantId inside the access token, and the sub-domain approach appears too complicated to me.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks so much for your answer Federico. This is exactly what I was after.
"exclude the possibility to include the tenantId inside the access token" - I agree. An access token should be for identifying properties of the requester, not the request itself. I.e. it indicates what they can do, not what they want to do. An example of why mixing the two could be problematic is if a user has access to more than one tenancy, their token may contain multiple tenant id's.
Don't you think including the TenantId in the access token would be easier to maintain. The frontend guy just sends the entire token, then the data gets filtered based on the access token?
REST is stateless, OAuth2 is not. The access token is related to other informations on server (at least user name) so I think that adding also the TenantId to these information is not so bad. Of course, this is valid only if user-TenantId is 1-1 relationship.
0

Actually I'm not sure that tenant should be implemented in a stateless way. The tenant could be part of the authentication so it should be kept in the state (exactly as User). If the tenant is part of the authentication we should be able of retrieve the tenant from Bearer exactly as we do for user.

In my case I have stored the users in the tenant and joe stored in one tenant is different from joe stored in another tenant. So, when the server receives the Bearer it can't only retrieve the user name, it also need to retrieve the tenant.

I've written a proof of concept. It lacks of groups management (so you can't use attributes in controller to check groups authorization).

It's published here
https://github.com/bubibubi/OAuth2CustomImplementation/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.