Best practise to support subdomain-based multi-tenancy

Hi,

We’re running a multi-tenant web app, where each tenant accesses their environment via their own subdomain, similar to how Slack does that, like my-company.our-webapp.com for example

Technically each subdomain is a dedicated application server connected to a dedicated database per tenant. The database stores all the users and they authenticate (login) directly on that dedicated application server.

The rest api is exposed (www.)our-webapp.com/api/vx.x.x/tenant_id/yyyyy/…, where there our api backend knows which backend database to connect to, based on the yyyyy for the tenant_id in the api url.

My question is this: what would be the best practise in such a setup for Hydra?

I’ve read somewhere that it is not advised to have a single Hydra instance for all tenants, so then it would be a Hydra instance per tenant? Is this realy the best practise, cause I feel that being quite a bit overhead.

On the other hand, if we’d just have a single Hydra instance, could we make it work properly, in a sense that the url for the consent app would likely have to be something like my-company.our-webapp.com/consent?.…, where the subdomain part (‘my-company’) would have to somehow be dynamic

I’m a bit at a loss trying to wrap my head around all the parts involved in a Hydra integration in a scenario as described above and whether it’s doable at all.

I did read up on the upcoming changes to the consent flow in https://github.com/ory/hydra/issues/772, but not sure whether those changes would help my case, make it harder or make no difference at all

Hoping someone can shed some light on this,

Paul

1 Like

Check out https://www.ory.sh/docs/1-hydra/2-overview/3-access-control#multi-tenant-systems

Hi, tnx for the quick response.

I had missed that bit in the docs, but having read it now, I don’t think it answers my questions (or I might just not understand the docs).

How does what is described under that multi-tenant-systems link help in redirecting to the proper app server instance in the consent flow?

Paul

I was in a hurry and did only read the title :slight_smile:

I’ve read somewhere that it is not advised to have a single Hydra instance for all tenants, so then it would be a Hydra instance per tenant? Is this realy the best practise, cause I feel that being quite a bit overhead.

Really depends on your needs of isolation. Having one instance is fine if you don’t really care that much about isolation. A valid use case for one instance would be an org-wide authZ server where tenants get access credentials from. For multiple instances it would make sense if each org should have its own authZ server. This might make sense where you have dedicated IAM systems in place per tenant.

could we make it work properly, in a sense that the url for the consent app would likely have to be something like my-company.our-webapp.com/consent?.…, where the subdomain part (‘my-company’) would have to somehow be dynamic

Since the implementation is up to you, you could definitely do that. A simple if / switch statement or something like that would solve that easily.

Hi arekkas

Tnx for your explanation.

In our setup we basically have one IAM systems in place per tenant, so as per your suggestion we’d have to run one hydra per instance per tenant.

But I was wondering: 3rd party apps that want to register with us, so tenants can choose to enable them in their instance of our webapp and grant them access to our API, we need to issue those 3rd party apps stuff like the client_id and client_secret, right? And those should be the same client_id/client_secret for every instance of our webapp, I think.

Wouldn’t that be easier/only possible when working with a single Hydra instance?

Regarding whether or not we could make it work and your answer to that that is could be as simple as a switch statement, I have a hard time envisioning that… for example: assume we have a single Hydra instance running and a 3rd party app which is already registered (has a client_id/secret) wants to access our webapp for tenant X, where tenant X’s webap is hosted on x.our-webapp.com. What would the flow then be, which urls would be in play and where would the aforementioned switch statement come in exactly?

One approach than I think might work would be:

  • 3rd party app goes to x.our-webapp.com/oauth2/auth?client_id=…, which we redirect to the Hydra instance
  • Hydra redirects to %%consent_url%%/contenst?consent=…, which is a shared url between all out tenants.
  • the page behing the consent url makes the rest call to Hydra to get more details about the consent request and with those details it is somehow able to determine to which tenant specific url to redirect the User Agent for authentication and authorization

Not being very familiar yet with Hydra, I wonder if the above is feasible, what I’m missing, if I’m not setting myself up for tons of (security) issues this way etc etc.

Some pointers more than appreciated

Paul

If I understand you correctly you want to have different consent apps for every tenant, right? That is very easy to solve:

Please be aware that consent apps are like the most fucking important thing in this set up. Pardon my language, but they are that important. A consent app can impersonate any user. It’s like the universal source of truth. Unless you want your tenants to be able to authenticate users on some other’s tenant behalf, you should really not do this.

Your complete setup feels complicated and specialized. I could probably help you more by knowing about the architecture and use cases, and what you anticipate there. But my time for community help & management is quite limited. If you’d like more sophisticated help than me dropping a few lines in this forums, I recommend checking out our consulting services (link on website, write me a pn, ping me in chat, drop a line to [email protected]). I think they’d make sense in your case. I would also recommend to first play a bit more with Hydra to get a better feeling if it’s the right software for your architecture (it does sound that way but you never know), before you commit to that service.

1 Like

Hi arekkas,

I hear you loud and clear. Will do some more digging and reviewing and might contact you for additional consulting.

Paul

1 Like

@paul I think you should use acr_values
See https://www.ory.sh/docs/api/hydra/
API: /oauth2/auth/requests/consent/{challenge}

x-go-name: ACRValues
ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request.
It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.

OpenID Connect defines it as follows:

Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values
that the Authorization Server is being requested to use for processing this Authentication Request, with the
values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication
performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a
Voluntary Claim by this parameter.

https://identityserver.github.io/Documentation/docsv2/endpoints/authorization.html

Hi,

Its been a while, but we finally got back to figuring out how we will implement oAuth2 and if we can use Hydra Ory for the implementation.

Yesterday we seem to have made a breakthrough in our research: we figured out that Hydra supports relative url’s as the (redirect) urls for the login and consent apps.

So, we use a (reverse) proxy in front of our deployment with the following rules:
*.mycompany.com/login > login app
*.mycompany.com/consent > consent app
*.mycompany.com/oauth2 > Hydra

And with the following config for Hydra:
urls.login: ‘/login’
urls.consent: ‘/consent’

This way, we can run with a single instance of Hydra, the login app and the consent app for all subdomains, like:

The client app that wants to gain access to the data of one of our tenants will ask their user their subdomain with us (sub2 for example) and with that start the oAuth2 flow, by opening sub2.mycompany.com/oauth2. Hydra will redirect to ‘/login’, which, since its a relative url, will resolve in the User Agent to sub2.mycompany.com/login etc etc.

Inside the login (and consent) app we read the subdomain of the request and based on that we know the tenant and can validate the users credentials against the tenant-specific AIM instance.

Now, we have the following questions:

  1. Is the fact that the consent and login urls can be relative urls a supported feature or is it something that might change in the future?
  2. What are the reasons for suggesting to use separate Hydra Ory instances per tenant? In several threads/discussions related to multi-tenancy support on this forum and under issues in GitLab, it seems to be suggested that A: isolate different tenants by using separate Hydra instances per tenant and B: Hydra isn’t designed for multi-tenancy (see https://github.com/ory/hydra/issues/428#issuecomment-296104694 for example). We’re trying to understand where the multi-tenancy limitations are in Hydra (from your perspective) and the reasoning behind the suggestion to isolate tenants using separate Hydra instances: what are the (security) issues when sharing a single Hydra instance across multiple tenants?
  3. If using separate Hydra instances per tenant is advised, how would one solve the ‘problem’ of registration of 3rd party clients, across all tenants? Meaning: a 3rd party wants to register their app with us (mycompany), making it available to all our tenants to install. So, the registration of this 3rd part app needs to be shared by all these individual Hydra instances somehow, because the client_id for this 3rd part app needs to be the same for each tenant

Hoping for some further insight (and the confirmation that the relative url support mentioned under Q1 is a feature :slight_smile: ),

Regards,
Paul

@tangkhaiphuong Mmm, don’t see how ACRValues are related to this question and how they could solve the problem

Hi,

To better aid the discussion, I’ve created a separate topic for questions 2 & 3 from my post above:

So this topic can be about answering question 1 :slight_smile:

Hello,

I am currently trying to setup the solution @paul suggested.

However I am running into the issue that Hydra redirects the user agent with the urls.self.issuer domain name as base instead of the domain that the request came thru, which is tenant.myservice.com.

I am currently using the version 1.2.1 of Hydra.

Was the relative route solving for login and consent removed? @hackerman

So far the only solution for me would be to implement a public instance of hydra for each tenant, with the only difference in config being the urls.self.issuer, where I use the tenant domain.

Which redirect specifically?

@hackerman The redirect from Hydra to the Login provider.

I have found the issue.

I am using AppAuth for the frontend. What this does is first fetch the configuration for openid, this will return the value stored at ** urls.self.issuer**.

So the auth process starts with the issuer url instead of the tenant URL i want.

Screenshot from 2020-01-23 16.35.07

To solve this I had to change the configuration URLs to use my tenant domain name, it still works since they point to the same openid provider through a proxy.

are the relative url’s working for specifying the login-consent provider, we tried with the following config.

  urls:
    consent: /consent
    login: /login
    logout: /logout

getting the following exception while starting the server.

msg="The configuration is invalid and could not be loaded." [config_key=urls.consent]="\"/consent\" is not valid \"uri\"" [config_key=urls.login]="\"/login\" is not valid \"uri\"" [config_key=urls.logout]="\"/logout\" is not valid \"uri\"" [config_key=urls]="validation failed" config_file=/etc/config/config.yaml

any recommendations on how the urls can be configured … we are using version “v1.4.6”

we seem to be expecting a uri (config.shcema.json), a simple string should suffice i think…

“login”: {
“type”: “string”,
“description”: “Sets the login endpoint of the User Login & Consent flow. Defaults to an internal fallback URL.”,
“format”: “uri”,
“examples”: [
https://my-login.app/login
]
}

These can not be relative any more and must be absolute

any thoughts on how we could configure a single instance of hydra for sub-domain based multi-tenant app ?

Write a “meta” consent app and redirect to the concrete consent app based on e.g. client id or some other discriminators