Multitenant application - identifying the tenant during login

Hi,

I am evaluating auth0 as a possible authentication approach for our app. Trying Lock for authentication. Our app is a SPA + WebAPI, with a multitenant concept. Each app user is assigned to one or more tenants. For each assigned tenant, the user has a particular set of permissions. A new tenant is automatically created when a new customer sign up within our app. When a user access our app, the tenant is identified by the subdomain of the url, such that before the user logs in, the app already knows the tenant and shows specific information related to it. After hitting the app page for a specific tenant, the user can then log in with his email and password. If the user has been previously assigned to the tenant, that means he has a set of permissions set for the tenant and the app logs the user in successfully.

I am trying to establish the best approach to do this with auth0 lock. I managed to show the login screen and log the user successfully. But I don’t know what’s the best approach to send the current tenant with the authorize request. I thought of two options:

  1. add an scope “tenant:xyz” and access that in a Rule, extract the tenant and pass it as a parameter to a backend api to get the user permissions in that tenant xyz, but that would make me have to dynamically add the scope to the API during a new tenant creation. And there could be hundreds or thousands of tenants in the app. Also, a change in scope requires the user to consent to the new scope in the first login, and if the last login is saved and the user is shown only the e-mail on the login screen, an error message is displayed.

  2. Another approach that I thought of would be to add a custom “tenant” param to the params object in auth0options, so that I could access the tenant in a Rule and call the webapi for the permissions. But when I do this, the tenant custom param seems to be ignored. If I change its name to something else, say “tenantlabel”, it works and becomes accessible in the Rule, but a warning is displayed in the Chrome Console stating that “Following parameters are not allowed on the /authorize endpoint: [tenantlabel]”, what leads me to believe this is not a recommended approach.

Am I ovelooking something? I believe this multitenant scenario is quite common these days but I couldn’t find specific information or examples on how to do the authentication process with auth0. I know authentication is one thing and authorization is another, but I wanted two requirements to be archieved:

  1. During login, tell the user/password is invalid for the tenant if it’s not assigned to it;
  2. Load the user’s tenant permissions and add to the jwt access token so that it’s not necessary to go to the database at each request to the webapi.

I am using Lock in an Angular SPA with ASP.NET Core Webapi as the back end. Latest stable version.

Thank you

2 Likes

Hi Daniel.
The topic of handling of authentication and authorization in multi-tenant applications is really broad, there are many choices to be made and design decisions that might or might not make sense depending on your needs. I’m going to provide some notes that will hopefully be useful to you, but I wouldn’t consider this complete by any means.

One of the first design decisions in a multi-tenant app is how you will handle authentication:

  • Will each tenant have its own user base of email/password authentication (e.g. a DB connection for each tenant) or will there be just one source of users (a single connection)?
  • will you provide your tenant with SSO capabilities (so that they can authenticate with their own identity provider)?
  • Will you provide social login capabilities?

In your case, you mention that a user can be associated with more than one tenant, so let’s assume that there will be one source of users (maybe one DB connection and some social logins) shared across all tenants.

