Skip to Content
DocsSDKsAuthorization

Authorization (RBAC)

The @ithena-one/mcp-governance SDK provides a flexible Role-Based Access Control (RBAC) system integrated into its processing pipeline.

Enabling RBAC

To enable authorization checks, set the enableRbac option to true in the GovernedServerOptions.

GovernedServerOptions
const options: GovernedServerOptions = { // ... other options enableRbac: true, identityResolver: myIdentityResolver, // REQUIRED for RBAC roleStore: myRoleStore, // REQUIRED for RBAC permissionStore: myPermissionStore, // REQUIRED for RBAC // derivePermission: myPermissionDeriver, // Optional: Defaults work often }; const governedServer = new GovernedServer(baseServer, options);
🚫

When enableRbac is true, you must provide implementations for identityResolver, roleStore, and permissionStore.

RBAC Pipeline Steps

When RBAC is enabled, the following steps occur during the request pipeline (after Identity Resolution):

Check Identity

If the IdentityResolver did not return a UserIdentity (i.e., returned null), an AuthorizationError with reason: 'identity' is thrown, and the request is denied.

Anonymous access is generally not permitted when RBAC is enabled unless specific permissions bypass checks (see next step).

Derive Permission

The derivePermission function (default or custom) is called. It should return a permission string (e.g., tool:call:my_tool) or null if no check is needed for this request.

Check Necessity

If derivePermission returns null, the RBAC check is skipped for this request, and the pipeline proceeds.

Get Roles

If a permission string is returned, RoleStore.getRoles(identity, opCtx) is called to fetch the user’s roles.

Check Permissions

For each role, PermissionStore.hasPermission(role, permission, opCtx) is called.

Grant / Deny

  • Grant: If any role grants the permission (hasPermission returns true), access is granted.
  • Deny: If no role grants the permission, an AuthorizationError with reason: 'permission' is thrown.

(See Core Concepts for the full pipeline diagram)

Components

RBAC relies on these key interfaces:

  • IdentityResolver: Resolves the caller’s UserIdentity. Must return non-null for RBAC checks. (See Interfaces)
  • RoleStore: Maps a UserIdentity to a list of role strings (string[]). (See Interfaces)
  • PermissionStore: Determines if a role grants a permission. (See Interfaces)

Permission Strings

Permissions are simple strings representing actions, generated by the derivePermission function.

  • Default Logic (defaultDerivePermission): Generates strings based on MCP methods and parameters (e.g., tool:call:cleanup, resource:read:db://orders/123). Certain methods like ping return null to skip checks.
  • Custom Logic: Provide your own derivePermission function in GovernedServerOptions for more granular control based on request details or context.
Custom derivePermission Example
// Example Custom derivePermission function myDerivePermission(request: Request, transportCtx: TransportContext): string | null { if (request.method === 'tools/call') { const toolName = request.params?.name; // Add extra check based on IP for a specific tool if (toolName === 'internal_admin_tool' && transportCtx.remoteAddress !== '192.168.1.10') { // Let PermissionStore handle the actual grant/deny based on role, // but derive a specific permission for internal access. return `internal:tool:call:${toolName}`; } // Fallback to default-like logic for other tools return toolName ? `tool:call:${toolName}` : null; } // Use default for other methods (or add more custom logic) return defaultDerivePermission(request, transportCtx); } const options: GovernedServerOptions = { // ... enableRbac: true, derivePermission: myDerivePermission, // ... other RBAC stores };

Error Handling

🚫
  • AuthenticationError: Thrown by IdentityResolver on failure. Results in a 4xx error.
  • AuthorizationError: Thrown by the pipeline if identity is missing (reason: 'identity') or permission is denied (reason: 'permission'). Results in a 4xx error (often -32001).

Denied Request Auditing

By default (auditDeniedRequests: true), requests denied by RBAC are still logged by the AuditLogStore.

  • AuditRecord.outcome.status will be 'denied'.
  • AuditRecord.outcome.error and AuditRecord.authorization will contain details.
  • Set auditDeniedRequests: false to disable auditing for denied requests.
Last updated on