@frontmcp/sdk 0.5.0 → 0.6.0
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/README.md +3 -3
- package/package.json +8 -19
- package/src/adapter/adapter.instance.js +5 -0
- package/src/adapter/adapter.instance.js.map +1 -1
- package/src/auth/authorization/authorization.class.d.ts +1 -4
- package/src/auth/authorization/authorization.class.js +6 -13
- package/src/auth/authorization/authorization.class.js.map +1 -1
- package/src/auth/flows/session.verify.flow.d.ts +1 -0
- package/src/auth/flows/session.verify.flow.js +11 -1
- package/src/auth/flows/session.verify.flow.js.map +1 -1
- package/src/auth/flows/well-known.jwks.flow.js +2 -2
- package/src/auth/flows/well-known.jwks.flow.js.map +1 -1
- package/src/auth/jwks/dev-key-persistence.d.ts +63 -0
- package/src/auth/jwks/dev-key-persistence.js +219 -0
- package/src/auth/jwks/dev-key-persistence.js.map +1 -0
- package/src/auth/jwks/index.d.ts +1 -0
- package/src/auth/jwks/index.js +1 -0
- package/src/auth/jwks/index.js.map +1 -1
- package/src/auth/jwks/jwks.service.d.ts +7 -4
- package/src/auth/jwks/jwks.service.js +81 -12
- package/src/auth/jwks/jwks.service.js.map +1 -1
- package/src/auth/jwks/jwks.types.d.ts +7 -0
- package/src/auth/jwks/jwks.types.js.map +1 -1
- package/src/auth/machine-id.d.ts +5 -0
- package/src/auth/machine-id.js +32 -0
- package/src/auth/machine-id.js.map +1 -0
- package/src/auth/session/index.d.ts +1 -0
- package/src/auth/session/index.js +3 -1
- package/src/auth/session/index.js.map +1 -1
- package/src/auth/session/record/session.base.js +5 -3
- package/src/auth/session/record/session.base.js.map +1 -1
- package/src/auth/session/record/session.stateless.d.ts +2 -2
- package/src/auth/session/record/session.stateless.js +5 -3
- package/src/auth/session/record/session.stateless.js.map +1 -1
- package/src/auth/session/redis-session.store.d.ts +64 -0
- package/src/auth/session/redis-session.store.js +204 -0
- package/src/auth/session/redis-session.store.js.map +1 -0
- package/src/auth/session/session.service.d.ts +0 -2
- package/src/auth/session/session.service.js +1 -7
- package/src/auth/session/session.service.js.map +1 -1
- package/src/auth/session/transport-session.manager.js +3 -5
- package/src/auth/session/transport-session.manager.js.map +1 -1
- package/src/auth/session/transport-session.types.d.ts +4 -0
- package/src/auth/session/transport-session.types.js +4 -3
- package/src/auth/session/transport-session.types.js.map +1 -1
- package/src/auth/session/utils/session-id.utils.d.ts +12 -1
- package/src/auth/session/utils/session-id.utils.js +48 -9
- package/src/auth/session/utils/session-id.utils.js.map +1 -1
- package/src/auth/ui/base-layout.d.ts +0 -8
- package/src/auth/ui/base-layout.js +1 -14
- package/src/auth/ui/base-layout.js.map +1 -1
- package/src/auth/ui/index.d.ts +3 -4
- package/src/auth/ui/index.js +10 -11
- package/src/auth/ui/index.js.map +1 -1
- package/src/auth/ui/{htmx-templates.d.ts → templates.d.ts} +5 -6
- package/src/auth/ui/{htmx-templates.js → templates.js} +8 -15
- package/src/auth/ui/templates.js.map +1 -0
- package/src/common/decorators/decorator-utils.js.map +1 -1
- package/src/common/decorators/front-mcp.decorator.js +28 -2
- package/src/common/decorators/front-mcp.decorator.js.map +1 -1
- package/src/common/index.d.ts +0 -1
- package/src/common/index.js +0 -1
- package/src/common/index.js.map +1 -1
- package/src/common/interfaces/adapter.interface.d.ts +6 -0
- package/src/common/interfaces/adapter.interface.js.map +1 -1
- package/src/common/interfaces/execution-context.interface.d.ts +52 -3
- package/src/common/interfaces/execution-context.interface.js +88 -3
- package/src/common/interfaces/execution-context.interface.js.map +1 -1
- package/src/common/interfaces/flow.interface.d.ts +13 -0
- package/src/common/interfaces/flow.interface.js +24 -0
- package/src/common/interfaces/flow.interface.js.map +1 -1
- package/src/common/interfaces/server.interface.d.ts +9 -0
- package/src/common/interfaces/server.interface.js.map +1 -1
- package/src/common/metadata/app.metadata.d.ts +108 -0
- package/src/common/metadata/front-mcp.metadata.d.ts +659 -2
- package/src/common/metadata/front-mcp.metadata.js +3 -1
- package/src/common/metadata/front-mcp.metadata.js.map +1 -1
- package/src/common/metadata/provider.metadata.d.ts +14 -0
- package/src/common/metadata/provider.metadata.js +18 -2
- package/src/common/metadata/provider.metadata.js.map +1 -1
- package/src/common/metadata/tool.metadata.d.ts +33 -1
- package/src/common/metadata/tool.metadata.js.map +1 -1
- package/src/common/migrate/auth-transport.migrate.d.ts +62 -0
- package/src/common/migrate/auth-transport.migrate.js +140 -0
- package/src/common/migrate/auth-transport.migrate.js.map +1 -0
- package/src/common/migrate/index.d.ts +1 -0
- package/src/common/migrate/index.js +6 -0
- package/src/common/migrate/index.js.map +1 -0
- package/src/common/schemas/http-output.schema.d.ts +10 -2
- package/src/common/schemas/index.d.ts +1 -0
- package/src/common/schemas/index.js +1 -0
- package/src/common/schemas/index.js.map +1 -1
- package/src/common/schemas/session-header.schema.d.ts +16 -0
- package/src/common/schemas/session-header.schema.js +42 -0
- package/src/common/schemas/session-header.schema.js.map +1 -0
- package/src/common/tokens/front-mcp.tokens.js +3 -1
- package/src/common/tokens/front-mcp.tokens.js.map +1 -1
- package/src/common/types/options/auth.options.d.ts +233 -3
- package/src/common/types/options/auth.options.js +29 -40
- package/src/common/types/options/auth.options.js.map +1 -1
- package/src/common/types/options/index.d.ts +2 -0
- package/src/common/types/options/index.js +2 -0
- package/src/common/types/options/index.js.map +1 -1
- package/src/common/types/options/redis.options.d.ts +22 -0
- package/src/common/types/options/redis.options.js +45 -0
- package/src/common/types/options/redis.options.js.map +1 -0
- package/src/common/types/options/transport.options.d.ts +84 -0
- package/src/common/types/options/transport.options.js +121 -0
- package/src/common/types/options/transport.options.js.map +1 -0
- package/src/completion/flows/complete.flow.d.ts +17 -2
- package/src/context/frontmcp-context-storage.d.ts +94 -0
- package/src/context/frontmcp-context-storage.js +183 -0
- package/src/context/frontmcp-context-storage.js.map +1 -0
- package/src/context/frontmcp-context.d.ts +269 -0
- package/src/context/frontmcp-context.js +360 -0
- package/src/context/frontmcp-context.js.map +1 -0
- package/src/context/frontmcp-context.provider.d.ts +43 -0
- package/src/context/frontmcp-context.provider.js +61 -0
- package/src/context/frontmcp-context.provider.js.map +1 -0
- package/src/context/index.d.ts +34 -0
- package/src/context/index.js +64 -0
- package/src/context/index.js.map +1 -0
- package/src/context/request-context-storage.d.ts +89 -0
- package/src/context/request-context-storage.js +183 -0
- package/src/context/request-context-storage.js.map +1 -0
- package/src/context/request-context.d.ts +184 -0
- package/src/context/request-context.js +209 -0
- package/src/context/request-context.js.map +1 -0
- package/src/context/request-context.provider.d.ts +37 -0
- package/src/context/request-context.provider.js +51 -0
- package/src/context/request-context.provider.js.map +1 -0
- package/src/context/session-key.provider.d.ts +45 -0
- package/src/context/session-key.provider.js +65 -0
- package/src/context/session-key.provider.js.map +1 -0
- package/src/context/trace-context.d.ts +43 -0
- package/src/context/trace-context.js +142 -0
- package/src/context/trace-context.js.map +1 -0
- package/src/errors/index.d.ts +1 -1
- package/src/errors/index.js +3 -1
- package/src/errors/index.js.map +1 -1
- package/src/errors/mcp.error.d.ts +7 -0
- package/src/errors/mcp.error.js +11 -1
- package/src/errors/mcp.error.js.map +1 -1
- package/src/flows/flow.instance.d.ts +16 -0
- package/src/flows/flow.instance.js +166 -80
- package/src/flows/flow.instance.js.map +1 -1
- package/src/flows/flow.registry.d.ts +5 -0
- package/src/flows/flow.registry.js +45 -3
- package/src/flows/flow.registry.js.map +1 -1
- package/src/front-mcp/front-mcp.d.ts +12 -0
- package/src/front-mcp/front-mcp.js +22 -3
- package/src/front-mcp/front-mcp.js.map +1 -1
- package/src/front-mcp/front-mcp.providers.d.ts +266 -1
- package/src/front-mcp/front-mcp.providers.js +2 -1
- package/src/front-mcp/front-mcp.providers.js.map +1 -1
- package/src/front-mcp/serverless-handler.d.ts +28 -0
- package/src/front-mcp/serverless-handler.js +61 -0
- package/src/front-mcp/serverless-handler.js.map +1 -0
- package/src/hooks/hooks.utils.d.ts +1 -1
- package/src/hooks/hooks.utils.js +10 -3
- package/src/hooks/hooks.utils.js.map +1 -1
- package/src/index.d.ts +8 -4
- package/src/index.js +20 -1
- package/src/index.js.map +1 -1
- package/src/logger/instances/instance.logger.js +0 -1
- package/src/logger/instances/instance.logger.js.map +1 -1
- package/src/logging/flows/set-level.flow.d.ts +17 -2
- package/src/notification/notification.service.js +5 -1
- package/src/notification/notification.service.js.map +1 -1
- package/src/prompt/flows/get-prompt.flow.d.ts +97 -2
- package/src/prompt/flows/prompts-list.flow.d.ts +12 -1
- package/src/provider/provider.registry.d.ts +97 -5
- package/src/provider/provider.registry.js +306 -9
- package/src/provider/provider.registry.js.map +1 -1
- package/src/provider/provider.types.d.ts +21 -3
- package/src/provider/provider.types.js.map +1 -1
- package/src/resource/flows/read-resource.flow.d.ts +22 -3
- package/src/resource/flows/resource-templates-list.flow.d.ts +20 -1
- package/src/resource/flows/resources-list.flow.d.ts +20 -1
- package/src/resource/flows/subscribe-resource.flow.d.ts +17 -2
- package/src/resource/flows/unsubscribe-resource.flow.d.ts +17 -2
- package/src/scope/flows/http.request.flow.js +43 -7
- package/src/scope/flows/http.request.flow.js.map +1 -1
- package/src/scope/scope.instance.js +12 -5
- package/src/scope/scope.instance.js.map +1 -1
- package/src/server/adapters/base.host.adapter.d.ts +9 -0
- package/src/server/adapters/base.host.adapter.js.map +1 -1
- package/src/server/adapters/express.host.adapter.d.ts +12 -0
- package/src/server/adapters/express.host.adapter.js +21 -1
- package/src/server/adapters/express.host.adapter.js.map +1 -1
- package/src/server/server.instance.d.ts +3 -0
- package/src/server/server.instance.js +14 -7
- package/src/server/server.instance.js.map +1 -1
- package/src/tool/flows/call-tool.flow.d.ts +118 -13
- package/src/tool/flows/call-tool.flow.js +240 -194
- package/src/tool/flows/call-tool.flow.js.map +1 -1
- package/src/tool/flows/tools-list.flow.d.ts +25 -11
- package/src/tool/flows/tools-list.flow.js +82 -31
- package/src/tool/flows/tools-list.flow.js.map +1 -1
- package/src/tool/tool.instance.d.ts +1 -4
- package/src/transport/adapters/transport.streamable-http.adapter.js +1 -0
- package/src/transport/adapters/transport.streamable-http.adapter.js.map +1 -1
- package/src/transport/flows/handle.sse.flow.js +9 -2
- package/src/transport/flows/handle.sse.flow.js.map +1 -1
- package/src/transport/flows/handle.streamable-http.flow.js +63 -6
- package/src/transport/flows/handle.streamable-http.flow.js.map +1 -1
- package/src/transport/mcp-handlers/complete-request.handler.d.ts +27 -1
- package/src/transport/mcp-handlers/get-prompt-request.handler.d.ts +52 -1
- package/src/transport/mcp-handlers/index.d.ts +413 -7
- package/src/transport/mcp-handlers/initialize-request.handler.js +12 -2
- package/src/transport/mcp-handlers/initialize-request.handler.js.map +1 -1
- package/src/transport/mcp-handlers/list-prompts-request.handler.d.ts +27 -1
- package/src/transport/mcp-handlers/list-resource-templates-request.handler.d.ts +32 -1
- package/src/transport/mcp-handlers/list-resources-request.handler.d.ts +32 -1
- package/src/transport/mcp-handlers/list-tools-request.handler.d.ts +30 -1
- package/src/transport/mcp-handlers/logging-set-level-request.handler.d.ts +20 -0
- package/src/transport/mcp-handlers/read-resource-request.handler.d.ts +27 -1
- package/src/transport/mcp-handlers/subscribe-request.handler.d.ts +20 -0
- package/src/transport/mcp-handlers/unsubscribe-request.handler.d.ts +20 -0
- package/src/transport/transport.registry.d.ts +68 -4
- package/src/transport/transport.registry.js +313 -11
- package/src/transport/transport.registry.js.map +1 -1
- package/src/auth/ui/htmx-templates.js.map +0 -1
- package/src/common/providers/session.provider.d.ts +0 -13
- package/src/common/providers/session.provider.js +0 -27
- package/src/common/providers/session.provider.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source width="400" media="(prefers-color-scheme: dark)" srcset="docs/live/assets/logo/frontmcp.dark.svg">
|
|
5
|
-
<source width="400" media="(prefers-color-scheme: light)" srcset="docs/live/assets/logo/frontmcp.light.svg">
|
|
6
|
-
<img width="400" alt="FrontMCP Logo" src="docs/live/assets/logo/frontmcp.light.svg">
|
|
4
|
+
<source width="400" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/agentfront/frontmcp/refs/heads/main/docs/live/assets/logo/frontmcp.dark.svg">
|
|
5
|
+
<source width="400" media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/agentfront/frontmcp/refs/heads/main/docs/live/assets/logo/frontmcp.light.svg">
|
|
6
|
+
<img width="400" alt="FrontMCP Logo" src="https://raw.githubusercontent.com/agentfront/frontmcp/refs/heads/main/docs/live/assets/logo/frontmcp.light.svg">
|
|
7
7
|
</picture>
|
|
8
8
|
<hr>
|
|
9
9
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "FrontMCP SDK",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"homepage": "https://docs.agentfront.dev",
|
|
@@ -40,28 +40,17 @@
|
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"zod": "^4.0.0",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"react": {
|
|
48
|
-
"optional": true
|
|
49
|
-
},
|
|
50
|
-
"react-dom": {
|
|
51
|
-
"optional": true
|
|
52
|
-
}
|
|
43
|
+
"express": "^4.21.0",
|
|
44
|
+
"cors": "^2.8.5",
|
|
45
|
+
"raw-body": "^3.0.0",
|
|
46
|
+
"content-type": "^1.0.5"
|
|
53
47
|
},
|
|
54
48
|
"dependencies": {
|
|
55
|
-
"@frontmcp/ui": "0.
|
|
56
|
-
"@modelcontextprotocol/sdk": "1.
|
|
57
|
-
"axios": "^1.6.0",
|
|
49
|
+
"@frontmcp/ui": "0.6.0",
|
|
50
|
+
"@modelcontextprotocol/sdk": "1.24.3",
|
|
58
51
|
"ioredis": "^5.8.0",
|
|
59
52
|
"jose": "^6.1.0",
|
|
60
|
-
"
|
|
61
|
-
"reflect-metadata": "^0.2.2",
|
|
62
|
-
"rxjs": "^7.8.0",
|
|
63
|
-
"vectoriadb": "1.0.0",
|
|
64
|
-
"tslib": "^2.3.0"
|
|
53
|
+
"reflect-metadata": "^0.2.2"
|
|
65
54
|
},
|
|
66
55
|
"devDependencies": {
|
|
67
56
|
"typescript": "^5.9.3"
|
|
@@ -42,6 +42,11 @@ class AdapterInstance extends common_1.AdapterEntry {
|
|
|
42
42
|
else {
|
|
43
43
|
throw Error('Invalid adapter kind');
|
|
44
44
|
}
|
|
45
|
+
// Inject logger if adapter supports it
|
|
46
|
+
if (typeof adapter.setLogger === 'function') {
|
|
47
|
+
const logger = this.globalProviders.get(common_1.FrontMcpLogger);
|
|
48
|
+
adapter.setLogger(logger.child(`adapter:${adapter.options.name}`));
|
|
49
|
+
}
|
|
45
50
|
const result = await adapter.fetch();
|
|
46
51
|
this.tools = new tool_registry_1.default(this.globalProviders, result.tools ?? [], {
|
|
47
52
|
kind: 'adapter',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.instance.js","sourceRoot":"","sources":["../../../src/adapter/adapter.instance.ts"],"names":[],"mappings":";;;;AAAA,
|
|
1
|
+
{"version":3,"file":"adapter.instance.js","sourceRoot":"","sources":["../../../src/adapter/adapter.instance.ts"],"names":[],"mappings":"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","sourcesContent":["import { AdapterEntry, AdapterInterface, AdapterKind, AdapterRecord, Ctor, Reference, FrontMcpLogger } from '../common';\nimport ProviderRegistry from '../provider/provider.registry';\nimport ToolRegistry from '../tool/tool.registry';\nimport ResourceRegistry from '../resource/resource.registry';\nimport PromptRegistry from '../prompt/prompt.registry';\n\nexport class AdapterInstance extends AdapterEntry {\n readonly deps: Set<Reference>;\n readonly globalProviders: ProviderRegistry;\n\n private tools: ToolRegistry;\n private resources: ResourceRegistry;\n private prompts: PromptRegistry;\n\n constructor(record: AdapterRecord, deps: Set<Reference>, globalProviders: ProviderRegistry) {\n super(record);\n this.deps = deps;\n this.globalProviders = globalProviders;\n\n this.ready = this.initialize();\n }\n\n protected async initialize() {\n const depsTokens = [...this.deps];\n const depsInstances = await Promise.all(depsTokens.map((t) => this.globalProviders.resolveBootstrapDep(t)));\n const rec = this.record;\n let adapter: AdapterInterface;\n if (rec.kind === AdapterKind.CLASS) {\n const klass = rec.useClass as any;\n adapter = new klass(...depsInstances);\n } else if (rec.kind === AdapterKind.CLASS_TOKEN) {\n const klass = rec.provide as any;\n adapter = new (klass as Ctor<any>)(...depsInstances);\n } else if (rec.kind === AdapterKind.FACTORY) {\n const deps = [...rec.inject()];\n const args: any[] = [];\n for (const d of deps) args.push(await this.globalProviders.resolveBootstrapDep(d));\n adapter = rec.useFactory(...args);\n } else if (rec.kind === AdapterKind.VALUE) {\n adapter = rec.useValue;\n } else {\n throw Error('Invalid adapter kind');\n }\n\n // Inject logger if adapter supports it\n if (typeof adapter.setLogger === 'function') {\n const logger = this.globalProviders.get(FrontMcpLogger);\n adapter.setLogger(logger.child(`adapter:${adapter.options.name}`));\n }\n\n const result = await adapter.fetch();\n\n this.tools = new ToolRegistry(this.globalProviders, result.tools ?? [], {\n kind: 'adapter',\n id: `${adapter.options.name}`,\n ref: rec.provide,\n });\n\n // this.resources = new ResourceRegistry(this.globalProviders, result.resources ?? [], {\n // kind: 'adapter',\n // id: rec.metadata.id ?? rec.metadata.name,\n // ref: rec.provide,\n // });\n\n // this.prompts = new PromptRegistry(this.globalProviders, result.prompts ?? [], {\n // kind: 'adapter',\n // id: rec.metadata.id ?? rec.metadata.name,\n // ref: rec.provide,\n // });\n\n await this.tools.ready;\n }\n}\n"]}
|
|
@@ -2,10 +2,7 @@ import { Authorization, AuthorizationCreateCtx, AuthorizedPrompt, AuthorizedTool
|
|
|
2
2
|
import { TransportSession, TransportProtocol } from '../session';
|
|
3
3
|
import { ProviderSnapshot } from '../session/session.types';
|
|
4
4
|
import { AuthMode } from '../../common';
|
|
5
|
-
|
|
6
|
-
* Get the current machine ID
|
|
7
|
-
*/
|
|
8
|
-
export declare function getMachineId(): string;
|
|
5
|
+
export { getMachineId } from '../machine-id';
|
|
9
6
|
/**
|
|
10
7
|
* Base Authorization class - represents authenticated user context
|
|
11
8
|
* Subclasses implement mode-specific behavior (Public, Transparent, Orchestrated)
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// auth/authorization/authorization.class.ts
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.AuthorizationBase = void 0;
|
|
5
|
-
exports.getMachineId = getMachineId;
|
|
4
|
+
exports.AuthorizationBase = exports.getMachineId = void 0;
|
|
6
5
|
const crypto_1 = require("crypto");
|
|
7
6
|
const session_id_utils_1 = require("../session/utils/session-id.utils");
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get the current machine ID
|
|
14
|
-
*/
|
|
15
|
-
function getMachineId() {
|
|
16
|
-
return MACHINE_ID;
|
|
17
|
-
}
|
|
7
|
+
const machine_id_1 = require("../machine-id");
|
|
8
|
+
// Re-export getMachineId for backwards compatibility
|
|
9
|
+
var machine_id_2 = require("../machine-id");
|
|
10
|
+
Object.defineProperty(exports, "getMachineId", { enumerable: true, get: function () { return machine_id_2.getMachineId; } });
|
|
18
11
|
/**
|
|
19
12
|
* Base Authorization class - represents authenticated user context
|
|
20
13
|
* Subclasses implement mode-specific behavior (Public, Transparent, Orchestrated)
|
|
@@ -74,7 +67,7 @@ class AuthorizationBase {
|
|
|
74
67
|
protocol,
|
|
75
68
|
createdAt: Date.now(),
|
|
76
69
|
expiresAt: this.expiresAt,
|
|
77
|
-
nodeId:
|
|
70
|
+
nodeId: (0, machine_id_1.getMachineId)(),
|
|
78
71
|
clientFingerprint: fingerprint,
|
|
79
72
|
};
|
|
80
73
|
this.#sessions.set(session.id, session);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorization.class.js","sourceRoot":"","sources":["../../../../src/auth/authorization/authorization.class.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/authorization/authorization.class.ts\n\nimport { randomUUID } from 'crypto';\nimport {\n Authorization,\n AuthorizationCreateCtx,\n AuthorizedPrompt,\n AuthorizedTool,\n AuthUser,\n LLMSafeAuthContext,\n} from './authorization.types';\nimport { TransportSession, TransportProtocol, SessionJwtPayload } from '../session';\nimport { ProviderSnapshot } from '../session/session.types';\nimport { encryptJson } from '../session/utils/session-id.utils';\nimport { AuthMode } from '../../common';\n\n// Single-process machine id generated at server launch\nconst MACHINE_ID = (() => {\n return process.env['MACHINE_ID'] || randomUUID();\n})();\n\n/**\n * Get the current machine ID\n */\nexport function getMachineId(): string {\n return MACHINE_ID;\n}\n\n/**\n * Base Authorization class - represents authenticated user context\n * Subclasses implement mode-specific behavior (Public, Transparent, Orchestrated)\n */\nexport abstract class AuthorizationBase implements Authorization {\n readonly id: string;\n abstract readonly mode: AuthMode;\n readonly isAnonymous: boolean;\n readonly user: AuthUser;\n readonly claims?: Record<string, unknown>;\n readonly expiresAt?: number;\n readonly scopes: string[];\n readonly authorizedProviders: Record<string, ProviderSnapshot>;\n readonly authorizedProviderIds: string[];\n readonly authorizedApps: Record<string, { id: string; toolIds: string[] }>;\n readonly authorizedAppIds: string[];\n readonly authorizedTools: Record<string, AuthorizedTool>;\n readonly authorizedToolIds: string[];\n readonly authorizedPrompts: Record<string, AuthorizedPrompt>;\n readonly authorizedPromptIds: string[];\n readonly authorizedResources: string[];\n\n /** The original bearer token (for transparent mode) */\n protected readonly token?: string;\n\n /** Active transport sessions for this authorization */\n readonly #sessions: Map<string, TransportSession> = new Map();\n\n /** Creation timestamp */\n readonly createdAt: number;\n\n protected constructor(ctx: AuthorizationCreateCtx) {\n this.id = ctx.id;\n this.isAnonymous = ctx.isAnonymous;\n this.user = ctx.user;\n this.claims = ctx.claims;\n this.expiresAt = ctx.expiresAt;\n this.scopes = ctx.scopes ?? [];\n this.token = ctx.token;\n this.createdAt = Date.now();\n\n // Initialize authorization projections\n this.authorizedProviders = ctx.authorizedProviders ?? {};\n this.authorizedProviderIds = ctx.authorizedProviderIds ?? Object.keys(this.authorizedProviders);\n this.authorizedApps = ctx.authorizedApps ?? {};\n this.authorizedAppIds = ctx.authorizedAppIds ?? Object.keys(this.authorizedApps);\n this.authorizedTools = ctx.authorizedTools ?? {};\n this.authorizedToolIds = ctx.authorizedToolIds ?? Object.keys(this.authorizedTools);\n this.authorizedPrompts = ctx.authorizedPrompts ?? {};\n this.authorizedPromptIds = ctx.authorizedPromptIds ?? Object.keys(this.authorizedPrompts);\n this.authorizedResources = ctx.authorizedResources ?? [];\n }\n\n /**\n * Create a new transport session for this authorization\n * @param protocol - Transport protocol (sse, streamable-http, etc.)\n * @param fingerprint - Optional client fingerprint for tracking\n */\n createTransportSession(protocol: TransportProtocol, fingerprint?: string): TransportSession {\n const sessionId = randomUUID();\n\n const session: TransportSession = {\n id: sessionId,\n authorizationId: this.id,\n protocol,\n createdAt: Date.now(),\n expiresAt: this.expiresAt,\n nodeId: MACHINE_ID,\n clientFingerprint: fingerprint,\n };\n\n this.#sessions.set(session.id, session);\n return session;\n }\n\n /**\n * Get existing transport session by ID\n */\n getTransportSession(sessionId: string): TransportSession | undefined {\n return this.#sessions.get(sessionId);\n }\n\n /**\n * Get all active transport sessions\n */\n getAllSessions(): TransportSession[] {\n return Array.from(this.#sessions.values());\n }\n\n /**\n * Remove a transport session\n */\n removeTransportSession(sessionId: string): boolean {\n return this.#sessions.delete(sessionId);\n }\n\n /**\n * Get count of active sessions\n */\n get sessionCount(): number {\n return this.#sessions.size;\n }\n\n /**\n * Abstract: Get access token for a provider\n * Implementation varies by mode:\n * - Public: throws (no tokens)\n * - Transparent: returns the original bearer token\n * - Orchestrated: retrieves from vault/store\n */\n abstract getToken(providerId?: string): Promise<string>;\n\n /**\n * Check if a scope is granted\n */\n hasScope(scope: string): boolean {\n return this.scopes.includes(scope);\n }\n\n /**\n * Check if all scopes are granted\n */\n hasAllScopes(scopes: string[]): boolean {\n return scopes.every((s) => this.scopes.includes(s));\n }\n\n /**\n * Check if any scope is granted\n */\n hasAnyScope(scopes: string[]): boolean {\n return scopes.some((s) => this.scopes.includes(s));\n }\n\n /**\n * Check if a tool is authorized\n */\n canAccessTool(toolId: string): boolean {\n return toolId in this.authorizedTools || this.authorizedToolIds.includes(toolId);\n }\n\n /**\n * Check if a prompt is authorized\n */\n canAccessPrompt(promptId: string): boolean {\n return promptId in this.authorizedPrompts || this.authorizedPromptIds.includes(promptId);\n }\n\n /**\n * Check if an app is authorized.\n * Used for progressive authorization to determine if tools from this app can be executed.\n * @param appId - App ID to check\n */\n isAppAuthorized(appId: string): boolean {\n return appId in this.authorizedApps || this.authorizedAppIds.includes(appId);\n }\n\n /**\n * Build URL for progressive/incremental authorization.\n * Used when a tool requires authorization for an app that was skipped during initial auth.\n * @param appId - App ID that requires authorization\n * @param baseUrl - Base URL of the server\n */\n getProgressiveAuthUrl(appId: string, baseUrl: string): string {\n return `${baseUrl}/oauth/authorize?app=${encodeURIComponent(appId)}&mode=incremental`;\n }\n\n /**\n * Check if the authorization is expired\n */\n isExpired(): boolean {\n if (!this.expiresAt) return false;\n return Date.now() > this.expiresAt;\n }\n\n /**\n * Get time until expiration in milliseconds\n * Returns undefined if no expiration, negative if expired\n */\n getTimeToExpiry(): number | undefined {\n if (!this.expiresAt) return undefined;\n return this.expiresAt - Date.now();\n }\n\n /**\n * Convert a transport session to encrypted session JWT\n * This is what gets sent in the Mcp-Session-Id header\n */\n toSessionJwt(session: TransportSession): string {\n const payload: SessionJwtPayload = {\n sid: session.id,\n aid: this.id,\n proto: session.protocol,\n nid: session.nodeId,\n iat: Math.floor(Date.now() / 1000),\n exp: this.expiresAt ? Math.floor(this.expiresAt / 1000) : undefined,\n };\n return encryptJson(payload);\n }\n\n /**\n * Convert to LLM-safe context (no tokens exposed)\n */\n toLLMSafeContext(session: TransportSession): LLMSafeAuthContext {\n return {\n authorizationId: this.id,\n sessionId: session.id,\n mode: this.mode,\n isAnonymous: this.isAnonymous,\n user: {\n sub: this.user.sub,\n name: this.user.name,\n },\n scopes: this.scopes,\n authorizedToolIds: this.authorizedToolIds,\n authorizedPromptIds: this.authorizedPromptIds,\n };\n }\n\n /**\n * Validate that no tokens are leaked in data\n * Throws if JWT pattern detected\n */\n static validateNoTokenLeakage(data: unknown): void {\n const json = JSON.stringify(data);\n // Detect JWT pattern (header.payload.signature)\n if (/eyJ[A-Za-z0-9_-]+\\.eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+/.test(json)) {\n throw new Error('SECURITY: Token detected in data - potential LLM context leak');\n }\n // Detect sensitive field names\n const sensitiveFields = ['access_token', 'refresh_token', 'id_token', 'tokenEnc', 'secretRefId'];\n for (const field of sensitiveFields) {\n if (json.includes(`\"${field}\"`)) {\n throw new Error(`SECURITY: Sensitive field \"${field}\" detected - potential leak`);\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"authorization.class.js","sourceRoot":"","sources":["../../../../src/auth/authorization/authorization.class.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;AAE5C,mCAAoC;AAWpC,wEAAgE;AAEhE,8CAA6C;AAE7C,qDAAqD;AACrD,4CAA6C;AAApC,0GAAA,YAAY,OAAA;AAErB;;;GAGG;AACH,MAAsB,iBAAiB;IAC5B,EAAE,CAAS;IAEX,WAAW,CAAU;IACrB,IAAI,CAAW;IACf,MAAM,CAA2B;IACjC,SAAS,CAAU;IACnB,MAAM,CAAW;IACjB,mBAAmB,CAAmC;IACtD,qBAAqB,CAAW;IAChC,cAAc,CAAoD;IAClE,gBAAgB,CAAW;IAC3B,eAAe,CAAiC;IAChD,iBAAiB,CAAW;IAC5B,iBAAiB,CAAmC;IACpD,mBAAmB,CAAW;IAC9B,mBAAmB,CAAW;IAEvC,uDAAuD;IACpC,KAAK,CAAU;IAElC,uDAAuD;IAC9C,SAAS,GAAkC,IAAI,GAAG,EAAE,CAAC;IAE9D,yBAAyB;IAChB,SAAS,CAAS;IAE3B,YAAsB,GAA2B;QAC/C,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE5B,uCAAuC;QACvC,IAAI,CAAC,mBAAmB,GAAG,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC;QACzD,IAAI,CAAC,qBAAqB,GAAG,GAAG,CAAC,qBAAqB,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChG,IAAI,CAAC,cAAc,GAAG,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;QAC/C,IAAI,CAAC,gBAAgB,GAAG,GAAG,CAAC,gBAAgB,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACjF,IAAI,CAAC,eAAe,GAAG,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,iBAAiB,GAAG,GAAG,CAAC,iBAAiB,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACpF,IAAI,CAAC,iBAAiB,GAAG,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC;QACrD,IAAI,CAAC,mBAAmB,GAAG,GAAG,CAAC,mBAAmB,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC1F,IAAI,CAAC,mBAAmB,GAAG,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC;IAC3D,CAAC;IAED;;;;OAIG;IACH,sBAAsB,CAAC,QAA2B,EAAE,WAAoB;QACtE,MAAM,SAAS,GAAG,IAAA,mBAAU,GAAE,CAAC;QAE/B,MAAM,OAAO,GAAqB;YAChC,EAAE,EAAE,SAAS;YACb,eAAe,EAAE,IAAI,CAAC,EAAE;YACxB,QAAQ;YACR,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,MAAM,EAAE,IAAA,yBAAY,GAAE;YACtB,iBAAiB,EAAE,WAAW;SAC/B,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACxC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,SAAiB;QACnC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,sBAAsB,CAAC,SAAiB;QACtC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC7B,CAAC;IAWD;;OAEG;IACH,QAAQ,CAAC,KAAa;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,MAAgB;QAC3B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,MAAgB;QAC1B,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,MAAc;QAC1B,OAAO,MAAM,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnF,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,QAAgB;QAC9B,OAAO,QAAQ,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3F,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,KAAa;QAC3B,OAAO,KAAK,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/E,CAAC;IAED;;;;;OAKG;IACH,qBAAqB,CAAC,KAAa,EAAE,OAAe;QAClD,OAAO,GAAG,OAAO,wBAAwB,kBAAkB,CAAC,KAAK,CAAC,mBAAmB,CAAC;IACxF,CAAC;IAED;;OAEG;IACH,SAAS;QACP,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC;QAClC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;IACrC,CAAC;IAED;;;OAGG;IACH,eAAe;QACb,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QACtC,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACrC,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,OAAyB;QACpC,MAAM,OAAO,GAAsB;YACjC,GAAG,EAAE,OAAO,CAAC,EAAE;YACf,GAAG,EAAE,IAAI,CAAC,EAAE;YACZ,KAAK,EAAE,OAAO,CAAC,QAAQ;YACvB,GAAG,EAAE,OAAO,CAAC,MAAM;YACnB,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;YAClC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SACpE,CAAC;QACF,OAAO,IAAA,8BAAW,EAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,OAAyB;QACxC,OAAO;YACL,eAAe,EAAE,IAAI,CAAC,EAAE;YACxB,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,IAAI,EAAE;gBACJ,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG;gBAClB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;aACrB;YACD,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;YACzC,mBAAmB,EAAE,IAAI,CAAC,mBAAmB;SAC9C,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,sBAAsB,CAAC,IAAa;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAClC,gDAAgD;QAChD,IAAI,sDAAsD,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACnF,CAAC;QACD,+BAA+B;QAC/B,MAAM,eAAe,GAAG,CAAC,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;QACjG,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,6BAA6B,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAxOD,8CAwOC","sourcesContent":["// auth/authorization/authorization.class.ts\n\nimport { randomUUID } from 'crypto';\nimport {\n Authorization,\n AuthorizationCreateCtx,\n AuthorizedPrompt,\n AuthorizedTool,\n AuthUser,\n LLMSafeAuthContext,\n} from './authorization.types';\nimport { TransportSession, TransportProtocol, SessionJwtPayload } from '../session';\nimport { ProviderSnapshot } from '../session/session.types';\nimport { encryptJson } from '../session/utils/session-id.utils';\nimport { AuthMode } from '../../common';\nimport { getMachineId } from '../machine-id';\n\n// Re-export getMachineId for backwards compatibility\nexport { getMachineId } from '../machine-id';\n\n/**\n * Base Authorization class - represents authenticated user context\n * Subclasses implement mode-specific behavior (Public, Transparent, Orchestrated)\n */\nexport abstract class AuthorizationBase implements Authorization {\n readonly id: string;\n abstract readonly mode: AuthMode;\n readonly isAnonymous: boolean;\n readonly user: AuthUser;\n readonly claims?: Record<string, unknown>;\n readonly expiresAt?: number;\n readonly scopes: string[];\n readonly authorizedProviders: Record<string, ProviderSnapshot>;\n readonly authorizedProviderIds: string[];\n readonly authorizedApps: Record<string, { id: string; toolIds: string[] }>;\n readonly authorizedAppIds: string[];\n readonly authorizedTools: Record<string, AuthorizedTool>;\n readonly authorizedToolIds: string[];\n readonly authorizedPrompts: Record<string, AuthorizedPrompt>;\n readonly authorizedPromptIds: string[];\n readonly authorizedResources: string[];\n\n /** The original bearer token (for transparent mode) */\n protected readonly token?: string;\n\n /** Active transport sessions for this authorization */\n readonly #sessions: Map<string, TransportSession> = new Map();\n\n /** Creation timestamp */\n readonly createdAt: number;\n\n protected constructor(ctx: AuthorizationCreateCtx) {\n this.id = ctx.id;\n this.isAnonymous = ctx.isAnonymous;\n this.user = ctx.user;\n this.claims = ctx.claims;\n this.expiresAt = ctx.expiresAt;\n this.scopes = ctx.scopes ?? [];\n this.token = ctx.token;\n this.createdAt = Date.now();\n\n // Initialize authorization projections\n this.authorizedProviders = ctx.authorizedProviders ?? {};\n this.authorizedProviderIds = ctx.authorizedProviderIds ?? Object.keys(this.authorizedProviders);\n this.authorizedApps = ctx.authorizedApps ?? {};\n this.authorizedAppIds = ctx.authorizedAppIds ?? Object.keys(this.authorizedApps);\n this.authorizedTools = ctx.authorizedTools ?? {};\n this.authorizedToolIds = ctx.authorizedToolIds ?? Object.keys(this.authorizedTools);\n this.authorizedPrompts = ctx.authorizedPrompts ?? {};\n this.authorizedPromptIds = ctx.authorizedPromptIds ?? Object.keys(this.authorizedPrompts);\n this.authorizedResources = ctx.authorizedResources ?? [];\n }\n\n /**\n * Create a new transport session for this authorization\n * @param protocol - Transport protocol (sse, streamable-http, etc.)\n * @param fingerprint - Optional client fingerprint for tracking\n */\n createTransportSession(protocol: TransportProtocol, fingerprint?: string): TransportSession {\n const sessionId = randomUUID();\n\n const session: TransportSession = {\n id: sessionId,\n authorizationId: this.id,\n protocol,\n createdAt: Date.now(),\n expiresAt: this.expiresAt,\n nodeId: getMachineId(),\n clientFingerprint: fingerprint,\n };\n\n this.#sessions.set(session.id, session);\n return session;\n }\n\n /**\n * Get existing transport session by ID\n */\n getTransportSession(sessionId: string): TransportSession | undefined {\n return this.#sessions.get(sessionId);\n }\n\n /**\n * Get all active transport sessions\n */\n getAllSessions(): TransportSession[] {\n return Array.from(this.#sessions.values());\n }\n\n /**\n * Remove a transport session\n */\n removeTransportSession(sessionId: string): boolean {\n return this.#sessions.delete(sessionId);\n }\n\n /**\n * Get count of active sessions\n */\n get sessionCount(): number {\n return this.#sessions.size;\n }\n\n /**\n * Abstract: Get access token for a provider\n * Implementation varies by mode:\n * - Public: throws (no tokens)\n * - Transparent: returns the original bearer token\n * - Orchestrated: retrieves from vault/store\n */\n abstract getToken(providerId?: string): Promise<string>;\n\n /**\n * Check if a scope is granted\n */\n hasScope(scope: string): boolean {\n return this.scopes.includes(scope);\n }\n\n /**\n * Check if all scopes are granted\n */\n hasAllScopes(scopes: string[]): boolean {\n return scopes.every((s) => this.scopes.includes(s));\n }\n\n /**\n * Check if any scope is granted\n */\n hasAnyScope(scopes: string[]): boolean {\n return scopes.some((s) => this.scopes.includes(s));\n }\n\n /**\n * Check if a tool is authorized\n */\n canAccessTool(toolId: string): boolean {\n return toolId in this.authorizedTools || this.authorizedToolIds.includes(toolId);\n }\n\n /**\n * Check if a prompt is authorized\n */\n canAccessPrompt(promptId: string): boolean {\n return promptId in this.authorizedPrompts || this.authorizedPromptIds.includes(promptId);\n }\n\n /**\n * Check if an app is authorized.\n * Used for progressive authorization to determine if tools from this app can be executed.\n * @param appId - App ID to check\n */\n isAppAuthorized(appId: string): boolean {\n return appId in this.authorizedApps || this.authorizedAppIds.includes(appId);\n }\n\n /**\n * Build URL for progressive/incremental authorization.\n * Used when a tool requires authorization for an app that was skipped during initial auth.\n * @param appId - App ID that requires authorization\n * @param baseUrl - Base URL of the server\n */\n getProgressiveAuthUrl(appId: string, baseUrl: string): string {\n return `${baseUrl}/oauth/authorize?app=${encodeURIComponent(appId)}&mode=incremental`;\n }\n\n /**\n * Check if the authorization is expired\n */\n isExpired(): boolean {\n if (!this.expiresAt) return false;\n return Date.now() > this.expiresAt;\n }\n\n /**\n * Get time until expiration in milliseconds\n * Returns undefined if no expiration, negative if expired\n */\n getTimeToExpiry(): number | undefined {\n if (!this.expiresAt) return undefined;\n return this.expiresAt - Date.now();\n }\n\n /**\n * Convert a transport session to encrypted session JWT\n * This is what gets sent in the Mcp-Session-Id header\n */\n toSessionJwt(session: TransportSession): string {\n const payload: SessionJwtPayload = {\n sid: session.id,\n aid: this.id,\n proto: session.protocol,\n nid: session.nodeId,\n iat: Math.floor(Date.now() / 1000),\n exp: this.expiresAt ? Math.floor(this.expiresAt / 1000) : undefined,\n };\n return encryptJson(payload);\n }\n\n /**\n * Convert to LLM-safe context (no tokens exposed)\n */\n toLLMSafeContext(session: TransportSession): LLMSafeAuthContext {\n return {\n authorizationId: this.id,\n sessionId: session.id,\n mode: this.mode,\n isAnonymous: this.isAnonymous,\n user: {\n sub: this.user.sub,\n name: this.user.name,\n },\n scopes: this.scopes,\n authorizedToolIds: this.authorizedToolIds,\n authorizedPromptIds: this.authorizedPromptIds,\n };\n }\n\n /**\n * Validate that no tokens are leaked in data\n * Throws if JWT pattern detected\n */\n static validateNoTokenLeakage(data: unknown): void {\n const json = JSON.stringify(data);\n // Detect JWT pattern (header.payload.signature)\n if (/eyJ[A-Za-z0-9_-]+\\.eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+/.test(json)) {\n throw new Error('SECURITY: Token detected in data - potential LLM context leak');\n }\n // Detect sensitive field names\n const sensitiveFields = ['access_token', 'refresh_token', 'id_token', 'tokenEnc', 'secretRefId'];\n for (const field of sensitiveFields) {\n if (json.includes(`\"${field}\"`)) {\n throw new Error(`SECURITY: Sensitive field \"${field}\" detected - potential leak`);\n }\n }\n }\n}\n"]}
|
|
@@ -10,6 +10,7 @@ declare const stateSchema: z.ZodObject<{
|
|
|
10
10
|
token: z.ZodOptional<z.ZodString>;
|
|
11
11
|
sessionIdHeader: z.ZodOptional<z.ZodString>;
|
|
12
12
|
sessionProtocol: z.ZodOptional<z.ZodString>;
|
|
13
|
+
userAgent: z.ZodOptional<z.ZodString>;
|
|
13
14
|
prmMetadataPath: z.ZodOptional<z.ZodString>;
|
|
14
15
|
prmMetadataHeader: z.ZodOptional<z.ZodString>;
|
|
15
16
|
jwtPayload: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
|
|
@@ -10,6 +10,7 @@ const auth_token_utils_1 = require("../session/utils/auth-token.utils");
|
|
|
10
10
|
const jwks_1 = require("../jwks");
|
|
11
11
|
const session_id_utils_1 = require("../session/utils/session-id.utils");
|
|
12
12
|
const authorization_1 = require("../authorization");
|
|
13
|
+
const notification_service_1 = require("../../notification/notification.service");
|
|
13
14
|
const inputSchema = common_1.httpRequestInputSchema;
|
|
14
15
|
const stateSchema = zod_1.z.object({
|
|
15
16
|
baseUrl: zod_1.z.string().min(1),
|
|
@@ -17,6 +18,7 @@ const stateSchema = zod_1.z.object({
|
|
|
17
18
|
token: zod_1.z.string().optional(),
|
|
18
19
|
sessionIdHeader: zod_1.z.string().optional(), // 'mcp-session-id'
|
|
19
20
|
sessionProtocol: zod_1.z.string().optional(), // 'sse/http/streamable-http'
|
|
21
|
+
userAgent: zod_1.z.string().optional(), // User-Agent header for platform detection
|
|
20
22
|
prmMetadataPath: zod_1.z.string().optional(),
|
|
21
23
|
prmMetadataHeader: zod_1.z.string().optional(),
|
|
22
24
|
jwtPayload: zod_1.z.object({}).passthrough().optional(),
|
|
@@ -63,6 +65,7 @@ let SessionVerifyFlow = class SessionVerifyFlow extends common_1.FlowBase {
|
|
|
63
65
|
? 'sse'
|
|
64
66
|
: undefined;
|
|
65
67
|
const token = (0, auth_token_utils_1.extractBearerToken)(authorizationHeader);
|
|
68
|
+
const userAgent = request.headers?.['user-agent'] ?? undefined;
|
|
66
69
|
const prmMetadataPath = `/.well-known/oauth-protected-resource${entryPath}${routeBase}`;
|
|
67
70
|
const prmMetadataHeader = `Bearer resource_metadata="${baseUrl}${prmMetadataPath}"`;
|
|
68
71
|
this.state.set({
|
|
@@ -71,6 +74,7 @@ let SessionVerifyFlow = class SessionVerifyFlow extends common_1.FlowBase {
|
|
|
71
74
|
token,
|
|
72
75
|
sessionIdHeader,
|
|
73
76
|
sessionProtocol,
|
|
77
|
+
userAgent,
|
|
74
78
|
prmMetadataPath,
|
|
75
79
|
prmMetadataHeader,
|
|
76
80
|
});
|
|
@@ -108,6 +112,7 @@ let SessionVerifyFlow = class SessionVerifyFlow extends common_1.FlowBase {
|
|
|
108
112
|
// ALWAYS use client's session ID, regardless of validation result.
|
|
109
113
|
// If payload is valid and nodeId matches, include payload for protocol detection.
|
|
110
114
|
// If validation failed, transport layer will handle the error appropriately.
|
|
115
|
+
const finalPayload = existingPayload && existingPayload.nodeId === machineId ? existingPayload : undefined;
|
|
111
116
|
this.respond({
|
|
112
117
|
kind: 'authorized',
|
|
113
118
|
authorization: {
|
|
@@ -115,7 +120,7 @@ let SessionVerifyFlow = class SessionVerifyFlow extends common_1.FlowBase {
|
|
|
115
120
|
user,
|
|
116
121
|
session: {
|
|
117
122
|
id: sessionIdHeader, // ← CRITICAL: Always use client's session ID
|
|
118
|
-
payload:
|
|
123
|
+
payload: finalPayload,
|
|
119
124
|
},
|
|
120
125
|
},
|
|
121
126
|
});
|
|
@@ -126,13 +131,18 @@ let SessionVerifyFlow = class SessionVerifyFlow extends common_1.FlowBase {
|
|
|
126
131
|
const now = Date.now();
|
|
127
132
|
const user = { sub: `anon:${crypto.randomUUID()}`, iss: 'public', name: 'Anonymous' };
|
|
128
133
|
const uuid = crypto.randomUUID();
|
|
134
|
+
// Detect platform from User-Agent header for UI rendering support
|
|
135
|
+
const platformDetectionConfig = this.scope.metadata.transport?.platformDetection;
|
|
136
|
+
const platformType = (0, notification_service_1.detectPlatformFromUserAgent)(this.state.userAgent, platformDetectionConfig);
|
|
129
137
|
// Create a valid session payload matching the SessionIdPayload schema
|
|
138
|
+
// Include platformType if detected (non-unknown) for Tool UI support
|
|
130
139
|
const payload = {
|
|
131
140
|
uuid,
|
|
132
141
|
nodeId: machineId,
|
|
133
142
|
authSig: 'public',
|
|
134
143
|
iat: Math.floor(now / 1000),
|
|
135
144
|
isPublic: true,
|
|
145
|
+
...(platformType !== 'unknown' && { platformType }),
|
|
136
146
|
};
|
|
137
147
|
const sessionId = (0, session_id_utils_1.encryptJson)(payload);
|
|
138
148
|
this.respond({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.verify.flow.js","sourceRoot":"","sources":["../../../../src/auth/flows/session.verify.flow.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/flows/session.verify.flow.ts\nimport {\n authorizationSchema,\n Flow,\n FlowBase,\n FlowRunOptions,\n StageHookOf,\n userClaimSchema,\n sessionIdSchema,\n httpRequestInputSchema,\n FlowPlan,\n AuthOptions,\n isTransparentMode,\n isPublicMode,\n TransparentAuthOptions,\n getRequestBaseUrl,\n normalizeEntryPrefix,\n normalizeScopeBase,\n} from '../../common';\nimport 'reflect-metadata';\nimport { z } from 'zod';\nimport { deriveTypedUser, extractBearerToken, isJwt } from '../session/utils/auth-token.utils';\nimport { JwksService, ProviderVerifyRef, VerifyResult } from '../jwks';\nimport { parseSessionHeader, encryptJson, decryptPublicSession } from '../session/utils/session-id.utils';\nimport { getMachineId } from '../authorization';\n\nconst inputSchema = httpRequestInputSchema;\n\nconst stateSchema = z.object({\n baseUrl: z.string().min(1),\n authorizationHeader: z.string().optional(),\n token: z.string().optional(),\n sessionIdHeader: z.string().optional(), // 'mcp-session-id'\n sessionProtocol: z.string().optional(), // 'sse/http/streamable-http'\n prmMetadataPath: z.string().optional(),\n prmMetadataHeader: z.string().optional(),\n jwtPayload: z.object({}).passthrough().optional(),\n user: userClaimSchema.optional(),\n session: sessionIdSchema.optional(),\n});\n\nconst UnauthorizedSchema = z\n .object({\n kind: z.literal('unauthorized'),\n prmMetadataHeader: z.string().describe('Path to protected resource metadata'),\n })\n .describe(\"401 Unauthorized with 'WWW-Authenticate' header for requesting authentication\");\n\nconst AuthorizedSchema = z\n .object({\n kind: z.literal('authorized'),\n authorization: authorizationSchema.describe('Session information if session id is present'),\n })\n .describe('Authorized session information');\n\nexport const sessionVerifyOutputSchema = z.union([UnauthorizedSchema, AuthorizedSchema]);\n\nconst plan = {\n pre: ['parseInput', 'handlePublicMode', 'requireAuthorizationHeader', 'verifyIfJwt'],\n execute: ['deriveUser', 'parseSessionHeader', 'buildAuthorizedOutput'],\n} as const satisfies FlowPlan<string>;\n\ndeclare global {\n interface ExtendFlows {\n 'session:verify': FlowRunOptions<\n SessionVerifyFlow,\n typeof plan,\n typeof inputSchema,\n typeof sessionVerifyOutputSchema,\n typeof stateSchema\n >;\n }\n}\n\nconst name = 'session:verify' as const;\nconst Stage = StageHookOf(name);\n\n@Flow({\n name,\n plan,\n inputSchema,\n outputSchema: sessionVerifyOutputSchema,\n access: 'authorized',\n})\nexport default class SessionVerifyFlow extends FlowBase<typeof name> {\n @Stage('parseInput')\n async parseInput() {\n const { request } = this.rawInput;\n const entryPath = normalizeEntryPrefix(this.scope.entryPath);\n const routeBase = normalizeScopeBase(this.scope.routeBase);\n const baseUrl = getRequestBaseUrl(request, entryPath);\n\n const authorizationHeader = (request.headers?.['authorization'] as string | undefined) ?? undefined;\n const httpTransportHeader = request.headers?.['http-transport'] as string | undefined;\n const sessionIdRawHeader = request.headers?.['mcp-session-id'] as string | undefined;\n const sessionIdQuery = request.query['sessionId'] as string | undefined;\n\n const sessionIdHeader = sessionIdRawHeader ?? sessionIdQuery ?? undefined;\n // Use sessionIdRawHeader (not sessionIdHeader) to distinguish header vs query param\n // sessionIdHeader is the merged value, but we need to know the source for protocol selection\n const sessionProtocol = httpTransportHeader\n ? 'http'\n : sessionIdRawHeader\n ? 'streamable-http'\n : sessionIdQuery\n ? 'sse'\n : undefined;\n\n const token = extractBearerToken(authorizationHeader);\n\n const prmMetadataPath = `/.well-known/oauth-protected-resource${entryPath}${routeBase}`;\n const prmMetadataHeader = `Bearer resource_metadata=\"${baseUrl}${prmMetadataPath}\"`;\n\n this.state.set({\n baseUrl,\n authorizationHeader,\n token,\n sessionIdHeader,\n sessionProtocol,\n prmMetadataPath,\n prmMetadataHeader,\n });\n }\n\n /**\n * Handle public mode - allow anonymous access without requiring authorization\n * In public mode, we create an anonymous authorization with a stateful session\n * but NO token. This allows public docs/CI to work without Authorization header.\n *\n * CRITICAL: When client sends mcp-session-id header, we MUST use that exact ID\n * for transport registry lookup. Creating a new session ID would cause mismatch.\n */\n @Stage('handlePublicMode')\n async handlePublicMode() {\n const authOptions = this.scope.auth?.options;\n\n // Skip if not public mode or if authorization header is present (authenticated public)\n if (!authOptions || !isPublicMode(authOptions)) {\n return;\n }\n\n // If token is present, let the normal verification flow handle it\n if (this.state.token) {\n return;\n }\n\n const sessionIdHeader = this.state.sessionIdHeader;\n const machineId = getMachineId();\n\n // CRITICAL: If client sent session ID, ALWAYS use it for transport lookup.\n // The transport registry uses this ID as the key. Creating a different ID\n // would cause \"session not initialized\" error.\n if (sessionIdHeader) {\n // Try to decrypt/validate for payload extraction (optional - for nodeId validation)\n const existingPayload = decryptPublicSession(sessionIdHeader);\n\n // Determine user based on whether we could extract payload\n const user = existingPayload\n ? { sub: `anon:${existingPayload.iat * 1000}`, iss: 'public', name: 'Anonymous' }\n : { sub: `anon:${crypto.randomUUID()}`, iss: 'public', name: 'Anonymous' };\n\n // ALWAYS use client's session ID, regardless of validation result.\n // If payload is valid and nodeId matches, include payload for protocol detection.\n // If validation failed, transport layer will handle the error appropriately.\n this.respond({\n kind: 'authorized',\n authorization: {\n token: '',\n user,\n session: {\n id: sessionIdHeader, // ← CRITICAL: Always use client's session ID\n payload: existingPayload && existingPayload.nodeId === machineId ? existingPayload : undefined,\n },\n },\n });\n return;\n }\n\n // No session header → create new session (initialize request)\n // For new sessions, don't pre-determine protocol. Let transport handler detect it.\n const now = Date.now();\n const user = { sub: `anon:${crypto.randomUUID()}`, iss: 'public', name: 'Anonymous' };\n const uuid = crypto.randomUUID();\n\n // Create a valid session payload matching the SessionIdPayload schema\n const payload = {\n uuid,\n nodeId: machineId,\n authSig: 'public',\n iat: Math.floor(now / 1000),\n isPublic: true,\n };\n\n const sessionId = encryptJson(payload);\n\n this.respond({\n kind: 'authorized',\n authorization: {\n token: '',\n user,\n session: { id: sessionId, payload },\n },\n });\n }\n\n @Stage('requireAuthorizationHeader', {\n filter: ({ state }) => !state.authorizationHeader,\n })\n async requireAuthorizationOrChallenge() {\n this.respond({\n kind: 'unauthorized',\n prmMetadataHeader: this.state.required.prmMetadataHeader,\n });\n }\n\n /**\n * If Authorization is a JWT:\n * - Attempt verification against any known / cached public keys we have (gateway/local)\n * - If verification fails → 401\n * - If verification ok → capture payload\n * If NOT a JWT:\n * - we do NOT attempt verification, just pass the raw token through\n */\n @Stage('verifyIfJwt')\n async verifyIfJwt() {\n const jwks = this.get(JwksService); // TODO: fix providers\n const token = this.state.required.token;\n\n if (!isJwt(token)) {\n // Non-JWT tokens are not supported - require JWT for verification\n this.respond({\n kind: 'unauthorized',\n prmMetadataHeader: this.state.required.prmMetadataHeader,\n });\n return;\n }\n\n // Best-effort verification using locally known keys (gateway/local provider cache).\n // Add defensive null check for this.scope.auth (consistent with line 130)\n const auth = this.scope.auth;\n if (!auth) {\n this.respond({\n kind: 'unauthorized',\n prmMetadataHeader: this.state.required.prmMetadataHeader,\n });\n return;\n }\n\n let verify: Promise<VerifyResult>;\n const authOptions = auth.options;\n\n // Transparent mode uses remote provider's keys, all other modes use local keys\n if (isTransparentMode(authOptions)) {\n const primary = authOptions as TransparentAuthOptions;\n const issuer = auth.issuer;\n const providerRefs: ProviderVerifyRef[] = [\n {\n id: primary.remote.id ?? 'default',\n issuerUrl: issuer,\n jwks: primary.remote.jwks,\n jwksUri: primary.remote.jwksUri,\n },\n ];\n verify = jwks.verifyTransparentToken(token, providerRefs);\n } else {\n // Public or orchestrated mode - verify against local gateway keys\n verify = jwks.verifyGatewayToken(token, this.state.required.baseUrl);\n }\n\n const result = await verify;\n\n if (result.ok) {\n this.state.set({ jwtPayload: result.payload });\n return;\n }\n this.respond({\n kind: 'unauthorized',\n prmMetadataHeader: this.state.required.prmMetadataHeader,\n });\n }\n\n @Stage('deriveUser')\n async deriveUser() {\n this.state.set('user', deriveTypedUser(this.state.required.jwtPayload ?? {}));\n }\n\n /**\n * Parse the session header (mcp-session-id)\n * - If session id is present, validate it\n * - If valid, capture the session info\n * - If NOT valid, ignore (no session)\n */\n @Stage('parseSessionHeader')\n async parseSessionHeader() {\n const {\n sessionIdHeader,\n required: { token },\n } = this.state;\n\n const session = parseSessionHeader(sessionIdHeader, token);\n if (session) {\n this.state.set('session', session);\n }\n }\n\n @Stage('buildAuthorizedOutput')\n async buildAuthorizedOutput() {\n const {\n required: { token, user },\n session,\n } = this.state;\n\n this.respond({\n kind: 'authorized',\n authorization: {\n token,\n user,\n session,\n },\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session.verify.flow.js","sourceRoot":"","sources":["../../../../src/auth/flows/session.verify.flow.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/flows/session.verify.flow.ts\nimport {\n authorizationSchema,\n Flow,\n FlowBase,\n FlowRunOptions,\n StageHookOf,\n userClaimSchema,\n sessionIdSchema,\n httpRequestInputSchema,\n FlowPlan,\n AuthOptions,\n isTransparentMode,\n isPublicMode,\n TransparentAuthOptions,\n getRequestBaseUrl,\n normalizeEntryPrefix,\n normalizeScopeBase,\n} from '../../common';\nimport 'reflect-metadata';\nimport { z } from 'zod';\nimport { deriveTypedUser, extractBearerToken, isJwt } from '../session/utils/auth-token.utils';\nimport { JwksService, ProviderVerifyRef, VerifyResult } from '../jwks';\nimport { parseSessionHeader, encryptJson, decryptPublicSession } from '../session/utils/session-id.utils';\nimport { getMachineId } from '../authorization';\nimport { detectPlatformFromUserAgent } from '../../notification/notification.service';\n\nconst inputSchema = httpRequestInputSchema;\n\nconst stateSchema = z.object({\n baseUrl: z.string().min(1),\n authorizationHeader: z.string().optional(),\n token: z.string().optional(),\n sessionIdHeader: z.string().optional(), // 'mcp-session-id'\n sessionProtocol: z.string().optional(), // 'sse/http/streamable-http'\n userAgent: z.string().optional(), // User-Agent header for platform detection\n prmMetadataPath: z.string().optional(),\n prmMetadataHeader: z.string().optional(),\n jwtPayload: z.object({}).passthrough().optional(),\n user: userClaimSchema.optional(),\n session: sessionIdSchema.optional(),\n});\n\nconst UnauthorizedSchema = z\n .object({\n kind: z.literal('unauthorized'),\n prmMetadataHeader: z.string().describe('Path to protected resource metadata'),\n })\n .describe(\"401 Unauthorized with 'WWW-Authenticate' header for requesting authentication\");\n\nconst AuthorizedSchema = z\n .object({\n kind: z.literal('authorized'),\n authorization: authorizationSchema.describe('Session information if session id is present'),\n })\n .describe('Authorized session information');\n\nexport const sessionVerifyOutputSchema = z.union([UnauthorizedSchema, AuthorizedSchema]);\n\nconst plan = {\n pre: ['parseInput', 'handlePublicMode', 'requireAuthorizationHeader', 'verifyIfJwt'],\n execute: ['deriveUser', 'parseSessionHeader', 'buildAuthorizedOutput'],\n} as const satisfies FlowPlan<string>;\n\ndeclare global {\n interface ExtendFlows {\n 'session:verify': FlowRunOptions<\n SessionVerifyFlow,\n typeof plan,\n typeof inputSchema,\n typeof sessionVerifyOutputSchema,\n typeof stateSchema\n >;\n }\n}\n\nconst name = 'session:verify' as const;\nconst Stage = StageHookOf(name);\n\n@Flow({\n name,\n plan,\n inputSchema,\n outputSchema: sessionVerifyOutputSchema,\n access: 'authorized',\n})\nexport default class SessionVerifyFlow extends FlowBase<typeof name> {\n @Stage('parseInput')\n async parseInput() {\n const { request } = this.rawInput;\n const entryPath = normalizeEntryPrefix(this.scope.entryPath);\n const routeBase = normalizeScopeBase(this.scope.routeBase);\n const baseUrl = getRequestBaseUrl(request, entryPath);\n\n const authorizationHeader = (request.headers?.['authorization'] as string | undefined) ?? undefined;\n const httpTransportHeader = request.headers?.['http-transport'] as string | undefined;\n const sessionIdRawHeader = request.headers?.['mcp-session-id'] as string | undefined;\n const sessionIdQuery = request.query['sessionId'] as string | undefined;\n\n const sessionIdHeader = sessionIdRawHeader ?? sessionIdQuery ?? undefined;\n // Use sessionIdRawHeader (not sessionIdHeader) to distinguish header vs query param\n // sessionIdHeader is the merged value, but we need to know the source for protocol selection\n const sessionProtocol = httpTransportHeader\n ? 'http'\n : sessionIdRawHeader\n ? 'streamable-http'\n : sessionIdQuery\n ? 'sse'\n : undefined;\n\n const token = extractBearerToken(authorizationHeader);\n const userAgent = (request.headers?.['user-agent'] as string | undefined) ?? undefined;\n\n const prmMetadataPath = `/.well-known/oauth-protected-resource${entryPath}${routeBase}`;\n const prmMetadataHeader = `Bearer resource_metadata=\"${baseUrl}${prmMetadataPath}\"`;\n\n this.state.set({\n baseUrl,\n authorizationHeader,\n token,\n sessionIdHeader,\n sessionProtocol,\n userAgent,\n prmMetadataPath,\n prmMetadataHeader,\n });\n }\n\n /**\n * Handle public mode - allow anonymous access without requiring authorization\n * In public mode, we create an anonymous authorization with a stateful session\n * but NO token. This allows public docs/CI to work without Authorization header.\n *\n * CRITICAL: When client sends mcp-session-id header, we MUST use that exact ID\n * for transport registry lookup. Creating a new session ID would cause mismatch.\n */\n @Stage('handlePublicMode')\n async handlePublicMode() {\n const authOptions = this.scope.auth?.options;\n\n // Skip if not public mode or if authorization header is present (authenticated public)\n if (!authOptions || !isPublicMode(authOptions)) {\n return;\n }\n\n // If token is present, let the normal verification flow handle it\n if (this.state.token) {\n return;\n }\n\n const sessionIdHeader = this.state.sessionIdHeader;\n const machineId = getMachineId();\n\n // CRITICAL: If client sent session ID, ALWAYS use it for transport lookup.\n // The transport registry uses this ID as the key. Creating a different ID\n // would cause \"session not initialized\" error.\n if (sessionIdHeader) {\n // Try to decrypt/validate for payload extraction (optional - for nodeId validation)\n const existingPayload = decryptPublicSession(sessionIdHeader);\n\n // Determine user based on whether we could extract payload\n const user = existingPayload\n ? { sub: `anon:${existingPayload.iat * 1000}`, iss: 'public', name: 'Anonymous' }\n : { sub: `anon:${crypto.randomUUID()}`, iss: 'public', name: 'Anonymous' };\n\n // ALWAYS use client's session ID, regardless of validation result.\n // If payload is valid and nodeId matches, include payload for protocol detection.\n // If validation failed, transport layer will handle the error appropriately.\n const finalPayload = existingPayload && existingPayload.nodeId === machineId ? existingPayload : undefined;\n\n this.respond({\n kind: 'authorized',\n authorization: {\n token: '',\n user,\n session: {\n id: sessionIdHeader, // ← CRITICAL: Always use client's session ID\n payload: finalPayload,\n },\n },\n });\n return;\n }\n\n // No session header → create new session (initialize request)\n // For new sessions, don't pre-determine protocol. Let transport handler detect it.\n const now = Date.now();\n const user = { sub: `anon:${crypto.randomUUID()}`, iss: 'public', name: 'Anonymous' };\n const uuid = crypto.randomUUID();\n\n // Detect platform from User-Agent header for UI rendering support\n const platformDetectionConfig = this.scope.metadata.transport?.platformDetection;\n const platformType = detectPlatformFromUserAgent(this.state.userAgent, platformDetectionConfig);\n\n // Create a valid session payload matching the SessionIdPayload schema\n // Include platformType if detected (non-unknown) for Tool UI support\n const payload = {\n uuid,\n nodeId: machineId,\n authSig: 'public',\n iat: Math.floor(now / 1000),\n isPublic: true,\n ...(platformType !== 'unknown' && { platformType }),\n };\n\n const sessionId = encryptJson(payload);\n\n this.respond({\n kind: 'authorized',\n authorization: {\n token: '',\n user,\n session: { id: sessionId, payload },\n },\n });\n }\n\n @Stage('requireAuthorizationHeader', {\n filter: ({ state }) => !state.authorizationHeader,\n })\n async requireAuthorizationOrChallenge() {\n this.respond({\n kind: 'unauthorized',\n prmMetadataHeader: this.state.required.prmMetadataHeader,\n });\n }\n\n /**\n * If Authorization is a JWT:\n * - Attempt verification against any known / cached public keys we have (gateway/local)\n * - If verification fails → 401\n * - If verification ok → capture payload\n * If NOT a JWT:\n * - we do NOT attempt verification, just pass the raw token through\n */\n @Stage('verifyIfJwt')\n async verifyIfJwt() {\n const jwks = this.get(JwksService); // TODO: fix providers\n const token = this.state.required.token;\n\n if (!isJwt(token)) {\n // Non-JWT tokens are not supported - require JWT for verification\n this.respond({\n kind: 'unauthorized',\n prmMetadataHeader: this.state.required.prmMetadataHeader,\n });\n return;\n }\n\n // Best-effort verification using locally known keys (gateway/local provider cache).\n // Add defensive null check for this.scope.auth (consistent with line 130)\n const auth = this.scope.auth;\n if (!auth) {\n this.respond({\n kind: 'unauthorized',\n prmMetadataHeader: this.state.required.prmMetadataHeader,\n });\n return;\n }\n\n let verify: Promise<VerifyResult>;\n const authOptions = auth.options;\n\n // Transparent mode uses remote provider's keys, all other modes use local keys\n if (isTransparentMode(authOptions)) {\n const primary = authOptions as TransparentAuthOptions;\n const issuer = auth.issuer;\n const providerRefs: ProviderVerifyRef[] = [\n {\n id: primary.remote.id ?? 'default',\n issuerUrl: issuer,\n jwks: primary.remote.jwks,\n jwksUri: primary.remote.jwksUri,\n },\n ];\n verify = jwks.verifyTransparentToken(token, providerRefs);\n } else {\n // Public or orchestrated mode - verify against local gateway keys\n verify = jwks.verifyGatewayToken(token, this.state.required.baseUrl);\n }\n\n const result = await verify;\n\n if (result.ok) {\n this.state.set({ jwtPayload: result.payload });\n return;\n }\n this.respond({\n kind: 'unauthorized',\n prmMetadataHeader: this.state.required.prmMetadataHeader,\n });\n }\n\n @Stage('deriveUser')\n async deriveUser() {\n this.state.set('user', deriveTypedUser(this.state.required.jwtPayload ?? {}));\n }\n\n /**\n * Parse the session header (mcp-session-id)\n * - If session id is present, validate it\n * - If valid, capture the session info\n * - If NOT valid, ignore (no session)\n */\n @Stage('parseSessionHeader')\n async parseSessionHeader() {\n const {\n sessionIdHeader,\n required: { token },\n } = this.state;\n\n const session = parseSessionHeader(sessionIdHeader, token);\n if (session) {\n this.state.set('session', session);\n }\n }\n\n @Stage('buildAuthorizedOutput')\n async buildAuthorizedOutput() {\n const {\n required: { token, user },\n session,\n } = this.state;\n\n this.respond({\n kind: 'authorized',\n authorization: {\n token,\n user,\n session,\n },\n });\n }\n}\n"]}
|
|
@@ -31,7 +31,7 @@ let WellKnownJwksFlow = class WellKnownJwksFlow extends common_1.FlowBase {
|
|
|
31
31
|
const jwksSvc = this.get(jwks_1.JwksService);
|
|
32
32
|
// Orchestrated gateway → serve own JWKS
|
|
33
33
|
if (isOrchestrated) {
|
|
34
|
-
const keysDoc = jwksSvc.getPublicJwks();
|
|
34
|
+
const keysDoc = await jwksSvc.getPublicJwks();
|
|
35
35
|
if (!keysDoc?.keys || !Array.isArray(keysDoc.keys)) {
|
|
36
36
|
throw new Error('orchestrator jwks not available');
|
|
37
37
|
}
|
|
@@ -51,7 +51,7 @@ let WellKnownJwksFlow = class WellKnownJwksFlow extends common_1.FlowBase {
|
|
|
51
51
|
}
|
|
52
52
|
else {
|
|
53
53
|
// Public or orchestrated mode - serve local JWKS
|
|
54
|
-
const keysDoc = jwksSvc.getPublicJwks();
|
|
54
|
+
const keysDoc = await jwksSvc.getPublicJwks();
|
|
55
55
|
if (keysDoc?.keys && Array.isArray(keysDoc.keys)) {
|
|
56
56
|
this.respond(common_1.httpRespond.json(keysDoc));
|
|
57
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"well-known.jwks.flow.js","sourceRoot":"","sources":["../../../../src/auth/flows/well-known.jwks.flow.ts"],"names":[],"mappings":";;;AAAA,qCAAqC;AACrC,yCAesB;AACtB,4BAA0B;AAC1B,6BAAwB;AACxB,kCAAsC;AAEtC,MAAM,WAAW,GAAG,wBAAe,CAAC;AAEpC,MAAM,WAAW,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3B,cAAc,EAAE,OAAC,CAAC,OAAO,EAAE;CAC5B,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,OAAC,CAAC,KAAK,CAAC,CAAC,uBAAc,EAAE,uBAAc,EAAE,2BAAkB,CAAC,CAAC,CAAC;AAEnF,MAAM,IAAI,GAAG;IACX,GAAG,EAAE,CAAC,YAAY,EAAE,eAAe,CAAC;IACpC,OAAO,EAAE,CAAC,aAAa,CAAC;CACW,CAAC;AActC,MAAM,IAAI,GAAG,iBAA0B,CAAC;AACxC,MAAM,KAAK,GAAG,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;AAYjB,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,iBAAqB;IAClE,MAAM,CAAC,WAAW,CAAC,OAAsB,EAAE,KAAiB;QAC1D,OAAO,IAAA,2BAAkB,EAAC,WAAW,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7F,CAAC;IAGK,AAAN,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YACb,cAAc,EAAE,KAAK,EAAE,gCAAgC;SACxD,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,WAAW;QACf,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAW,CAAC,CAAC;QAEtC,wCAAwC;QACxC,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"well-known.jwks.flow.js","sourceRoot":"","sources":["../../../../src/auth/flows/well-known.jwks.flow.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/flows/well-known.jwks.flow.ts\nimport {\n Flow,\n FlowBase,\n FlowPlan,\n FlowRunOptions,\n httpInputSchema,\n HttpJsonSchema,\n HttpRedirectSchema,\n httpRespond,\n HttpTextSchema,\n ScopeEntry,\n ServerRequest,\n StageHookOf,\n isTransparentMode,\n makeWellKnownPaths,\n} from '../../common';\nimport 'reflect-metadata';\nimport { z } from 'zod';\nimport { JwksService } from '../jwks';\n\nconst inputSchema = httpInputSchema;\n\nconst stateSchema = z.object({\n isOrchestrated: z.boolean(),\n});\n\nconst outputSchema = z.union([HttpJsonSchema, HttpTextSchema, HttpRedirectSchema]);\n\nconst plan = {\n pre: ['parseInput', 'validateInput'],\n execute: ['collectData'],\n} as const satisfies FlowPlan<string>;\n\ndeclare global {\n interface ExtendFlows {\n 'well-known.jwks': FlowRunOptions<\n WellKnownJwksFlow,\n typeof plan,\n typeof inputSchema,\n typeof outputSchema,\n typeof stateSchema\n >;\n }\n}\n\nconst name = 'well-known.jwks' as const;\nconst Stage = StageHookOf(name);\n\n@Flow({\n name,\n plan,\n inputSchema,\n outputSchema,\n access: 'public',\n middleware: {\n method: 'GET',\n },\n})\nexport default class WellKnownJwksFlow extends FlowBase<typeof name> {\n static canActivate(request: ServerRequest, scope: ScopeEntry) {\n return makeWellKnownPaths('jwks.json', scope.entryPath, scope.routeBase).has(request.path);\n }\n\n @Stage('parseInput')\n async parseInput() {\n this.state.set({\n isOrchestrated: false, // scope.orchestrated, TODO: fix\n });\n }\n\n @Stage('collectData')\n async collectData() {\n const { isOrchestrated } = this.state.required;\n const jwksSvc = this.get(JwksService);\n\n // Orchestrated gateway → serve own JWKS\n if (isOrchestrated) {\n const keysDoc = await jwksSvc.getPublicJwks();\n if (!keysDoc?.keys || !Array.isArray(keysDoc.keys)) {\n throw new Error('orchestrator jwks not available');\n }\n this.respond(httpRespond.json(keysDoc));\n return;\n }\n\n const options = this.scope.auth.options;\n if (options && isTransparentMode(options)) {\n // Transparent mode - use remote provider's JWKS\n if (options.remote.jwks && options.remote.jwks.keys.length) {\n this.respond(httpRespond.json(options.remote.jwks));\n } else {\n const location = options.remote.jwksUri ?? `${options.remote.provider}/.well-known/jwks.json`;\n this.respond(httpRespond.redirect(location));\n }\n } else {\n // Public or orchestrated mode - serve local JWKS\n const keysDoc = await jwksSvc.getPublicJwks();\n if (keysDoc?.keys && Array.isArray(keysDoc.keys)) {\n this.respond(httpRespond.json(keysDoc));\n } else {\n this.respond(httpRespond.notFound());\n }\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { JSONWebKeySet } from 'jose';
|
|
2
|
+
/**
|
|
3
|
+
* Data structure for persisted development keys
|
|
4
|
+
*/
|
|
5
|
+
export interface DevKeyData {
|
|
6
|
+
/** Key ID (kid) */
|
|
7
|
+
kid: string;
|
|
8
|
+
/** Private key in JWK format (portable) */
|
|
9
|
+
privateKey: JsonWebKey;
|
|
10
|
+
/** Public JWKS for verification */
|
|
11
|
+
publicJwk: JSONWebKeySet;
|
|
12
|
+
/** Key creation timestamp (ms) */
|
|
13
|
+
createdAt: number;
|
|
14
|
+
/** Algorithm used */
|
|
15
|
+
alg: 'RS256' | 'ES256';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Options for dev key persistence
|
|
19
|
+
*/
|
|
20
|
+
export interface DevKeyPersistenceOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Path to store dev keys
|
|
23
|
+
* @default '.frontmcp/dev-keys.json'
|
|
24
|
+
*/
|
|
25
|
+
keyPath?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Enable persistence in production (NOT RECOMMENDED)
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
forceEnable?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if dev key persistence is enabled based on environment and options
|
|
34
|
+
*/
|
|
35
|
+
export declare function isDevKeyPersistenceEnabled(options?: DevKeyPersistenceOptions): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the key file path
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveKeyPath(options?: DevKeyPersistenceOptions): string;
|
|
40
|
+
/**
|
|
41
|
+
* Load persisted dev key from file
|
|
42
|
+
*
|
|
43
|
+
* @param options - Persistence options
|
|
44
|
+
* @returns The loaded key data or null if not found/invalid
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadDevKey(options?: DevKeyPersistenceOptions): Promise<DevKeyData | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Save dev key to file
|
|
49
|
+
*
|
|
50
|
+
* Uses atomic write (temp file + rename) to prevent corruption.
|
|
51
|
+
* Sets file permissions to 0o600 (owner read/write only) for security.
|
|
52
|
+
*
|
|
53
|
+
* @param keyData - Key data to persist
|
|
54
|
+
* @param options - Persistence options
|
|
55
|
+
* @returns true if save succeeded, false otherwise
|
|
56
|
+
*/
|
|
57
|
+
export declare function saveDevKey(keyData: DevKeyData, options?: DevKeyPersistenceOptions): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* Delete persisted dev key
|
|
60
|
+
*
|
|
61
|
+
* @param options - Persistence options
|
|
62
|
+
*/
|
|
63
|
+
export declare function deleteDevKey(options?: DevKeyPersistenceOptions): Promise<void>;
|