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
.
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
returnstrue
), access is granted. - Deny: If no role grants the permission, an
AuthorizationError
withreason: 'permission'
is thrown.
(See Core Concepts for the full pipeline diagram)
Components
RBAC relies on these key interfaces:
IdentityResolver
: Resolves the caller’sUserIdentity
. Must return non-null for RBAC checks. (See Interfaces)RoleStore
: Maps aUserIdentity
to a list of role strings (string[]
). (See Interfaces)PermissionStore
: Determines if arole
grants apermission
. (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 likeping
returnnull
to skip checks. - Custom Logic: Provide your own
derivePermission
function inGovernedServerOptions
for more granular control based on request details or context.
// 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 byIdentityResolver
on failure. Results in a4xx
error.AuthorizationError
: Thrown by the pipeline if identity is missing (reason: 'identity'
) or permission is denied (reason: 'permission'
). Results in a4xx
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
andAuditRecord.authorization
will contain details.- Set
auditDeniedRequests: false
to disable auditing for denied requests.