Assign different roles for each organization

Hello!

I have looked at some of the closed answers and I feel like they don’t quite cover this use case, so I feel it might be helpful to re-open this.

I explained the problem to the team with this example. I’ll start with the goals:

  • we are a SaaS platform which provides services for many different merchants - let’s call it my_saas_svc
  • each merchant has a unique key - we’ll call this merchantId
  • it is possible for a single identity to be a member of multiple merchant accounts. BUT we would like the identity to have different roles for different merchant accounts
  • the current proposal was to do a claims check for the merchant ID
  • we also have a potential use-case for customers who are also merchants

But if the identity can be associated with multiple merchantIds, this claim would need to be an array. Which leads to a few subtle security problems.

Consider this scenario, where I’m a “bad actor” trying to exploit the system

  1. XYZ corp sells $10 Million AUD a year using my_saas_svc
  2. I get myself hired by XYZ corp as a consultant. They assign my identity the Merchant ReadOnly role for merchant Id mer-123 using an XYZ corp email address
  3. I use my XYZ corp email/identity to sign myself up as a new merchant. I get the Merchant Administrator role, with merchant ID mer-456
  4. I now have roles [ "Merchant ReadOnly", "Merchant Adminstrator" ] and a merchantIds claim of [ "mer-123", "mer-456" ]

If we base our entitlements purely on the roles that are present and make no link between the role and the merchantId that role was actually supposed to be applied for, it’s a problem because it now looks like I have both roles for both merchantIds. I’m now a Merchant Administrator of XYZ Corp. In other words, if my security principle pseudo-code is:

Roles:contains("Merchant Administrator") && Claims:merchantIds:contains("mer-123")

…I’ve managed to become a “Merchant Administrator” of “XYZ Corp” as far as my entitlement checks are concerned, when I was only supposed to be a “Merchant Administrator” of the organisation I created.

What is therefore needed is a SCOPED role-assignment. This is not to be confused with “oauth” scope which has a different meaning - we’ll call our scoping “breadth” to avoid confusion. Our role-assignment would need to have a “breadth” which is limited to a specific merchant, e.g.

  • role assignment 1: { "role": "Merchant ReadOnly", "breadth": "my_saas_svc/merchant/mer-123" }
  • role assignment 2: { "role": "Merchant Administrator", "breadth": "my_saas_svc/merchant/mer-456" }

Then my pseudo-code would become:

Roles:contains(role: "Merchant Administrator", breadth: my_saas_svc/merchant/mer-123)

Access would be denied for my identity, since the “breadth” of the assignment for that role only covers my_saas_svc/merchant/mer-456. An example of a system that supports this kind of scoped/breadthed RBAC is Azure RBAC Steps to assign an Azure role - Azure RBAC | Microsoft Learn

I’m currently looking into “Organizations” as recommended by a colleague:

@konrad.sopala I read your comments on some of the tickets referenced here. I believe this is what those other posters were trying to explain. I also have this design issue.

EDIT: it sort of looks like organizations cover this scenario, as long as two organizations can use my_saas_svc as the IDP for both BUT have different roles between the two organizations which are put in the token on login:

It’s not 100% clear from the documentation if that’s the case, but it kind of sounds like it. I think there’s a limit on the organizations that would mean we eventually have to bump up to enterprise though. There’s some entity limits per organization per tenant as well: