ruby-on-railsdevisemulti-tenantapartment-gem

How to migrate existing rails application to multi-tenant application with Apartment?


I'm struggling to migrate existing single-tenant application to multi-tenant application with Apartment and Devise without using subdomains.

I already have an application that has Company and multiple users who belong to that company.
Now I need to scale that application and add another Company. Users in each company are unique and identified by email.

So what I want to is - when user is trying to Sign In - I want to switch to correct tenant based on user email and continue with flow.

I want to keep all User data in separate databases. And here I fail to understand how to lookup correct tenant? All guides and examples show how to handle when User is added to excluded_models. But that doesn't work for me.

So it seems, that there should be some public schema where UserEmail_Tenant table is? Or am I missing something?


Solution

  • You really only have 2 choices:

    1. Ensure that users login via a tenant-specific URL
    2. Have a way to map login credentials to a tenant

    The problem with (2) mapping login credentials to a tenant is that it prohibits credential re-use, which is a show stopper when a user wants to have access to 2 of your tenants at the same time. To handle this case, you have to give every user a tenant-specific ID that is globally unique. You can do it

    Most companies find these options unacceptable, but I have seen it done on occasion.

    Most companies prefer to allow users to reuse their email address as their ID in the case where they have accounts on multiple tenants. The companies choose instead to segregate the tenant accounts by giving each tenant a unique login URL and not allowing direct logins via their (the service-provider company with the multi-tenant app) main site. Instead, they require users to go to the tenant's site and click some kind of login button there, which directs them to the tenant-specific login page.

    Options for tenant-specific URLs include:

    Most companies choose subdomains for 2 reasons:

    1. It makes a cleaner URL than putting a tenant ID parameter in the URL.
    2. It means every tenant can have the exact same path and no tenant parameter for every URL, which can make both development and testing easier and SEO more effective.
    3. To the extent page content varies by tenant, it supports search engines and SEO much better because the page variants are on different domains, not collapsed into one domain and triggered by some opaque parameter.
    4. It makes sharding much easier. When you reach some reasonable number of tenants on one server, instead of jumping through hoops to continue to scale vertically, you can easily scale horizontally by leveraging the subdomain to route traffic to a different server (cluster) for the next set of tenants.

    So while you could create a map of email logins to tenants, I would advise against it, because it will fail painfully as soon as a user tries to create an account with a second tenant using the same email. Even if you think no one will ever do that, you cannot be sure, especially as your company grows. And even if 99.99% of your users do not do that, by the time you hit 1 million users you will have 100 of them who do.

    If you are committed to allowing users to use their email address as their user ID and having them log in via a common URL, I would recommend making your users also indicate which tenant they are logging into, either via text box or option menu, which you can pre-fill/select based on a cookie value. In this scenario, to log in you have to provide user ID, tenant ID, and password.

    If you are OK with users not using their email address as user ID, then I would just make the tenant ID part of the user ID. So user 'steve' in tenant 'apt' might have user ID 'steve_apt'.

    These 2 options both allow the user to have accounts on more than one tenant and also work without any global map of user IDs to tenants.

    If you are committed to allowing users to use their email address as their user ID and having them log in via a common URL, and you do not want them to have to be aware of the fact that the company is one tenant of many on your system, then yes, you need some kind of user ID to tenant mapping.

    You can provide this mapping in several ways.

    Polling the tenants looking for the user ID can also be done in several different ways. I would not recommend this approach in general, but if you are going to do it, I would recommend you do it by using a PostgreSQL database, keeping each tenant in a separate schema within the same database and either using a materialized view or a PL/pgSQL stored dynamic SQL procedure.

    I'm not a big fan of any of these solutions, but I guess if I had to choose I would go with an authentication schema that holds user ID, password, and tenant_id and is only used for login, preferably by an isolated and more secure app. How you handle the fact that this requires the user ID to tenant mapping exist in 2 different places that both can lay a claim to being the source of truth, and what you do when they disagree, is a problem for which my main suggestion is to not do that in the first place.