Setting Up RBAC with Logto Organization Roles and Scopes: Complete Implementation Guide
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 inpackages/schemas/tables/organizations.sql)organization_roles– Contains role definitions per organization, with a mandatorytypefield checked bycheck_organization_role_type(must be"User") (defined inpackages/schemas/tables/organization_roles.sql)organization_role_scope_relations– Maps which scopes belong to which roles, creating the permission matrix (defined inpackages/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 viaorganization_user_relations(defined inpackages/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, Logto enumerates two organization-related scopes in the UserScope enum:
urn:logto:scope:organizations– Returns the list of organization IDs a user belongs tourn: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 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 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) 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 IDsorganization_data– Full organization objectsorganization_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
- Create an organization – Call
POST /api/organizationsto generate an organization record - Define roles – Store role definitions in
organization_roleswithtype: "User" - Attach scopes – Link scopes to roles via
organization_role_scope_relations - Assign users – Ensure user membership in
organization_user_relations, then link to roles inorganization_role_user_relations - Request tokens – Include
urn:logto:scope:organizationsorurn:logto:scope:organization_rolesin authorization requests - 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
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
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, andorganization_role_user_relations - Special OIDC scopes
urn:logto:scope:organizationsandurn:logto:scope:organization_rolesenable claim injection into ID tokens - The URN format
urn:logto:organization:<orgId>standardizes organization identification via helpers inopenid.ts - Role types must be set to
"User"as enforced bycheck_organization_role_type - Management API endpoints in
packages/core/src/routes/admin-user/organization.tsprovide 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.
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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →