# Setting Up RBAC with Logto Organization Roles and Scopes: Complete Implementation Guide

> Implement Logto organization roles and scopes for granular RBAC. Discover how Logto's relational tables and OIDC scopes enable fine-grained access control. Read the complete guide now.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: how-to-guide
- Published: 2026-07-04

---

**Logto implements organization-level RBAC through four relational tables that define organizations, roles, scopes, and user assignments, exposing this data via special OIDC scopes (`urn:logto:scope:organizations` and `urn:logto:scope:organization_roles`) that inject claims into ID tokens for fine-grained access control.**

Setting up RBAC with Logto organization roles and scopes enables multi-tenant applications to enforce fine-grained permissions at the organization level. In the logto-io/logto repository, this architecture separates user memberships from role definitions and scope assignments using a relational schema defined in SQL migration files. This guide explains the database schema, OIDC claim mapping, and Management API endpoints required to implement organization-level RBAC.

## Understanding the Organization RBAC Data Model

Logto persists organization-level RBAC metadata across four main tables in `packages/schemas/tables/`. This schema ensures referential integrity between organizations, roles, scopes, and users.

### The Four Core Tables

- **`organizations`** – Stores organization metadata (defined in [`packages/schemas/tables/organizations.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/organizations.sql))
- **`organization_roles`** – Contains role definitions per organization, with a mandatory `type` field checked by `check_organization_role_type` (must be `"User"`) (defined in [`packages/schemas/tables/organization_roles.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/organization_roles.sql))
- **`organization_role_scope_relations`** – Maps which scopes belong to which roles, creating the permission matrix (defined in [`packages/schemas/tables/organization_role_scope_relations.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/organization_role_scope_relations.sql))
- **`organization_role_user_relations`** – Links users to roles within organizations, with a foreign-key constraint ensuring the user is already a member via `organization_user_relations` (defined in [`packages/schemas/tables/organization_role_user_relations.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/organization_role_user_relations.sql))

## Organization Scopes and URN Handling

Logto defines special OIDC scopes and URN formats to standardize organization identification across tokens.

### Special OIDC Scopes

In [`packages/toolkit/core-kit/src/openid.ts`](https://github.com/logto-io/logto/blob/main/packages/toolkit/core-kit/src/openid.ts), Logto enumerates two organization-related scopes in the **UserScope** enum:

- **`urn:logto:scope:organizations`** – Returns the list of organization IDs a user belongs to
- **`urn:logto:scope:organization_roles`** – Returns the roles the user has inside each organization

### URN Format and Helpers

Organizations use a deterministic **Uniform Resource Name (URN)** format: `urn:logto:organization:<orgId>`. The [`openid.ts`](https://github.com/logto-io/logto/blob/main/openid.ts) file exports helper functions `buildOrganizationUrn` and `getOrganizationIdFromUrn` for consistent conversion between organization IDs and URN strings.

## API Surface and Token Claims

The RBAC system exposes organization data through REST endpoints and injects authorization claims into OIDC tokens.

### Management API Endpoints

The endpoint defined in [`packages/core/src/routes/admin-user/organization.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/admin-user/organization.ts) provides:

- **GET** `/users/:userId/organizations` – Returns an array of organizations with embedded role data for the specified user

Additional Management API endpoints (referenced in [`packages/core/src/routes/role.openapi.json`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/role.openapi.json)) allow creating organizations, defining roles, attaching scopes, and assigning roles to users.

### ID Token Claim Mapping

When a client requests organization scopes, Logto injects claims into the ID token or userinfo response through `extendedIdTokenClaimsByScope`:

- **`organizations`** – Array of organization IDs
- **`organization_data`** – Full organization objects
- **`organization_roles`** – Role IDs per organization

## Implementation Workflow

Setting up RBAC requires configuring the database layer through the Management API, then requesting the appropriate scopes during authentication.

### Step-by-Step Setup Process

1. **Create an organization** – Call `POST /api/organizations` to generate an organization record
2. **Define roles** – Store role definitions in `organization_roles` with `type: "User"`
3. **Attach scopes** – Link scopes to roles via `organization_role_scope_relations`
4. **Assign users** – Ensure user membership in `organization_user_relations`, then link to roles in `organization_role_user_relations`
5. **Request tokens** – Include `urn:logto:scope:organizations` or `urn:logto:scope:organization_roles` in authorization requests
6. **Verify access** – Resource servers validate the access token and check role-scope relations in the database

## Practical Code Examples

The following TypeScript examples demonstrate interacting with the Management API to configure organization RBAC.

### Creating Organizations and Roles

```typescript
import fetch from 'node-fetch';

const api = (path: string, init: RequestInit = {}) =>
  fetch(`https://api.logto.io/${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer YOUR_ACCESS_TOKEN`,
      'Content-Type': 'application/json',
    },
  }).then((res) => res.json());

// Create an organization
const org = await api('api/organizations', {
  method: 'POST',
  body: JSON.stringify({
    name: 'Acme Corp',
    description: 'Acme Corp organization',
  }),
});

// Define a role inside the organization
const role = await api(`api/organizations/${org.id}/roles`, {
  method: 'POST',
  body: JSON.stringify({
    name: 'admin',
    description: 'Full access inside Acme',
    type: 'User', // Required by check_organization_role_type
  }),
});

// Attach scopes to the role
await api(
  `api/organizations/${org.id}/roles/${role.id}/scopes`,
  {
    method: 'POST',
    body: JSON.stringify({
      scopes: ['read:projects', 'write:projects'],
    }),
  }
);

// Add user to organization and assign role
await api(`api/organizations/${org.id}/users`, {
  method: 'POST',
  body: JSON.stringify({ userId: 'user-123' }),
});

await api(
  `api/organizations/${org.id}/users/user-123/roles`,
  {
    method: 'POST',
    body: JSON.stringify({ roleId: role.id }),
  }
);

```

### Requesting Organization Tokens

```typescript
import { AuthorizationCode } from '@logto/client';

const client = new AuthorizationCode({
  clientId: 'YOUR_CLIENT_ID',
  redirectUri: 'https://your.app/callback',
  issuer: 'https://logto.io',
});

const authUrl = client.getAuthorizationUri({
  scope: 'openid profile urn:logto:scope:organizations urn:logto:scope:organization_roles',
});
// Redirect user to authUrl, exchange code for tokens
// ID token will contain organizations and organization_roles claims

```

## Summary

- Logto implements organization-level RBAC through four tables: `organizations`, `organization_roles`, `organization_role_scope_relations`, and `organization_role_user_relations`
- Special OIDC scopes `urn:logto:scope:organizations` and `urn:logto:scope:organization_roles` enable claim injection into ID tokens
- The URN format `urn:logto:organization:<orgId>` standardizes organization identification via helpers in [`openid.ts`](https://github.com/logto-io/logto/blob/main/openid.ts)
- Role types must be set to `"User"` as enforced by `check_organization_role_type`
- Management API endpoints in [`packages/core/src/routes/admin-user/organization.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/admin-user/organization.ts) provide programmatic access to organization membership data

## Frequently Asked Questions

### What is the difference between organization roles and user roles in Logto?

Organization roles are scoped to specific organizations and stored in `organization_roles`, while user roles are global. Organization roles use the `organization_role_user_relations` table to link users within specific organizational contexts, enabling multi-tenant permission isolation.

### How does Logto validate that a user can be assigned a role in an organization?

The database schema enforces referential integrity through foreign-key constraints in `organization_role_user_relations`. A user must already exist in `organization_user_relations` (the membership table) before they can be assigned a role, preventing orphaned role assignments.

### What claims appear in the ID token when requesting organization scopes?

When requesting `urn:logto:scope:organizations` and `urn:logto:scope:organization_roles`, Logto injects three claims: `organizations` (array of IDs), `organization_data` (full objects), and `organization_roles` (role mappings). These are mapped via `extendedIdTokenClaimsByScope` in [`packages/toolkit/core-kit/src/openid.ts`](https://github.com/logto-io/logto/blob/main/packages/toolkit/core-kit/src/openid.ts).

### Can I use custom scopes with organization roles?

Yes. Custom scopes defined in your API resource can be attached to organization roles through the `organization_role_scope_relations` table. When assigning scopes via the Management API (`POST /api/organizations/{orgId}/roles/{roleId}/scopes`), include your custom scope identifiers in the request body.