In terms of the authentication transaction, the tenant is not relevant when using a shared user base. Using a vanity URL (such as https://tenant1.app.com) migh provide the application a hint of the user intent (“I want to see my tenant1 resources”), but the identity of the user is the same regardless of the tenant chosen. So, even if you have tenant1.yourapp.com and tenant2.yourapp.com, you will probably have one common authentication endpoint (e.g. yourapp.com/login) that subdomains will use to identify who the user is when needed (that endpoint will probably do so by redirecting to Auth0’s /authorize endpoint, and this might all happen in a popup window to ease state management). Once you know how the user is, and when the user is back at the subdomain, you would know if the user is authorized or not to the tenant. But that’s something that is better handled at the application level, not in a rule (e.g. “You tried to access tenant1, but you are only authorized to access tenant2 and tenant3. Which one would you like to use?”).

If you had different user stores for each tenant, the situation would be different, as you would require a hint to know which directory of users to use when authenticating. In this case, the login_hint parameter in OAuth2 comes in handy.

Then there’s the matter of authorization. You mentioned that each user has a different set of permissions for each assigned tenant. Most likely this will be a set of roles, but that’s completely up to you. Imagine that in your backend, somewhere, you have this information:

{
  "user": "user1",
  "tenants": [
    "tenant1" : {
      "roles": ["full admin"],
    },
    "tenant2": {
      "roles": ["user", "moderator"]
    }
  ]
}

That is information about what the user can do. You can do all authorization decisions in the backend, with just a bearer token that has the user id as a claim (plus the usual expiration, audience, issuer and signature, of course).

Imagine an access_token that simply has this information about the user:

{
 "sub": "user1"
}

And your SPA would make a request to your backend API:

GET https://yourbackend.com/tenant1/api/posts/450

Authorization: Bearer xxxxxx

In this case, as the token only identifies the user, your backend will need to check user1’s permission, to see if she’s allowed to access tenant1 and, in particular, if she can read post 450 (or whatever). If you expect permissions to change on-the-fly, then that’s the model you are after.

You could put permission information in the access_token about what the user can do, but that be simply an optimization. You can write a rule that checks the user permissions somewhere, and use custom claims to convey this information in the access_token and/or id_token. Something like this (I’m simplifying here for easy reading, but you would need to use namespaced claims):

{
  "sub": "user1",
  "roles": "tenant1:full_admin tenant2:user tenant2:moderator"
}

At some point, putting all the permission information in the token becomes unmanageable, especially when a user has many tenants assigned and/or much granularity in the permissions (you end up with a big token). Plus, you will have to live with the fact that access_tokens have a certain validity time, and decide if you are willing to live with that compromise (what if permissions change while the token is still valid?).

A different approach could be creating different APIs for different tenants, so instead of having a simple “https://yourbackend.com/api” audience you could have “https://tenant1.yourbackend.com/api” audiences. Client applications, after determining which tenant the user wants to access, would put that àudience` in the /authorize request:

https://yourtenant.auth0.com/authorize?client=xxxxx&audience=https://tenant1.yourbackend.com/api

In this case, the access_token issued would be only valid to use against tenant1’s resources. If the user switches tenant, the client application would request a different token.

What’s the role of OAuth’ scopes? It’s important to remember that OAuth2 is a delegated authorization protocol: users delegate access to their resources (i.e. the backend API) to clients (e.g. the SPA). So the scopes should not focus on what the user can do (that’s up to the backend to decide), but what the client application can do with that token. It is, implicitely or explicitly, a subset of what the user can do.

So, even if the user is defined like this on the back end:

{
  "sub": "user1",
  "tenants": [
    "tenant1" : {
      "roles": ["full admin"],
    },
    "tenant2": {
      "roles": ["user", "moderator"]
    }
  ]
}

an access_token issued to a specific client could restrict what the application can do. E.g.:

{
  "aud": "https://mybackend.com/api",
  "scopes": "read:posts",
  "sub": "user1",
  "azp": "my_limited_app"
}

So even if user1 is a full administrator, this app (“my_limited_app”) will only be able to “read posts”.

In the case of B2B applications, users aren’t really the owner of the resource, but they themselves where given access (e.g. “You are the administrator of tenant1”). You can try shoehorning these concepts in scopes, so that the list of scopes in the token are the intersection of the scopes allowed to the user based on his permissions and the scopes requested by the client application (or permitted to it). But then again, it might not be a good idea to put the scopes for every tenant in one single access token.

Sorry for the wall of text and not fully organized content, but hopefully it will be somewhat useful. Let me concisely answer your questions:

Your application should handle that after authentication. It will also have better tools for that, such as offering the possibility of signing up, request access, or whatever makes sense.

If you want this, the best choice might be to model each tenant as a different backend API, and indicate in the audience the tenant you want a token for. Beware, though, that if you don’t check the permissions in the backend the permissions in the token will be valid until the token expires.

5 Likes