@frontmcp/skills 1.0.3 → 1.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/catalog/frontmcp-authorities/SKILL.md +272 -0
- package/catalog/frontmcp-authorities/references/authority-profiles.md +262 -0
- package/catalog/frontmcp-authorities/references/claims-mapping.md +266 -0
- package/catalog/frontmcp-authorities/references/custom-evaluators.md +420 -0
- package/catalog/frontmcp-authorities/references/rbac-abac-rebac.md +391 -0
- package/catalog/frontmcp-channels/SKILL.md +122 -0
- package/catalog/frontmcp-channels/examples/channel-sources/agent-notify.md +70 -0
- package/catalog/frontmcp-channels/examples/channel-sources/app-errors.md +71 -0
- package/catalog/frontmcp-channels/examples/channel-sources/file-watcher.md +102 -0
- package/catalog/frontmcp-channels/examples/channel-sources/job-completion.md +79 -0
- package/catalog/frontmcp-channels/examples/channel-sources/replay-buffer.md +106 -0
- package/catalog/frontmcp-channels/examples/channel-sources/service-connector.md +136 -0
- package/catalog/frontmcp-channels/examples/channel-sources/webhook-github.md +85 -0
- package/catalog/frontmcp-channels/examples/channel-two-way/whatsapp-bridge.md +133 -0
- package/catalog/frontmcp-channels/references/channel-sources.md +214 -0
- package/catalog/frontmcp-channels/references/channel-two-way.md +195 -0
- package/catalog/frontmcp-config/SKILL.md +20 -18
- package/catalog/frontmcp-config/examples/configure-auth/multi-app-auth.md +1 -2
- package/catalog/frontmcp-config/examples/configure-auth/public-mode-setup.md +1 -2
- package/catalog/frontmcp-config/examples/configure-auth/remote-oauth-with-vault.md +1 -2
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-self-signed-tokens.md +1 -2
- package/catalog/frontmcp-config/examples/configure-auth-modes/remote-enterprise-oauth.md +1 -2
- package/catalog/frontmcp-config/examples/configure-auth-modes/transparent-jwt-validation.md +1 -2
- package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +121 -0
- package/catalog/frontmcp-config/examples/configure-deployment-targets/json-schema-ide-support.md +64 -0
- package/catalog/frontmcp-config/examples/configure-deployment-targets/multi-target-with-security.md +113 -0
- package/catalog/frontmcp-config/examples/configure-elicitation/basic-confirmation-gate.md +1 -2
- package/catalog/frontmcp-config/examples/configure-elicitation/distributed-elicitation-redis.md +1 -2
- package/catalog/frontmcp-config/examples/configure-http/entry-path-reverse-proxy.md +1 -2
- package/catalog/frontmcp-config/examples/configure-http/unix-socket-local.md +1 -2
- package/catalog/frontmcp-config/examples/configure-security-headers/csp-report-only.md +69 -0
- package/catalog/frontmcp-config/examples/configure-security-headers/full-production-headers.md +91 -0
- package/catalog/frontmcp-config/examples/configure-throttle/distributed-redis-throttle.md +1 -2
- package/catalog/frontmcp-config/examples/configure-throttle/per-tool-rate-limit.md +1 -2
- package/catalog/frontmcp-config/examples/configure-throttle/server-level-rate-limit.md +1 -2
- package/catalog/frontmcp-config/examples/configure-transport/custom-protocol-flags.md +1 -2
- package/catalog/frontmcp-config/examples/configure-transport/distributed-sessions-redis.md +1 -2
- package/catalog/frontmcp-config/examples/configure-transport/stateless-serverless.md +1 -2
- package/catalog/frontmcp-config/examples/configure-transport-protocol-presets/legacy-preset-nodejs.md +1 -2
- package/catalog/frontmcp-config/examples/configure-transport-protocol-presets/stateless-api-serverless.md +1 -2
- package/catalog/frontmcp-config/references/configure-deployment-targets.md +214 -0
- package/catalog/frontmcp-config/references/configure-elicitation.md +1 -2
- package/catalog/frontmcp-config/references/configure-security-headers.md +198 -0
- package/catalog/frontmcp-deployment/SKILL.md +1 -0
- package/catalog/frontmcp-deployment/examples/build-for-cli/cli-binary-build.md +1 -2
- package/catalog/frontmcp-deployment/examples/build-for-cli/unix-socket-daemon.md +1 -2
- package/catalog/frontmcp-deployment/examples/build-for-mcpb/mcpb-bundle-build.md +117 -0
- package/catalog/frontmcp-deployment/examples/build-for-sdk/connect-openai.md +1 -3
- package/catalog/frontmcp-deployment/examples/build-for-sdk/create-flat-config.md +1 -2
- package/catalog/frontmcp-deployment/examples/build-for-sdk/multi-platform-connect.md +3 -3
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/basic-worker-deploy.md +1 -2
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/worker-custom-domain.md +1 -2
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/worker-with-kv-storage.md +1 -2
- package/catalog/frontmcp-deployment/examples/deploy-to-lambda/lambda-handler-with-cors.md +1 -2
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +1 -2
- package/catalog/frontmcp-deployment/examples/mcp-client-integration/http-remote.md +106 -0
- package/catalog/frontmcp-deployment/examples/mcp-client-integration/stdio-binary-with-env.md +107 -0
- package/catalog/frontmcp-deployment/examples/mcp-client-integration/stdio-npx.md +89 -0
- package/catalog/frontmcp-deployment/references/build-for-mcpb.md +209 -0
- package/catalog/frontmcp-deployment/references/build-for-sdk.md +1 -2
- package/catalog/frontmcp-deployment/references/mcp-client-integration.md +225 -0
- package/catalog/frontmcp-development/SKILL.md +4 -3
- package/catalog/frontmcp-development/examples/create-agent/basic-agent-with-tools.md +3 -6
- package/catalog/frontmcp-development/examples/create-agent/custom-multi-pass-agent.md +1 -2
- package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +2 -4
- package/catalog/frontmcp-development/examples/create-agent-llm-config/anthropic-config.md +1 -2
- package/catalog/frontmcp-development/examples/create-agent-llm-config/openai-config.md +1 -2
- package/catalog/frontmcp-development/examples/create-job/basic-report-job.md +1 -2
- package/catalog/frontmcp-development/examples/create-job/job-with-permissions.md +2 -3
- package/catalog/frontmcp-development/examples/create-job/job-with-retry.md +1 -2
- package/catalog/frontmcp-development/examples/create-plugin-hooks/tool-level-hooks-and-stage-replacement.md +2 -5
- package/catalog/frontmcp-development/examples/create-provider/basic-database-provider.md +4 -3
- package/catalog/frontmcp-development/examples/create-skill-with-tools/directory-skill-with-tools.md +2 -3
- package/catalog/frontmcp-development/examples/create-tool/basic-class-tool.md +1 -2
- package/catalog/frontmcp-development/examples/create-tool/tool-with-di-and-errors.md +2 -2
- package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +1 -2
- package/catalog/frontmcp-development/examples/create-tool-annotations/destructive-delete-tool.md +2 -4
- package/catalog/frontmcp-development/examples/create-tool-annotations/readonly-query-tool.md +1 -2
- package/catalog/frontmcp-development/examples/create-tool-output-schema-types/primitive-and-media-outputs.md +3 -6
- package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-raw-shape-output.md +1 -2
- package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-schema-advanced-output.md +2 -4
- package/catalog/frontmcp-development/examples/decorators-guide/agent-skill-job-workflow.md +3 -5
- package/catalog/frontmcp-development/examples/decorators-guide/basic-server-with-app-and-tools.md +5 -5
- package/catalog/frontmcp-development/examples/decorators-guide/multi-app-with-plugins-and-providers.md +4 -6
- package/catalog/frontmcp-development/examples/official-plugins/cache-and-feature-flags.md +3 -5
- package/catalog/frontmcp-development/examples/official-plugins/production-multi-plugin-setup.md +4 -5
- package/catalog/frontmcp-development/examples/official-plugins/remember-plugin-session-memory.md +3 -5
- package/catalog/frontmcp-development/examples/{official-adapters → openapi-adapter}/authenticated-adapter-with-polling.md +2 -2
- package/catalog/frontmcp-development/examples/{official-adapters → openapi-adapter}/basic-openapi-adapter.md +2 -2
- package/catalog/frontmcp-development/examples/openapi-adapter/format-resolution-and-custom-resolvers.md +108 -0
- package/catalog/frontmcp-development/examples/{official-adapters → openapi-adapter}/multi-api-hub-with-inline-spec.md +2 -2
- package/catalog/frontmcp-development/examples/openapi-adapter/ref-security-and-filtering.md +111 -0
- package/catalog/frontmcp-development/references/create-agent.md +4 -7
- package/catalog/frontmcp-development/references/create-job.md +3 -6
- package/catalog/frontmcp-development/references/create-plugin-hooks.md +12 -16
- package/catalog/frontmcp-development/references/create-skill-with-tools.md +2 -3
- package/catalog/frontmcp-development/references/create-tool.md +93 -23
- package/catalog/frontmcp-development/references/create-workflow.md +2 -3
- package/catalog/frontmcp-development/references/decorators-guide.md +32 -36
- package/catalog/frontmcp-development/references/official-adapters.md +24 -153
- package/catalog/frontmcp-development/references/openapi-adapter.md +431 -0
- package/catalog/frontmcp-extensibility/examples/vectoriadb/product-catalog-search.md +4 -4
- package/catalog/frontmcp-extensibility/examples/vectoriadb/semantic-search-with-persistence.md +4 -4
- package/catalog/frontmcp-extensibility/examples/vectoriadb/tfidf-keyword-search.md +4 -3
- package/catalog/frontmcp-guides/SKILL.md +3 -3
- package/catalog/frontmcp-guides/examples/example-knowledge-base/agent-and-plugin.md +4 -5
- package/catalog/frontmcp-guides/examples/example-knowledge-base/vector-search-and-resources.md +4 -3
- package/catalog/frontmcp-guides/examples/example-task-manager/auth-and-crud-tools.md +4 -4
- package/catalog/frontmcp-guides/examples/example-weather-api/weather-tool-with-schemas.md +1 -2
- package/catalog/frontmcp-guides/references/example-knowledge-base.md +22 -17
- package/catalog/frontmcp-guides/references/example-task-manager.md +16 -11
- package/catalog/frontmcp-guides/references/example-weather-api.md +6 -3
- package/catalog/frontmcp-observability/examples/telemetry-api/tool-custom-spans.md +2 -3
- package/catalog/frontmcp-observability/examples/tracing-setup/basic-tracing.md +4 -3
- package/catalog/frontmcp-observability/references/telemetry-api.md +2 -3
- package/catalog/frontmcp-production-readiness/examples/common-checklist/observability-setup.md +1 -2
- package/catalog/frontmcp-production-readiness/examples/common-checklist/security-hardening.md +3 -4
- package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +229 -0
- package/catalog/frontmcp-production-readiness/examples/production-browser/cross-platform-crypto.md +2 -3
- package/catalog/frontmcp-production-readiness/examples/production-cli-binary/stdio-transport-error-handling.md +1 -2
- package/catalog/frontmcp-production-readiness/examples/production-cloudflare/durable-objects-state.md +2 -4
- package/catalog/frontmcp-production-readiness/examples/production-cloudflare/workers-runtime-constraints.md +2 -3
- package/catalog/frontmcp-production-readiness/examples/production-lambda/cold-start-connection-reuse.md +3 -2
- package/catalog/frontmcp-production-readiness/examples/production-vercel/cold-start-optimization.md +2 -2
- package/catalog/frontmcp-production-readiness/examples/production-vercel/stateless-serverless-design.md +3 -3
- package/catalog/frontmcp-production-readiness/references/distributed-ha.md +194 -0
- package/catalog/frontmcp-setup/SKILL.md +11 -11
- package/catalog/frontmcp-setup/examples/project-structure-standalone/feature-folder-organization.md +5 -3
- package/catalog/frontmcp-setup/examples/project-structure-standalone/minimal-standalone-layout.md +4 -2
- package/catalog/frontmcp-setup/examples/setup-project/basic-node-server.md +4 -2
- package/catalog/frontmcp-setup/examples/setup-project/vercel-serverless-server.md +4 -2
- package/catalog/frontmcp-setup/examples/setup-redis/hybrid-vercel-kv-with-pubsub.md +8 -7
- package/catalog/frontmcp-setup/references/setup-project.md +10 -9
- package/catalog/frontmcp-setup/references/setup-redis.md +19 -16
- package/catalog/frontmcp-testing/examples/test-direct-client/basic-create-test.md +1 -3
- package/catalog/frontmcp-testing/examples/test-direct-client/openai-claude-format-test.md +1 -3
- package/catalog/frontmcp-testing/examples/test-tool-unit/schema-validation-test.md +2 -2
- package/catalog/frontmcp-testing/references/test-direct-client.md +1 -3
- package/catalog/frontmcp-testing/references/test-tool-unit.md +2 -2
- package/catalog/skills-manifest.json +364 -12
- package/package.json +1 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontmcp-authorities
|
|
3
|
+
description: 'Use when implementing authorization, access control, RBAC, ABAC, or ReBAC for tools, resources, or prompts. Covers JWT claims mapping, authority profiles, and policy enforcement.'
|
|
4
|
+
tags: [authorization, rbac, abac, rebac, security, permissions, roles, access-control, authorities, jwt]
|
|
5
|
+
category: development
|
|
6
|
+
targets: [all]
|
|
7
|
+
bundle: [full]
|
|
8
|
+
priority: 9
|
|
9
|
+
visibility: both
|
|
10
|
+
license: Apache-2.0
|
|
11
|
+
metadata:
|
|
12
|
+
docs: https://docs.agentfront.dev/frontmcp/authentication/authorities
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# FrontMCP Authorities
|
|
16
|
+
|
|
17
|
+
Built-in RBAC/ABAC/ReBAC authorization system for FrontMCP entry types. Each flow has native `checkEntryAuthorities` and `filterByAuthorities` stages that enforce access control policies declared via the `authorities` field on entry decorators. Configured via `@FrontMcp({ authorities: { claimsMapping, profiles, scopeMapping } })` — no plugin needed. Flow stages handle enforcement, and developers can hook into them with `Will`, `Did`, and `Around` decorators. Supports named profiles for reuse, JWT claims mapping for any identity provider, inline policies with roles/permissions/attributes/relationships, and composable combinators (`allOf`, `anyOf`, `not`).
|
|
18
|
+
|
|
19
|
+
## When to Use This Skill
|
|
20
|
+
|
|
21
|
+
### Must Use
|
|
22
|
+
|
|
23
|
+
- Adding role-based or permission-based access control to tools, resources, or prompts
|
|
24
|
+
- Restricting MCP entry visibility based on the caller's JWT claims
|
|
25
|
+
- Enforcing tenant isolation (ABAC) or relationship checks (ReBAC) on entries
|
|
26
|
+
|
|
27
|
+
### Recommended
|
|
28
|
+
|
|
29
|
+
- Setting up a multi-tenant server where different tenants see different tools
|
|
30
|
+
- Building an admin vs. user distinction across your MCP surface
|
|
31
|
+
- Combining multiple authorization models (e.g., RBAC + ABAC) on the same entry
|
|
32
|
+
|
|
33
|
+
### Skip When
|
|
34
|
+
|
|
35
|
+
- You only need authentication (login/token validation) without authorization (see `frontmcp-config` / `configure-auth`)
|
|
36
|
+
- You are building a public server with no access restrictions (use `mode: 'public'`)
|
|
37
|
+
- You need OAuth scopes at the transport level, not entry-level policies (see `configure-auth-modes`)
|
|
38
|
+
|
|
39
|
+
> **Decision:** Use this skill whenever you need to control **who can access which entries** based on roles, permissions, attributes, or relationships.
|
|
40
|
+
|
|
41
|
+
## CRITICAL: Ask About JWT Shape First
|
|
42
|
+
|
|
43
|
+
Before writing any authorities configuration, the coding agent **MUST** ask the developer:
|
|
44
|
+
|
|
45
|
+
> "What identity provider (IdP) are you using, and what does your JWT payload look like? I need to know where roles, permissions, and tenant ID are located in the claims."
|
|
46
|
+
|
|
47
|
+
**Why this matters:** Every IdP places roles and permissions in different JWT claim paths. Auth0 uses namespaced URIs (`https://myapp.com/roles`), Keycloak nests them under `realm_access.roles`, Okta uses `groups`, Cognito uses `cognito:groups`, and Frontegg uses flat `roles`/`permissions`. Writing `claimsMapping` without knowing the actual token shape will produce silent authorization failures where every user is denied.
|
|
48
|
+
|
|
49
|
+
**What to collect before proceeding:**
|
|
50
|
+
|
|
51
|
+
1. Identity provider name (Auth0, Keycloak, Okta, Cognito, Frontegg, custom)
|
|
52
|
+
2. A sample decoded JWT payload (redacted sensitive values)
|
|
53
|
+
3. The claim path for roles (e.g., `realm_access.roles`)
|
|
54
|
+
4. The claim path for permissions (e.g., `permissions` or `scope`)
|
|
55
|
+
5. The claim path for tenant/org ID if multi-tenant (e.g., `org_id`, `tenantId`)
|
|
56
|
+
|
|
57
|
+
See `references/claims-mapping.md` for IdP-specific claim paths.
|
|
58
|
+
|
|
59
|
+
## Prerequisites
|
|
60
|
+
|
|
61
|
+
- FrontMCP SDK installed (`@frontmcp/sdk`)
|
|
62
|
+
- `@frontmcp/auth` available (peer dependency of SDK, provides all authorities types)
|
|
63
|
+
- An authentication mode configured (see `frontmcp-config` / `configure-auth-modes`) so that `authInfo` is populated on incoming requests
|
|
64
|
+
- Knowledge of the developer's JWT token structure (see critical section above)
|
|
65
|
+
|
|
66
|
+
## Steps
|
|
67
|
+
|
|
68
|
+
### Step 1: Add the Authorities Config
|
|
69
|
+
|
|
70
|
+
Add the `authorities` field to your `@FrontMcp` decorator. No plugin import needed — authorities is a built-in framework feature.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { FrontMcp } from '@frontmcp/sdk';
|
|
74
|
+
|
|
75
|
+
@FrontMcp({
|
|
76
|
+
name: 'my-server',
|
|
77
|
+
authorities: {
|
|
78
|
+
// configured in next steps
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
export class MyServer {}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Step 2: Configure JWT Claims Mapping
|
|
85
|
+
|
|
86
|
+
Set `claimsMapping` to tell the engine where roles, permissions, and user/tenant identifiers live in your IdP's JWT. Each value is a dot-path into the decoded JWT claims object.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Keycloak example — in @FrontMcp({ authorities: { ... } })
|
|
90
|
+
authorities: {
|
|
91
|
+
claimsMapping: {
|
|
92
|
+
roles: 'realm_access.roles',
|
|
93
|
+
permissions: 'resource_access.my-client.roles',
|
|
94
|
+
tenantId: 'org_id',
|
|
95
|
+
userId: 'sub',
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Auth0 example
|
|
100
|
+
authorities: {
|
|
101
|
+
claimsMapping: {
|
|
102
|
+
roles: 'https://myapp.com/roles',
|
|
103
|
+
permissions: 'permissions',
|
|
104
|
+
tenantId: 'org_id',
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If no `claimsMapping` is provided, the engine falls back to `authInfo.user.roles` and `authInfo.user.permissions`. For non-standard token shapes, use `claimsResolver` instead (see Common Patterns below).
|
|
110
|
+
|
|
111
|
+
### Step 3: Register Named Profiles
|
|
112
|
+
|
|
113
|
+
Profiles let you define reusable authorization policies and reference them by name in decorators. Register them in the `profiles` field.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// In @FrontMcp({ authorities: { ... } })
|
|
117
|
+
authorities: {
|
|
118
|
+
claimsMapping: { roles: 'realm_access.roles', permissions: 'scope' },
|
|
119
|
+
profiles: {
|
|
120
|
+
admin: {
|
|
121
|
+
roles: { any: ['admin', 'superadmin'] },
|
|
122
|
+
},
|
|
123
|
+
authenticated: {
|
|
124
|
+
attributes: {
|
|
125
|
+
conditions: [{ path: 'user.sub', op: 'exists', value: true }],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
matchTenant: {
|
|
129
|
+
attributes: {
|
|
130
|
+
conditions: [
|
|
131
|
+
{ path: 'claims.org_id', op: 'eq', value: { fromInput: 'tenantId' } },
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
editor: {
|
|
136
|
+
permissions: { any: ['content:write', 'content:publish'] },
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
For type-safe profile names, augment the global interface:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
declare global {
|
|
146
|
+
interface FrontMcpAuthorityProfiles {
|
|
147
|
+
admin: true;
|
|
148
|
+
authenticated: true;
|
|
149
|
+
matchTenant: true;
|
|
150
|
+
editor: true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Step 4: Add Authorities to Entries
|
|
156
|
+
|
|
157
|
+
Use the `authorities` field on any entry decorator (`@Tool`, `@Resource`, `@Prompt`, `@Skill`). Three forms are supported:
|
|
158
|
+
|
|
159
|
+
**String (profile reference):**
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
@Tool({ name: 'delete_user', authorities: 'admin' })
|
|
163
|
+
export default class DeleteUserTool extends ToolContext { ... }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**String array (multiple profiles, AND semantics):**
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
@Tool({ name: 'update_tenant_settings', authorities: ['authenticated', 'matchTenant'] })
|
|
170
|
+
export default class UpdateTenantSettingsTool extends ToolContext { ... }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Inline policy object:**
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
@Tool({
|
|
177
|
+
name: 'publish_content',
|
|
178
|
+
authorities: {
|
|
179
|
+
roles: { any: ['editor', 'admin'] },
|
|
180
|
+
permissions: { all: ['content:publish'] },
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
export default class PublishContentTool extends ToolContext { ... }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Combinators for complex policies:**
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
@Tool({
|
|
190
|
+
name: 'sensitive_action',
|
|
191
|
+
authorities: {
|
|
192
|
+
allOf: [
|
|
193
|
+
{ roles: { any: ['admin'] } },
|
|
194
|
+
{ attributes: { conditions: [{ path: 'env.NODE_ENV', op: 'eq', value: 'production' }] } },
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
})
|
|
198
|
+
export default class SensitiveActionTool extends ToolContext { ... }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Scenario Routing Table
|
|
202
|
+
|
|
203
|
+
| Scenario | Approach | Reference |
|
|
204
|
+
| ------------------------------------- | ---------------------------------------------------------------- | ---------------------------------- |
|
|
205
|
+
| Simple role gate (admin-only tool) | `authorities: 'admin'` profile | `references/authority-profiles.md` |
|
|
206
|
+
| Permission-based access | `authorities: { permissions: { all: ['x'] } }` | `references/rbac-abac-rebac.md` |
|
|
207
|
+
| Tenant isolation | ABAC with `{ fromInput: 'tenantId' }` | `references/rbac-abac-rebac.md` |
|
|
208
|
+
| Resource ownership check | ReBAC with relationship resolver | `references/rbac-abac-rebac.md` |
|
|
209
|
+
| IP allowlist or custom logic | Custom evaluator via `custom.*` | `references/custom-evaluators.md` |
|
|
210
|
+
| Different IdP (Auth0/Keycloak/Okta) | Configure `claimsMapping` | `references/claims-mapping.md` |
|
|
211
|
+
| Admin OR (editor AND same-tenant) | `anyOf` / `allOf` combinators | `references/authority-profiles.md` |
|
|
212
|
+
| Custom pre/post authority logic | Hook with `Will`/`Did`/`Around` on `checkEntryAuthorities` stage | `references/custom-evaluators.md` |
|
|
213
|
+
| Replace built-in check with OPA/Cedar | `Around('checkEntryAuthorities')` hook | `references/custom-evaluators.md` |
|
|
214
|
+
| Audit authority decisions | `Did('checkEntryAuthorities')` hook for logging/metrics | `references/custom-evaluators.md` |
|
|
215
|
+
| Tenant allowlist in Redis/DB | Async custom evaluator with `custom.*` field | `references/custom-evaluators.md` |
|
|
216
|
+
| Subscription check before tool runs | Async custom evaluator or `Will('checkEntryAuthorities')` hook | `references/custom-evaluators.md` |
|
|
217
|
+
| Feature flag gate on a tool | Async custom evaluator checking flag service | `references/custom-evaluators.md` |
|
|
218
|
+
|
|
219
|
+
## Common Patterns
|
|
220
|
+
|
|
221
|
+
| Pattern | Correct | Incorrect | Why |
|
|
222
|
+
| ----------------- | ------------------------------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
223
|
+
| Claims mapping | `claimsMapping: { roles: 'realm_access.roles' }` | `claimsMapping: { roles: 'roles' }` (for Keycloak) | Keycloak nests roles under `realm_access.roles`; using the wrong path silently resolves to `[]` |
|
|
224
|
+
| Profile reference | `authorities: 'admin'` | `authorities: { profile: 'admin' }` | Profiles are referenced as plain strings, not nested objects |
|
|
225
|
+
| Multiple profiles | `authorities: ['authenticated', 'matchTenant']` | `authorities: 'authenticated, matchTenant'` | Use an array, not a comma-separated string |
|
|
226
|
+
| Inline RBAC | `{ roles: { any: ['admin'] } }` | `{ roles: ['admin'] }` | `roles` expects an object with `all` and/or `any` arrays |
|
|
227
|
+
| Dynamic value ref | `{ fromInput: 'tenantId' }` | `'{{ tenantId }}'` | Use `DynamicValueRef` objects, not template strings |
|
|
228
|
+
| OR combinator | `{ anyOf: [policy1, policy2] }` | `{ operator: 'OR', ...policy1, ...policy2 }` | Use `anyOf` for clarity; `operator` applies to top-level fields within a single policy |
|
|
229
|
+
| ReBAC resolver | Register `relationshipResolver` in plugin options | Inline the DB query in the policy | The resolver is a separate interface; policies only declare the relationship to check |
|
|
230
|
+
|
|
231
|
+
## Verification Checklist
|
|
232
|
+
|
|
233
|
+
### Configuration
|
|
234
|
+
|
|
235
|
+
- [ ] `authorities: { ... }` is set on the `@FrontMcp` decorator
|
|
236
|
+
- [ ] `claimsMapping` paths match the actual JWT token structure from your IdP
|
|
237
|
+
- [ ] All profile names referenced in `authorities: 'name'` are registered in `profiles`
|
|
238
|
+
- [ ] Authentication mode is configured (not `public`) so `authInfo` is populated
|
|
239
|
+
- [ ] If using ReBAC, `relationshipResolver` is provided in plugin options
|
|
240
|
+
|
|
241
|
+
### Runtime
|
|
242
|
+
|
|
243
|
+
- [ ] Unauthenticated requests to protected entries receive a denial error
|
|
244
|
+
- [ ] Users with the correct roles/permissions can access their entries
|
|
245
|
+
- [ ] `tools/list` response omits entries the caller is not authorized to see
|
|
246
|
+
- [ ] ABAC conditions with `{ fromInput: ... }` resolve correctly from tool arguments
|
|
247
|
+
- [ ] Custom evaluators return proper `AuthoritiesResult` objects
|
|
248
|
+
|
|
249
|
+
### Type Safety
|
|
250
|
+
|
|
251
|
+
- [ ] `FrontMcpAuthorityProfiles` is augmented for type-safe profile references
|
|
252
|
+
- [ ] `@frontmcp/auth` is imported so the `authorities` field is available on decorators
|
|
253
|
+
|
|
254
|
+
## Troubleshooting
|
|
255
|
+
|
|
256
|
+
| Problem | Cause | Solution |
|
|
257
|
+
| ----------------------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
258
|
+
| `profile 'admin' is not registered` | Profile used in decorator but not in `authorities.profiles` config | Add the profile to the `profiles` field in `@FrontMcp({ authorities })` |
|
|
259
|
+
| All users denied despite correct roles | `claimsMapping.roles` path does not match the actual JWT claim path | Decode a real JWT and verify the dot-path resolves to the roles array |
|
|
260
|
+
| `authorities` field not recognized on decorator | `@frontmcp/auth` not imported (metadata augmentation not active) | Add `import '@frontmcp/auth'` or import any type from `@frontmcp/auth/authorities` |
|
|
261
|
+
| ABAC condition always fails | `{ fromInput: 'tenantId' }` but tool input field is named `tenant_id` | The `fromInput` key must exactly match the tool's input schema field name |
|
|
262
|
+
| ReBAC always denies | No `relationshipResolver` provided | Implement `RelationshipResolver` and pass it to plugin options |
|
|
263
|
+
| Custom evaluator not found | Key in `custom.*` policy does not match registered evaluator name | Ensure the evaluator is registered with the same key used in the policy |
|
|
264
|
+
| List endpoints show all entries | No `authorities` config in `@FrontMcp()` or hook priority conflict | Verify `authorities: { ... }` is set on `@FrontMcp()` decorator |
|
|
265
|
+
| `AuthorityDeniedError` has no detail | `deniedBy` field shows generic message | Check the `evaluatedPolicies` array on the error for which policy type failed |
|
|
266
|
+
|
|
267
|
+
## Reference
|
|
268
|
+
|
|
269
|
+
- [Auth Architecture](https://docs.agentfront.dev/frontmcp/authentication/architecture) — Full three-layer model: server auth, auth providers, authorities, vault, scope challenges
|
|
270
|
+
- [Authorities Documentation](https://docs.agentfront.dev/frontmcp/authentication/authorities) — RBAC/ABAC/ReBAC details, profiles, combinators, hooking
|
|
271
|
+
- Source: `libs/auth/src/authorities/` (engine, types, evaluators), flow stages in `libs/sdk/src/tool/flows/`, `libs/sdk/src/resource/flows/`, `libs/sdk/src/prompt/flows/`, `libs/sdk/src/agent/flows/`
|
|
272
|
+
- Related skills: `frontmcp-config`, `frontmcp-development`, `frontmcp-extensibility`
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: authority-profiles
|
|
3
|
+
description: Registering, composing, and using named authority profiles for reusable authorization policies
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Authority Profiles
|
|
7
|
+
|
|
8
|
+
Named authority profiles let you define reusable authorization policies once and reference them by name in entry decorators. Instead of repeating inline policy objects across dozens of tools, you register profiles in `@FrontMcp({ authorities: { profiles } })` and use `authorities: 'profileName'` on entries.
|
|
9
|
+
|
|
10
|
+
## Registering Profiles
|
|
11
|
+
|
|
12
|
+
Profiles are registered in the `profiles` field of the `authorities` config. Each key is the profile name, and the value is an `AuthoritiesPolicyMetadata` object.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { FrontMcp } from '@frontmcp/sdk';
|
|
16
|
+
|
|
17
|
+
@FrontMcp({
|
|
18
|
+
name: 'my-server',
|
|
19
|
+
authorities: {
|
|
20
|
+
claimsMapping: { roles: 'realm_access.roles', permissions: 'scope' },
|
|
21
|
+
profiles: {
|
|
22
|
+
// RBAC: user must have at least one of these roles
|
|
23
|
+
admin: {
|
|
24
|
+
roles: { any: ['admin', 'superadmin'] },
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// ABAC: user must be authenticated (sub exists)
|
|
28
|
+
authenticated: {
|
|
29
|
+
attributes: {
|
|
30
|
+
conditions: [{ path: 'user.sub', op: 'exists', value: true }],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// ABAC: tenant from JWT must match the tenantId input argument
|
|
35
|
+
matchTenant: {
|
|
36
|
+
attributes: {
|
|
37
|
+
conditions: [
|
|
38
|
+
{ path: 'claims.org_id', op: 'eq', value: { fromInput: 'tenantId' } },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
// RBAC: permission-based
|
|
44
|
+
canPublish: {
|
|
45
|
+
permissions: { all: ['content:publish'] },
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// Combined: role AND permission
|
|
49
|
+
editorWithPublish: {
|
|
50
|
+
roles: { any: ['editor'] },
|
|
51
|
+
permissions: { all: ['content:publish'] },
|
|
52
|
+
// operator defaults to 'AND', so both must pass
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
export class MyServer {}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Using Profiles in Decorators
|
|
62
|
+
|
|
63
|
+
### Single Profile (String)
|
|
64
|
+
|
|
65
|
+
The simplest form. The named profile's policy is evaluated. If the profile is not registered, the request is denied with `"profile 'name' is not registered"`.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
@Tool({ name: 'delete_user', authorities: 'admin' })
|
|
69
|
+
export default class DeleteUserTool extends ToolContext {
|
|
70
|
+
async execute(input: { userId: string }) {
|
|
71
|
+
// only admin or superadmin can reach here
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Resource({
|
|
76
|
+
name: 'internal-metrics',
|
|
77
|
+
uri: 'metrics://internal',
|
|
78
|
+
authorities: 'admin',
|
|
79
|
+
})
|
|
80
|
+
export default class InternalMetricsResource extends ResourceContext {
|
|
81
|
+
async read() {
|
|
82
|
+
// only admin can read this resource
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Prompt({
|
|
87
|
+
name: 'admin-report',
|
|
88
|
+
authorities: 'admin',
|
|
89
|
+
})
|
|
90
|
+
export default class AdminReportPrompt extends PromptContext {
|
|
91
|
+
async execute() {
|
|
92
|
+
// only admin can use this prompt
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Multiple Profiles (String Array)
|
|
98
|
+
|
|
99
|
+
When an array of profile names is provided, they are evaluated with AND semantics -- all profiles must pass.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
@Tool({
|
|
103
|
+
name: 'update_tenant_billing',
|
|
104
|
+
authorities: ['authenticated', 'matchTenant', 'admin'],
|
|
105
|
+
})
|
|
106
|
+
export default class UpdateTenantBillingTool extends ToolContext {
|
|
107
|
+
async execute(input: { tenantId: string; plan: string }) {
|
|
108
|
+
// user must be: authenticated AND in the matching tenant AND admin
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Inline Policy (Object)
|
|
114
|
+
|
|
115
|
+
For one-off policies that do not need to be reused, pass an `AuthoritiesPolicyMetadata` object directly.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
@Tool({
|
|
119
|
+
name: 'view_own_profile',
|
|
120
|
+
authorities: {
|
|
121
|
+
attributes: {
|
|
122
|
+
conditions: [{ path: 'user.sub', op: 'exists', value: true }],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
export default class ViewOwnProfileTool extends ToolContext {
|
|
127
|
+
async execute() {
|
|
128
|
+
// any authenticated user
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Composing Profiles with Combinators
|
|
134
|
+
|
|
135
|
+
For complex authorization logic, use `allOf`, `anyOf`, and `not` combinators within inline policies. Profiles and inline policies can be mixed.
|
|
136
|
+
|
|
137
|
+
### allOf (AND)
|
|
138
|
+
|
|
139
|
+
All nested policies must pass.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
@Tool({
|
|
143
|
+
name: 'deploy_to_production',
|
|
144
|
+
authorities: {
|
|
145
|
+
allOf: [
|
|
146
|
+
{ roles: { any: ['deployer', 'admin'] } },
|
|
147
|
+
{ attributes: { conditions: [{ path: 'env.NODE_ENV', op: 'eq', value: 'production' }] } },
|
|
148
|
+
{ permissions: { all: ['deployments:create'] } },
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
export default class DeployTool extends ToolContext { ... }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### anyOf (OR)
|
|
156
|
+
|
|
157
|
+
At least one nested policy must pass.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
@Tool({
|
|
161
|
+
name: 'manage_content',
|
|
162
|
+
authorities: {
|
|
163
|
+
anyOf: [
|
|
164
|
+
{ roles: { any: ['admin'] } },
|
|
165
|
+
{
|
|
166
|
+
// editor who is in the same tenant
|
|
167
|
+
roles: { any: ['editor'] },
|
|
168
|
+
attributes: {
|
|
169
|
+
conditions: [
|
|
170
|
+
{ path: 'claims.org_id', op: 'eq', value: { fromInput: 'tenantId' } },
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
export default class ManageContentTool extends ToolContext { ... }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### not (Negation)
|
|
181
|
+
|
|
182
|
+
Inverts a nested policy.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
@Tool({
|
|
186
|
+
name: 'public_read_only',
|
|
187
|
+
authorities: {
|
|
188
|
+
not: { roles: { any: ['blocked', 'suspended'] } },
|
|
189
|
+
},
|
|
190
|
+
})
|
|
191
|
+
export default class PublicReadOnlyTool extends ToolContext { ... }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### operator Field
|
|
195
|
+
|
|
196
|
+
The `operator` field controls how top-level fields within a single policy are combined. It defaults to `'AND'`.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// Both roles AND permissions must pass (default AND)
|
|
200
|
+
authorities: {
|
|
201
|
+
roles: { any: ['editor'] },
|
|
202
|
+
permissions: { all: ['content:write'] },
|
|
203
|
+
operator: 'AND',
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Either roles OR permissions must pass
|
|
207
|
+
authorities: {
|
|
208
|
+
roles: { any: ['admin'] },
|
|
209
|
+
permissions: { any: ['content:admin'] },
|
|
210
|
+
operator: 'OR',
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Important:** `operator` applies to the fields within a single `AuthoritiesPolicyMetadata` object. For composing multiple independent policies, use `allOf`/`anyOf`.
|
|
215
|
+
|
|
216
|
+
## Type-Safe Profile Names
|
|
217
|
+
|
|
218
|
+
Augment the global `FrontMcpAuthorityProfiles` interface to get compile-time checks on profile name strings.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// types/authorities.d.ts
|
|
222
|
+
declare global {
|
|
223
|
+
interface FrontMcpAuthorityProfiles {
|
|
224
|
+
admin: true;
|
|
225
|
+
authenticated: true;
|
|
226
|
+
matchTenant: true;
|
|
227
|
+
canPublish: true;
|
|
228
|
+
editorWithPublish: true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export {};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
With this augmentation, `authorities: 'nonexistent'` will produce a TypeScript error.
|
|
236
|
+
|
|
237
|
+
## List Filtering Behavior
|
|
238
|
+
|
|
239
|
+
The authorities system does not only enforce on execution. The built-in `filterByAuthorities` flow stages in `list-tools`, `list-resources`, and `list-prompts` flows filter out entries the caller is not authorized to see. This means:
|
|
240
|
+
|
|
241
|
+
- `tools/list` only returns tools the current user is authorized to call
|
|
242
|
+
- `resources/list` only returns resources the current user can read
|
|
243
|
+
- `prompts/list` only returns prompts the current user can get
|
|
244
|
+
|
|
245
|
+
This filtering happens automatically. No additional configuration is needed. Entries without an `authorities` field are always visible.
|
|
246
|
+
|
|
247
|
+
## Profile Design Guidelines
|
|
248
|
+
|
|
249
|
+
| Guideline | Example |
|
|
250
|
+
| --- | --- |
|
|
251
|
+
| Name profiles after the role or intent, not the technical check | `admin` not `hasAdminRole` |
|
|
252
|
+
| Keep profiles single-purpose | `matchTenant` does tenant check only; combine with `['authenticated', 'matchTenant']` |
|
|
253
|
+
| Use `allOf`/`anyOf` for complex compositions in inline policies | Do not create profiles that duplicate combinator logic |
|
|
254
|
+
| Document your profiles in a central file | `types/authorities.d.ts` with augmentation and comments |
|
|
255
|
+
| Test profiles with both authorized and unauthorized users | See `frontmcp-testing` skill for auth testing patterns |
|
|
256
|
+
|
|
257
|
+
## Reference
|
|
258
|
+
|
|
259
|
+
- Type: `AuthoritiesPolicyMetadata` in `libs/auth/src/authorities/authorities.types.ts`
|
|
260
|
+
- Registry: `AuthoritiesProfileRegistry` in `libs/auth/src/authorities/authorities.registry.ts`
|
|
261
|
+
- Engine: `AuthoritiesEngine` in `libs/auth/src/authorities/authorities.engine.ts`
|
|
262
|
+
- Related: `references/claims-mapping.md`, `references/rbac-abac-rebac.md`, `references/custom-evaluators.md`
|