We have a multi-tenant system, but want to allow users to have access to multiple tenants.
Ideally, when a user logs in to Sphinx and they have rights to multiple tenants, I'd like to return a list of the tenants available to them and they can choose which one they want to access.
It loos like this tenant choosing is not related to security, but simply business logic.
I mean, security-wise, what matters is what tenants are allowed. If the user will filter all tenants simultaneously or once at time, this is more concerning your UI and business logic.
Thus my suggestion is that you simply implement it in your application.
I'm not sure it is that simple. Each Tenant has their own database and the XData app uses the Tenant Middleware to select the database.
I'm thinking that I have a table in the Sphinx database that links Users to all the Tenants they are allowed to access, so this is identifying the what tenants are allowed.
The process I am thinking of is that the user logs in and then selects the TenantId that they want to work with. We then need to add this to the JWT, but the configure token will be complete before the list is shown.
I have this doubt too.
I don't see it as business logic.
There's a multi-tenant platform, and let's suppose a user, with his personal and unique email address (or phone number, or whatever), needs to login as a "Tenant 1" user, because he does some work for "Company 1", and then he also needs to login as a "Tenant 2" user, because he also works at "Company 2".
Shouldn't it be managed at security level?
I don't think so.
If the same user, with same credentials (unique e-mail/phone number and password), can access both "Tenant 1" and/or "Tenant 2", it's not a security issue. It's an UI/filter issue.
Yes, but then this is UI. The original JWT token generated by Sphinx (actually, generated by you from Sphinx) says the user can have access to Tenant 1 and Tenant 2. If you want your app to only show data from Tenant 1, it's up to your app.
But I believe that Russel and me were talking about same email but (maybe?) distinct passwords (or not) for each tenant. Not the same credentials as such (user + password), but simply the same username, by external organizational factors that we can't control.
Let's suppose we develop an app, multitenant, using Sphinx for authentication & authorization, and then we publish it and sell to different companies as SAAS, and clients are be able to register their users using their personal email accounts or phone numbers. We can't predict if there'll be someone who works for distinct clients. Could be an accountant, lawyer or consultant who provides services to several companies.
What would happen when one tries to register the same user for a 'second' tenant? "This email address is already in use?".
That's why we think that there should be a way to let the user choose the tenant during login when there's a multi-tenant scenario, otherwise the only alternative I can imagine (as an UI oriented solution) is using distinct login screens/pages for each tenant that'll force the authentication points specifically to that tenant behind the scenes.
What would be the best approach for this scenario ?
I currently use tenant-id in the JWT to determine which tenant to access, so I guess I could switch to one of the other methods for tenant identification. I must admit I'd forgotten about those.
No correct answer. Depends on your business needs.
"Multi-tenancy" is a too broad concept. First, with Sphinx you have a completely separated authentication procedure (SSO), so let's separate Sphinx (auth) from your business application (API).
You can have multi tenancy in auth, in the api, and in both.
A user can have a single login info (email/password) and then access multiple "tenants" (companies, for example) in the database.
The auth can be multi tenant, in this case, there will be one user for each tenant. That would be the case if you have multiple different applications, for example.
Usually the tenant is "hidden" from the end-user. (Usually, it's not a rule). That means the user doesn't even realize the app is multi tenant. He goes to "login.myapp.com" to login to MyApp, and he goes to "login.coolapp.com" to login to a second app. But in the end, the authentication server for both apps is the same, same database, etc..
The approach I have come up with is to user a header field to hold the tenant-id instead of the JWT.
So:
User logins into Sphinx
Sphinx returns JWT access token with a claim that lists the tenants the user has rights to access
In the WebCore App OnLoggedIn Event if the var Tenant-Id is not set and the user has more than one tenant available, show a list and ask them to choose.
Choosing the tenant then sets the Tenant-id that is returned with all calls to XData (along with the JWT) and the main page is loaded
On XData server the tenant-id is compared with the available tenants in the the access token to ensure there's been no messing about en-route
I'm contemplating a different approach, where I would have a central Sphinx doing all signing-ins. And each tenant application will be hosted in a separate sub-domain, per Tenant. So the Tenant ID would be the subdomain name, as well as the DB name, as well as the Client application name in Sphinx. The signon would be initiated from each Tenant application, which would redirect to this Sphinx, passing in their Tenant ID as the Client. And each Client would have a redirect set up back to that same Tenant, if it all makes sense.
Can anyone see any flaws in this approach? Or recommend a better one?
All tenants run through a multi-tenant app, which initiates login.
Apps and their required parameters are stored in a table in the Sphinx Database.
There is a Tenant Table and each tenant is either a agent (agents can access multiple clients and don't need to have their own database) or a client (tenant kind) and each user has a Tenant Id.
There is another table that specifies which clients can be managed by the agent.
When the user logs in the kind of tenant is added to the JWT.
On the WebCore app, if the tenant kind is agent then a call is made to get the list of clients that can be managed and the user selects the one they want to work with.
The header option is used to pass the tenant id rather that the JWT.
A user at a client can login and they are directly assigned to their Tenant id.
Ok, thanks! So, you have a single shared Sphinx DB, if I understood you correctly. And then you apparently have something before all this, that prompts the user for something and decides on the user's Tenant (your point 3), right? - because when you call Sphinx URL, you are already requesting a specific Client in the request and it's Tenant-specific. I think it's more complicated than what I want, especially with Agents. I'll try exploring my design next, to see if it comes out any smoother, but if I run into issues, I'll be sure to refer to the approach you are describing...
I have a Sphinx Server application that handles the authentication. It has other methods for managing the tenant data.
There is a server application and a webcore application. The webcore application runs off a single url, I don't use domain or url mapping. The identification of the tenant id is done at login.
The Tenant id is then set as a header value and the multitenant functionality on the server app selects the correct database.