# Authorization at Tenant Scope

From [Descope Enterprise Identity](https://learn.descope.io/c/b2b) — Enterprise SSO, tenant-based identity, and the provisioning patterns that arrive with enterprise customers.

Take this course interactively at https://learn.descope.io/c/b2b/authorization-at-tenant-scope

---

![Project Roles vs Tenant Roles. 5 minutes.](video:authorization-at-tenant-scope-project-roles-vs-tenant-roles)

Fundamentals taught roles and permissions at the project level: one set of roles, shared across the whole project. This module is the same mechanic with a scope on it. Multi-tenant apps need some roles to mean different things — or to exist at all — only inside a single customer's tenant.

Every role carries a scope, decided the moment it is created. A project-level role is like a company-wide job title: Editor means the same thing whichever team you are on, and it can be assigned in any tenant. A tenant-scoped role exists inside one tenant only. When you create a role through the Management SDK, the optional `tenantId` argument is what scopes it — present makes it tenant-scoped, omitted makes it project-level.

Tenant admins can also create tenant-scoped roles themselves through the Role Management Widget, which is useful when one customer needs a role your others do not, without cluttering the project-wide list everyone else sees.

## The mistake teams make on their second customer

- Creating a new role per customer — "Acme Admin", "Globex Admin" — instead of reusing one role
- A role is a definition: a name plus a set of permissions
- An assignment is the fact that one specific user holds one specific role within one specific tenant
- The role should stay singular; it is the assignment that is supposed to multiply

If you find yourself naming roles after customers, that is the signal the two have been conflated. Reuse the role, and grant it to the right user in the right tenant instead. That is a tenant-scoped assignment rather than a tenant-scoped role, and it is usually what was actually needed.

## Key terms

- Project-level role — defined once and available to assign in every tenant in the project.
- Tenant-scoped role — defined for, and assignable only within, one specific tenant.
- Assignment — the record that one user holds one role within one tenant; the thing that multiplies per customer.

## Summary

- That a role's scope is fixed when it is created, not when it is assigned
- That roles should be reused and assignments should multiply, not the other way round
- That a role named after a customer is a reliable sign of the two being confused

---

![Reading the Multi-Tenant Token. 4 minutes.](video:authorization-at-tenant-scope-reading-the-multi-tenant-token)

Once a role is assigned within a tenant, Descope carries that fact into the session token so your app can read it without another round trip.

The `tenants` claim is a map. Each key is a tenant ID the user belongs to, and each value nests that tenant's roles and permissions for this user. A separate top-level claim, `dct` — Descope Current Tenant — names whichever of those is currently active: set automatically for a single-tenant user, or after tenant selection for somebody who belongs to more than one.

```json title="A session token for a user in two tenants"
{
  "sub": "U2abc123def456",
  "iss": "P2xyz789ghi012",
  "dct": "T2acme001",
  "tenants": {
    "T2acme001": {
      "roles": ["Tenant Admin"],
      "permissions": ["users:manage", "billing:view"]
    },
    "T2globex002": {
      "roles": ["Viewer"],
      "permissions": ["users:read"]
    }
  },
  "exp": 1893456000
}
```

This user is a Tenant Admin in one customer's tenant and only a Viewer in another — the same platform, two different roles, because roles are scoped per tenant. `dct` confirms which one is active right now.

## Read it with the helpers, not by hand

- `getJwtRoles(token, tenantId)` and `getJwtPermissions(token, tenantId)` read straight from the `tenants` claim for the tenant you name
- `validateTenantRoles(authInfo, tenantId, roles)` on the backend checks a role against a specific tenant in one call
- It is the same validation Fundamentals covered, with a tenant ID added as an argument

## Key terms

- `tenants` claim — the map from each tenant ID the user belongs to onto that tenant's nested roles and permissions.
- `dct` — Descope Current Tenant, the claim naming the user's currently active tenant.

## Summary

- That roles and permissions are nested per tenant ID rather than listed at the root
- Which claim tells you which tenant is active, and when it gets set
- That the SDK helpers take a tenant ID, so you never hand-parse the claim

---

![What Your App Still Enforces. 4 minutes.](video:authorization-at-tenant-scope-what-your-app-still-enforces)

The authorization boundary from Fundamentals does not change here, so it is worth restating at tenant scope rather than assuming it carries over.

"Tenant Admin in `T2acme001`" is a fact Descope hands you in the token. "Tenant Admins can archive invoices" is a fact only your app knows, because invoices are your resource, not Descope's. Descope answers who, which tenant, and which role. Your application still answers whether this role may do this, to this resource.

## What that means in practice

- Your check takes two things from the token together: the role, and the tenant it is scoped to
- It takes one thing from your own domain: what that role is permitted to touch
- Checking the role alone, without confirming which tenant it was held in, is the multi-tenant version of skipping the check entirely

That last point is the one worth dwelling on. A Tenant Admin role in one customer's tenant should never authorize an action against another customer's data, even though the role name matches exactly. A check that reads only the role name will happily allow it, and the token was correct the whole time — which is the same failure shape as the cross-tenant leak in Fundamentals, arriving through authorization rather than through a query.

## Key terms

- Authorization enforcement — interpreting a role and deciding whether to allow an action on a resource.
- Tenant-scoped check — confirming both the role and the tenant it was granted in, before allowing an action against that tenant's data.

## Summary

- That Descope reports tenant and role, and your app still maps that to resource access
- That a role check without a tenant check lets one customer act on another's data
- That the token being correct is no protection when nothing reads the tenant out of it

---

![When RBAC Runs Out. 3 minutes.](video:authorization-at-tenant-scope-when-rbac-runs-out)

Tenant-scoped roles solve "different customers, different permissions". They stop being enough once the requirement stops being about tenants and starts being about individual resources or relationships.

## Two shapes a role cannot express

- Cross-tenant sharing — a user in one tenant needs access to a single resource belonging to another. Roles are granted within a tenant, and there is no role meaning "Editor in a tenant I do not belong to"
- Per-resource grants — a user should edit one specific document, not every document their role's permissions cover in that tenant

Both need the decision to depend on a relationship or an attribute — this specific resource, this specific grant — rather than only on tenant membership and a role name.

Descope's Fine-Grained Authorization, covering relationship-based and attribute-based access control, is built for exactly this gap. It composes with the RBAC you already have rather than replacing it: keep RBAC for the coarse "which tenant, which role" check, and add FGA where access has to turn on a specific relationship or resource.

> [!NOTE]
> This is a signpost rather than a lesson on FGA. Fundamentals covers the model in Authorization, and the deeper treatment belongs to its own module. What matters here is recognizing the two shapes above when a customer describes one, rather than reaching for another role.

## Key terms

- Cross-tenant sharing — granting access to a resource that belongs to a tenant the user is not a member of.
- Per-resource grant — access to one instance of a resource rather than to the resource type across a tenant.
- Fine-Grained Authorization (FGA) — Descope's relationship- and attribute-based authorization service.

## Summary

- The two requirement shapes tenant-scoped roles cannot express on their own
- That both need a relationship or attribute, not a role name
- That FGA composes with RBAC rather than replacing it

---
