p2-inc / keycloak-orgs

Single realm, multi-tenancy for SaaS apps
https://phasetwo.io
Other
389 stars 66 forks source link

Q&A; conditional organization login #24

Closed tomschulze closed 1 year ago

tomschulze commented 1 year ago

I want to try your multi-tenancy single-realm concept. My tenants' applications live in separate domains. I am running my keycloak instance behind a proxy. UserA from tenantA logs in by accessing tenanta.app.com/auth/realms/app/protocol/openid-connect/auth and userB from tenantB logs in via tenantb.app.com/auth/realms/app/protocol/openid-connect/auth. They do share the same keycloak instance and the same realm. I want to prevent userA to be able to login from tenantB.app.com.

My approach now would be to write a ConditionalAuthenticator SPI that matches request headers w/ the organization the user's a member of (hostnames would equal organization names). In order to match organizations to the user attempting to login I would use the OrganizationProvider. That's what you do in the OrganizationRoleMapper.

Does that fit into keycloak-orgs logic or am I bending it too much :)? I am wondering if there's any built-in functionality that I can use? The domainModel in the organizationModel seems to be used only for the keycloak-home-idp-discovery, right?

xgp commented 1 year ago

@tomschulze We have a customer with a similar use case. We helped them solve it without the need for an additional authenticator.

Does that help? Feel free to tell me I'm not understanding the use case correclty.

tomschulze commented 1 year ago

Thanks for your quick reply. Sorry for my delay. I had 2 days off :).

The flow you describe would technically work work in my case.

However, I do not want to leave the impression that it's possible to log in from any domain as they are completely unrelated.

In your customer's case the browser redirect would still happen before the "final authentication" in the app. I want to treat a login from userA on tenantb.app.com/auth/realms/app/protocol/openid-connect/auth equally to 'incorrect password or username'. In other words, I do not even want to issue a tokenSet.

So when writing a ConditionalAuthenticator, the best way to map organizations to users would be to use the OrganizationProvider, right?

xgp commented 1 year ago

You can find the relationship from organizations to users with the OrganizationProvider. This method is probably your best bet:

  Stream<OrganizationModel> getUserOrganizationsStream(RealmModel realm, UserModel user);