mcp-trellis 0.1.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/LICENSE +21 -0
- package/README.md +595 -0
- package/dist/adapters/node.d.ts +47 -0
- package/dist/adapters/node.d.ts.map +1 -0
- package/dist/adapters/node.js +116 -0
- package/dist/adapters/node.js.map +1 -0
- package/dist/app.d.ts +69 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +101 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/bearer.d.ts +21 -0
- package/dist/auth/bearer.d.ts.map +1 -0
- package/dist/auth/bearer.js +32 -0
- package/dist/auth/bearer.js.map +1 -0
- package/dist/clients.d.ts +33 -0
- package/dist/clients.d.ts.map +1 -0
- package/dist/clients.js +63 -0
- package/dist/clients.js.map +1 -0
- package/dist/dispatch.d.ts +7 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +79 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/http.d.ts +17 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +38 -0
- package/dist/http.js.map +1 -0
- package/dist/jsonrpc.d.ts +32 -0
- package/dist/jsonrpc.d.ts.map +1 -0
- package/dist/jsonrpc.js +17 -0
- package/dist/jsonrpc.js.map +1 -0
- package/dist/methods.d.ts +52 -0
- package/dist/methods.d.ts.map +1 -0
- package/dist/methods.js +114 -0
- package/dist/methods.js.map +1 -0
- package/dist/oauth/authorize.d.ts +3 -0
- package/dist/oauth/authorize.d.ts.map +1 -0
- package/dist/oauth/authorize.js +109 -0
- package/dist/oauth/authorize.js.map +1 -0
- package/dist/oauth/base64url.d.ts +5 -0
- package/dist/oauth/base64url.d.ts.map +1 -0
- package/dist/oauth/base64url.js +17 -0
- package/dist/oauth/base64url.js.map +1 -0
- package/dist/oauth/clientauth.d.ts +19 -0
- package/dist/oauth/clientauth.d.ts.map +1 -0
- package/dist/oauth/clientauth.js +76 -0
- package/dist/oauth/clientauth.js.map +1 -0
- package/dist/oauth/codes.d.ts +30 -0
- package/dist/oauth/codes.d.ts.map +1 -0
- package/dist/oauth/codes.js +93 -0
- package/dist/oauth/codes.js.map +1 -0
- package/dist/oauth/constants.d.ts +32 -0
- package/dist/oauth/constants.d.ts.map +1 -0
- package/dist/oauth/constants.js +30 -0
- package/dist/oauth/constants.js.map +1 -0
- package/dist/oauth/metadata.d.ts +45 -0
- package/dist/oauth/metadata.d.ts.map +1 -0
- package/dist/oauth/metadata.js +36 -0
- package/dist/oauth/metadata.js.map +1 -0
- package/dist/oauth/pkce.d.ts +4 -0
- package/dist/oauth/pkce.d.ts.map +1 -0
- package/dist/oauth/pkce.js +17 -0
- package/dist/oauth/pkce.js.map +1 -0
- package/dist/oauth/redirect.d.ts +11 -0
- package/dist/oauth/redirect.d.ts.map +1 -0
- package/dist/oauth/redirect.js +26 -0
- package/dist/oauth/redirect.js.map +1 -0
- package/dist/oauth/resource.d.ts +7 -0
- package/dist/oauth/resource.d.ts.map +1 -0
- package/dist/oauth/resource.js +44 -0
- package/dist/oauth/resource.js.map +1 -0
- package/dist/oauth/revoke.d.ts +4 -0
- package/dist/oauth/revoke.d.ts.map +1 -0
- package/dist/oauth/revoke.js +30 -0
- package/dist/oauth/revoke.js.map +1 -0
- package/dist/oauth/router.d.ts +18 -0
- package/dist/oauth/router.d.ts.map +1 -0
- package/dist/oauth/router.js +105 -0
- package/dist/oauth/router.js.map +1 -0
- package/dist/oauth/scope.d.ts +11 -0
- package/dist/oauth/scope.d.ts.map +1 -0
- package/dist/oauth/scope.js +24 -0
- package/dist/oauth/scope.js.map +1 -0
- package/dist/oauth/token.d.ts +3 -0
- package/dist/oauth/token.d.ts.map +1 -0
- package/dist/oauth/token.js +137 -0
- package/dist/oauth/token.js.map +1 -0
- package/dist/oauth/types.d.ts +115 -0
- package/dist/oauth/types.d.ts.map +1 -0
- package/dist/oauth/types.js +28 -0
- package/dist/oauth/types.js.map +1 -0
- package/dist/oauth/wellknown.d.ts +8 -0
- package/dist/oauth/wellknown.d.ts.map +1 -0
- package/dist/oauth/wellknown.js +35 -0
- package/dist/oauth/wellknown.js.map +1 -0
- package/dist/protocol.d.ts +4 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +14 -0
- package/dist/protocol.js.map +1 -0
- package/dist/registry.d.ts +37 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +61 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +11 -0
- package/dist/server.js.map +1 -0
- package/dist/tools.d.ts +80 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +97 -0
- package/dist/tools.js.map +1 -0
- package/dist/validate.d.ts +23 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +88 -0
- package/dist/validate.js.map +1 -0
- package/docs/diagrams/architecture.svg +1 -0
- package/docs/diagrams/first-connection.svg +1 -0
- package/docs/diagrams/ports.svg +1 -0
- package/package.json +68 -0
package/dist/http.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** JSON `error` body / audit reason when a host port throws. */
|
|
2
|
+
export const INTERNAL_ERROR = "internal_error";
|
|
3
|
+
export const corsHeaders = (extra) => ({
|
|
4
|
+
"Access-Control-Allow-Origin": "*",
|
|
5
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
6
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, MCP-Protocol-Version",
|
|
7
|
+
...extra,
|
|
8
|
+
});
|
|
9
|
+
export const jsonResponse = (data, status = 200, extraHeaders, options) => {
|
|
10
|
+
const cors = options?.cors !== false;
|
|
11
|
+
return new Response(JSON.stringify(data), {
|
|
12
|
+
status,
|
|
13
|
+
headers: {
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
"Cache-Control": "no-store",
|
|
16
|
+
...(cors ? corsHeaders() : {}),
|
|
17
|
+
...extraHeaders,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
export const emptyResponse = (status) => new Response(null, { status, headers: corsHeaders() });
|
|
22
|
+
export const optionsResponse = (options) => new Response(null, {
|
|
23
|
+
status: 204,
|
|
24
|
+
headers: options?.cors === false ? { "Cache-Control": "no-store" } : corsHeaders(),
|
|
25
|
+
});
|
|
26
|
+
export const methodNotAllowed = (message = "Method not allowed", options) => jsonResponse({ error: message }, 405, undefined, options);
|
|
27
|
+
/**
|
|
28
|
+
* OPTIONS → 204; allowed verb → null (continue); otherwise → 405.
|
|
29
|
+
* Shared by OAuth handlers that all need the same method gate.
|
|
30
|
+
*/
|
|
31
|
+
export const requireHttpMethod = (request, allowed, options) => {
|
|
32
|
+
if (request.method === "OPTIONS")
|
|
33
|
+
return optionsResponse(options);
|
|
34
|
+
if (allowed.has(request.method))
|
|
35
|
+
return null;
|
|
36
|
+
return jsonResponse({ detail: "method not allowed" }, 405, undefined, options);
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,KAA8B,EACN,EAAE,CAAC,CAAC;IAC5B,6BAA6B,EAAE,GAAG;IAClC,8BAA8B,EAAE,oBAAoB;IACpD,8BAA8B,EAC5B,mDAAmD;IACrD,GAAG,KAAK;CACT,CAAC,CAAC;AAOH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,IAAa,EACb,MAAM,GAAG,GAAG,EACZ,YAAqC,EACrC,OAA6B,EACnB,EAAE;IACZ,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,KAAK,KAAK,CAAC;IACrC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,UAAU;YAC3B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9B,GAAG,YAAY;SAChB;KACF,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAc,EAAY,EAAE,CACxD,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;AAEzD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAA6B,EAAY,EAAE,CACzE,IAAI,QAAQ,CAAC,IAAI,EAAE;IACjB,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,OAAO,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE;CACnF,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,OAAO,GAAG,oBAAoB,EAC9B,OAA6B,EACnB,EAAE,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAEzE;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,OAAgB,EAChB,OAA4B,EAC5B,OAA6B,EACZ,EAAE;IACnB,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,YAAY,CACjB,EAAE,MAAM,EAAE,oBAAoB,EAAE,EAChC,GAAG,EACH,SAAS,EACT,OAAO,CACR,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type JsonRpcId = string | number | null;
|
|
2
|
+
export type JsonRpcRequest = {
|
|
3
|
+
jsonrpc?: string;
|
|
4
|
+
id?: JsonRpcId;
|
|
5
|
+
method?: string;
|
|
6
|
+
params?: Record<string, unknown>;
|
|
7
|
+
result?: unknown;
|
|
8
|
+
error?: unknown;
|
|
9
|
+
};
|
|
10
|
+
export type JsonRpcSuccess = {
|
|
11
|
+
jsonrpc: "2.0";
|
|
12
|
+
id: JsonRpcId;
|
|
13
|
+
result: unknown;
|
|
14
|
+
};
|
|
15
|
+
export type JsonRpcFailure = {
|
|
16
|
+
jsonrpc: "2.0";
|
|
17
|
+
id: JsonRpcId;
|
|
18
|
+
error: {
|
|
19
|
+
code: number;
|
|
20
|
+
message: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export type JsonRpcResponse = JsonRpcSuccess | JsonRpcFailure;
|
|
24
|
+
export declare const JSONRPC_PARSE_ERROR = -32700;
|
|
25
|
+
export declare const JSONRPC_INVALID_REQUEST = -32600;
|
|
26
|
+
export declare const JSONRPC_METHOD_NOT_FOUND = -32601;
|
|
27
|
+
export declare const JSONRPC_INVALID_PARAMS = -32602;
|
|
28
|
+
export declare const JSONRPC_INTERNAL_ERROR = -32603;
|
|
29
|
+
export declare const JSONRPC_UNAUTHORIZED = -32001;
|
|
30
|
+
export declare const rpcResult: (id: JsonRpcId, result: unknown) => JsonRpcSuccess;
|
|
31
|
+
export declare const rpcError: (id: JsonRpcId, code: number, message: string) => JsonRpcFailure;
|
|
32
|
+
//# sourceMappingURL=jsonrpc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.d.ts","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAE/C,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,cAAc,CAAC;AAE9D,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAC1C,eAAO,MAAM,uBAAuB,SAAS,CAAC;AAC9C,eAAO,MAAM,wBAAwB,SAAS,CAAC;AAC/C,eAAO,MAAM,sBAAsB,SAAS,CAAC;AAC7C,eAAO,MAAM,sBAAsB,SAAS,CAAC;AAC7C,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAE3C,eAAO,MAAM,SAAS,GACpB,IAAI,SAAS,EACb,QAAQ,OAAO,KACd,cAID,CAAC;AAEH,eAAO,MAAM,QAAQ,GACnB,IAAI,SAAS,EACb,MAAM,MAAM,EACZ,SAAS,MAAM,KACd,cAID,CAAC"}
|
package/dist/jsonrpc.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const JSONRPC_PARSE_ERROR = -32700;
|
|
2
|
+
export const JSONRPC_INVALID_REQUEST = -32600;
|
|
3
|
+
export const JSONRPC_METHOD_NOT_FOUND = -32601;
|
|
4
|
+
export const JSONRPC_INVALID_PARAMS = -32602;
|
|
5
|
+
export const JSONRPC_INTERNAL_ERROR = -32603;
|
|
6
|
+
export const JSONRPC_UNAUTHORIZED = -32001;
|
|
7
|
+
export const rpcResult = (id, result) => ({
|
|
8
|
+
jsonrpc: "2.0",
|
|
9
|
+
id,
|
|
10
|
+
result,
|
|
11
|
+
});
|
|
12
|
+
export const rpcError = (id, code, message) => ({
|
|
13
|
+
jsonrpc: "2.0",
|
|
14
|
+
id,
|
|
15
|
+
error: { code, message },
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=jsonrpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.js","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAyBA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,KAAK,CAAC;AAC1C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,KAAK,CAAC;AAC9C,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAK,CAAC;AAC/C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAAK,CAAC;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAAK,CAAC;AAC7C,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAK,CAAC;AAE3C,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,EAAa,EACb,MAAe,EACC,EAAE,CAAC,CAAC;IACpB,OAAO,EAAE,KAAK;IACd,EAAE;IACF,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,EAAa,EACb,IAAY,EACZ,OAAe,EACC,EAAE,CAAC,CAAC;IACpB,OAAO,EAAE,KAAK;IACd,EAAE;IACF,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;CACzB,CAAC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type JsonRpcRequest } from "./jsonrpc.js";
|
|
2
|
+
import type { ToolRegistry } from "./registry.js";
|
|
3
|
+
import { type WwwAuthenticateOptions } from "./auth/bearer.js";
|
|
4
|
+
export type Principal = {
|
|
5
|
+
id: string;
|
|
6
|
+
scopes: string[];
|
|
7
|
+
};
|
|
8
|
+
export type AuditEntry = {
|
|
9
|
+
/** JSON-RPC method, or `""` for transport-level rejections made before parsing. */
|
|
10
|
+
method: string;
|
|
11
|
+
tool?: string;
|
|
12
|
+
principalId?: string;
|
|
13
|
+
ok: boolean;
|
|
14
|
+
error?: string;
|
|
15
|
+
durationMs: number;
|
|
16
|
+
};
|
|
17
|
+
export type McpPorts<TCtx> = {
|
|
18
|
+
authenticate: (req: Request, method: string, tool?: string) => Promise<Principal | null>;
|
|
19
|
+
context: (req: Request, principal: Principal | null) => TCtx | Promise<TCtx>;
|
|
20
|
+
audit?: (entry: AuditEntry) => void | Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
export type ServerInfo = {
|
|
23
|
+
name: string;
|
|
24
|
+
version: string;
|
|
25
|
+
};
|
|
26
|
+
export type McpHandlerOptions<TCtx> = {
|
|
27
|
+
registry: ToolRegistry<TCtx>;
|
|
28
|
+
ports: McpPorts<TCtx>;
|
|
29
|
+
serverInfo: ServerInfo;
|
|
30
|
+
instructions?: string;
|
|
31
|
+
wwwAuthenticate: WwwAuthenticateOptions | ((req: Request) => WwwAuthenticateOptions);
|
|
32
|
+
/** Methods that skip auth (default: initialize, ping, notifications/*). */
|
|
33
|
+
publicMethods?: Set<string>;
|
|
34
|
+
};
|
|
35
|
+
export declare const DEFAULT_PUBLIC_METHODS: Set<string>;
|
|
36
|
+
export declare const isNotification: (body: JsonRpcRequest) => boolean;
|
|
37
|
+
export declare const resolveWww: <TCtx>(options: McpHandlerOptions<TCtx>, req: Request) => WwwAuthenticateOptions;
|
|
38
|
+
export declare const unauthorized: (www: WwwAuthenticateOptions, message?: string) => Response;
|
|
39
|
+
/**
|
|
40
|
+
* Invoke the audit port, swallowing any failure.
|
|
41
|
+
* Telemetry must never turn a served response into a transport error —
|
|
42
|
+
* this is the only call path to `ports.audit`.
|
|
43
|
+
*/
|
|
44
|
+
export declare const safeAudit: <TCtx>(options: McpHandlerOptions<TCtx>, entry: AuditEntry) => Promise<void>;
|
|
45
|
+
export declare const hasScope: (principal: Principal, scope: string | undefined) => boolean;
|
|
46
|
+
export declare const dispatchRpc: <TCtx>(input: {
|
|
47
|
+
req: Request;
|
|
48
|
+
body: JsonRpcRequest;
|
|
49
|
+
options: McpHandlerOptions<TCtx>;
|
|
50
|
+
publicMethods: Set<string>;
|
|
51
|
+
}) => Promise<Response>;
|
|
52
|
+
//# sourceMappingURL=methods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methods.d.ts","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EAGpB,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,IAAI,IAAI;IAC3B,YAAY,EAAE,CACZ,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,KACV,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC/B,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,IAAI,IAAI;IACpC,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EACX,sBAAsB,GACtB,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,sBAAsB,CAAC,CAAC;IAC/C,2EAA2E;IAC3E,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,sBAAsB,aAKjC,CAAC;AAUH,eAAO,MAAM,cAAc,GAAI,MAAM,cAAc,KAAG,OACQ,CAAC;AAE/D,eAAO,MAAM,UAAU,GAAI,IAAI,EAC7B,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAChC,KAAK,OAAO,KACX,sBAG0B,CAAC;AAE9B,eAAO,MAAM,YAAY,GACvB,KAAK,sBAAsB,EAC3B,gBAAwB,KACvB,QAGC,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAU,IAAI,EAClC,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAChC,OAAO,UAAU,KAChB,OAAO,CAAC,IAAI,CAMd,CAAC;AAEF,eAAO,MAAM,QAAQ,GACnB,WAAW,SAAS,EACpB,OAAO,MAAM,GAAG,SAAS,KACxB,OAGF,CAAC;AAoEF,eAAO,MAAM,WAAW,GAAU,IAAI,EAAE,OAAO;IAC7C,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACjC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B,KAAG,OAAO,CAAC,QAAQ,CAsCnB,CAAC"}
|
package/dist/methods.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { JSONRPC_METHOD_NOT_FOUND, JSONRPC_UNAUTHORIZED, rpcError, rpcResult, } from "./jsonrpc.js";
|
|
2
|
+
import { emptyResponse, jsonResponse } from "./http.js";
|
|
3
|
+
import { pickProtocolVersion } from "./protocol.js";
|
|
4
|
+
import { wwwAuthenticateHeader, } from "./auth/bearer.js";
|
|
5
|
+
export const DEFAULT_PUBLIC_METHODS = new Set([
|
|
6
|
+
"initialize",
|
|
7
|
+
"ping",
|
|
8
|
+
"notifications/initialized",
|
|
9
|
+
"initialized",
|
|
10
|
+
]);
|
|
11
|
+
const NOTIFICATION_PREDICATES = [
|
|
12
|
+
(body) => typeof body.method === "string" && body.method.startsWith("notifications/"),
|
|
13
|
+
(body) => body.method === "initialized",
|
|
14
|
+
(body) => body.id === undefined && body.method === undefined && "result" in body,
|
|
15
|
+
];
|
|
16
|
+
export const isNotification = (body) => NOTIFICATION_PREDICATES.some((predicate) => predicate(body));
|
|
17
|
+
export const resolveWww = (options, req) => typeof options.wwwAuthenticate === "function"
|
|
18
|
+
? options.wwwAuthenticate(req)
|
|
19
|
+
: options.wwwAuthenticate;
|
|
20
|
+
export const unauthorized = (www, message = "Unauthorized") => jsonResponse(rpcError(null, JSONRPC_UNAUTHORIZED, message), 401, {
|
|
21
|
+
"WWW-Authenticate": wwwAuthenticateHeader(www),
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Invoke the audit port, swallowing any failure.
|
|
25
|
+
* Telemetry must never turn a served response into a transport error —
|
|
26
|
+
* this is the only call path to `ports.audit`.
|
|
27
|
+
*/
|
|
28
|
+
export const safeAudit = async (options, entry) => {
|
|
29
|
+
try {
|
|
30
|
+
await options.ports.audit?.(entry);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Intentionally ignored.
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export const hasScope = (principal, scope) => {
|
|
37
|
+
if (!scope)
|
|
38
|
+
return true;
|
|
39
|
+
return principal.scopes.includes(scope) || principal.scopes.includes("*");
|
|
40
|
+
};
|
|
41
|
+
const METHODS = {
|
|
42
|
+
initialize: async ({ id, body, options }) => jsonResponse(rpcResult(id, {
|
|
43
|
+
protocolVersion: pickProtocolVersion(body.params),
|
|
44
|
+
capabilities: { tools: {} },
|
|
45
|
+
serverInfo: options.serverInfo,
|
|
46
|
+
instructions: options.instructions ?? "",
|
|
47
|
+
})),
|
|
48
|
+
ping: async ({ id }) => jsonResponse(rpcResult(id, {})),
|
|
49
|
+
"tools/list": async ({ id, options }) => jsonResponse(rpcResult(id, { tools: options.registry.list() })),
|
|
50
|
+
"tools/call": async ({ req, body, id, principal, ctx, options }) => {
|
|
51
|
+
const params = body.params ?? {};
|
|
52
|
+
const name = String(params.name ?? "");
|
|
53
|
+
const rawArgs = params.arguments;
|
|
54
|
+
const args = rawArgs !== null &&
|
|
55
|
+
typeof rawArgs === "object" &&
|
|
56
|
+
!Array.isArray(rawArgs)
|
|
57
|
+
? rawArgs
|
|
58
|
+
: {};
|
|
59
|
+
const tool = options.registry.get(name);
|
|
60
|
+
if (tool?.scope && (!principal || !hasScope(principal, tool.scope))) {
|
|
61
|
+
await safeAudit(options, {
|
|
62
|
+
method: "tools/call",
|
|
63
|
+
tool: name,
|
|
64
|
+
principalId: principal?.id,
|
|
65
|
+
ok: false,
|
|
66
|
+
error: "missing_scope",
|
|
67
|
+
durationMs: 0,
|
|
68
|
+
});
|
|
69
|
+
return unauthorized(resolveWww(options, req), `Missing scope: ${tool.scope}`);
|
|
70
|
+
}
|
|
71
|
+
const started = Date.now();
|
|
72
|
+
const result = await options.registry.call(name, ctx, args);
|
|
73
|
+
await safeAudit(options, {
|
|
74
|
+
method: "tools/call",
|
|
75
|
+
tool: name,
|
|
76
|
+
principalId: principal?.id,
|
|
77
|
+
ok: !result.isError,
|
|
78
|
+
error: result.isError ? result.content[0]?.text : undefined,
|
|
79
|
+
durationMs: Date.now() - started,
|
|
80
|
+
});
|
|
81
|
+
return jsonResponse(rpcResult(id, result));
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
export const dispatchRpc = async (input) => {
|
|
85
|
+
const { req, body, options, publicMethods } = input;
|
|
86
|
+
const method = body.method ?? "";
|
|
87
|
+
// Notifications omit `id`; explicit null is a valid request id (JSON-RPC).
|
|
88
|
+
const id = body.id === undefined ? null : body.id;
|
|
89
|
+
const toolName = method === "tools/call" ? String(body.params?.name ?? "") : undefined;
|
|
90
|
+
const isPublic = publicMethods.has(method) || method.startsWith("notifications/");
|
|
91
|
+
let principal = null;
|
|
92
|
+
if (!isPublic) {
|
|
93
|
+
principal = await options.ports.authenticate(req, method, toolName);
|
|
94
|
+
if (!principal) {
|
|
95
|
+
await safeAudit(options, {
|
|
96
|
+
method,
|
|
97
|
+
tool: toolName,
|
|
98
|
+
ok: false,
|
|
99
|
+
error: "unauthorized",
|
|
100
|
+
durationMs: 0,
|
|
101
|
+
});
|
|
102
|
+
return unauthorized(resolveWww(options, req));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const handler = METHODS[method];
|
|
106
|
+
if (!handler) {
|
|
107
|
+
if (body.id === undefined)
|
|
108
|
+
return emptyResponse(202);
|
|
109
|
+
return jsonResponse(rpcError(body.id, JSONRPC_METHOD_NOT_FOUND, `Method not found: ${method}`));
|
|
110
|
+
}
|
|
111
|
+
const ctx = await options.ports.context(req, principal);
|
|
112
|
+
return handler({ req, body, id, principal, ctx, options });
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=methods.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methods.js","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../src/oauth/authorize.ts"],"names":[],"mappings":"AAYA,OAAO,EAKL,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAC;AAmDpB,eAAO,MAAM,eAAe,GAC1B,SAAS,OAAO,EAChB,SAAS,kBAAkB,KAC1B,OAAO,CAAC,QAAQ,CAoFlB,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { requireHttpMethod } from "../http.js";
|
|
2
|
+
import { issueAuthCode } from "./codes.js";
|
|
3
|
+
import { OAUTH_ERRORS } from "./constants.js";
|
|
4
|
+
import { isAllowedRedirectUri, } from "./redirect.js";
|
|
5
|
+
import { canonicalResource, firstResourceError, } from "./resource.js";
|
|
6
|
+
import { firstScopeError, formatScope, requestedScopes } from "./scope.js";
|
|
7
|
+
import { advertisedScopes, oauthError, unregisteredClientError, resolveSecret, } from "./types.js";
|
|
8
|
+
const GET_ONLY = new Set(["GET"]);
|
|
9
|
+
/**
|
|
10
|
+
* A pre-registered client is bound to its own redirect URIs.
|
|
11
|
+
* Only unknown (dynamic / public) clients fall back to the global allowlist.
|
|
12
|
+
*/
|
|
13
|
+
const redirectAllowed = (redirectUri, registered, allowlist) => {
|
|
14
|
+
if (!redirectUri)
|
|
15
|
+
return false;
|
|
16
|
+
if (registered)
|
|
17
|
+
return registered.redirectUris.includes(redirectUri);
|
|
18
|
+
return isAllowedRedirectUri(redirectUri, allowlist);
|
|
19
|
+
};
|
|
20
|
+
const firstAuthzError = (input) => {
|
|
21
|
+
const rule = [
|
|
22
|
+
{
|
|
23
|
+
ok: input.responseType === "code",
|
|
24
|
+
description: "response_type must be code",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
ok: redirectAllowed(input.redirectUri, input.registered, input.redirect),
|
|
28
|
+
description: "invalid redirect_uri",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
ok: Boolean(input.codeChallenge) && input.challengeMethod === "S256",
|
|
32
|
+
description: "PKCE S256 code_challenge required",
|
|
33
|
+
},
|
|
34
|
+
].find((entry) => !entry.ok);
|
|
35
|
+
if (rule) {
|
|
36
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 400, rule.description);
|
|
37
|
+
}
|
|
38
|
+
return firstResourceError(input.resource, input.expectedResource);
|
|
39
|
+
};
|
|
40
|
+
export const handleAuthorize = async (request, options) => {
|
|
41
|
+
const methodError = requireHttpMethod(request, GET_ONLY);
|
|
42
|
+
if (methodError)
|
|
43
|
+
return methodError;
|
|
44
|
+
const url = new URL(request.url);
|
|
45
|
+
const expectedResource = canonicalResource(url.origin, options.resourcePath);
|
|
46
|
+
const clientId = url.searchParams.get("client_id") ?? "";
|
|
47
|
+
if (!clientId) {
|
|
48
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 400, "client_id required");
|
|
49
|
+
}
|
|
50
|
+
const registered = (await options.ports.clientStore?.get(clientId)) ?? null;
|
|
51
|
+
if (!registered) {
|
|
52
|
+
const unregistered = unregisteredClientError(options, {
|
|
53
|
+
code: OAUTH_ERRORS.unauthorizedClient,
|
|
54
|
+
status: 400,
|
|
55
|
+
});
|
|
56
|
+
if (unregistered)
|
|
57
|
+
return unregistered;
|
|
58
|
+
}
|
|
59
|
+
const authzError = firstAuthzError({
|
|
60
|
+
responseType: url.searchParams.get("response_type") ?? "",
|
|
61
|
+
redirectUri: url.searchParams.get("redirect_uri") ?? "",
|
|
62
|
+
codeChallenge: url.searchParams.get("code_challenge") ?? "",
|
|
63
|
+
challengeMethod: url.searchParams.get("code_challenge_method") ?? "S256",
|
|
64
|
+
resource: url.searchParams.get("resource") ?? "",
|
|
65
|
+
expectedResource,
|
|
66
|
+
registered,
|
|
67
|
+
redirect: options.redirect,
|
|
68
|
+
});
|
|
69
|
+
if (authzError)
|
|
70
|
+
return authzError;
|
|
71
|
+
const advertised = advertisedScopes(options);
|
|
72
|
+
const granted = requestedScopes(url.searchParams.get("scope") ?? "", advertised);
|
|
73
|
+
const scopeError = firstScopeError(granted, advertised);
|
|
74
|
+
if (scopeError)
|
|
75
|
+
return scopeError;
|
|
76
|
+
const user = await options.ports.resolveUser(request);
|
|
77
|
+
if (!user) {
|
|
78
|
+
const next = `${url.pathname}${url.search}`;
|
|
79
|
+
const login = options.ports.loginUrl(request, next);
|
|
80
|
+
let loginTarget;
|
|
81
|
+
try {
|
|
82
|
+
loginTarget = new URL(login, url.origin);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 500, "loginUrl must be absolute or same-origin relative");
|
|
86
|
+
}
|
|
87
|
+
if (loginTarget.protocol !== "http:" && loginTarget.protocol !== "https:") {
|
|
88
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 500, "loginUrl must be absolute or same-origin relative");
|
|
89
|
+
}
|
|
90
|
+
return Response.redirect(loginTarget.toString(), 302);
|
|
91
|
+
}
|
|
92
|
+
const redirectUri = url.searchParams.get("redirect_uri") ?? "";
|
|
93
|
+
const secret = await resolveSecret(options.ports, request);
|
|
94
|
+
const code = await issueAuthCode(secret, {
|
|
95
|
+
clientId,
|
|
96
|
+
redirectUri,
|
|
97
|
+
codeChallenge: url.searchParams.get("code_challenge") ?? "",
|
|
98
|
+
userId: user.id,
|
|
99
|
+
resource: expectedResource,
|
|
100
|
+
scope: formatScope(granted),
|
|
101
|
+
});
|
|
102
|
+
const target = new URL(redirectUri);
|
|
103
|
+
target.searchParams.set("code", code);
|
|
104
|
+
const state = url.searchParams.get("state") ?? "";
|
|
105
|
+
if (state)
|
|
106
|
+
target.searchParams.set("state", state);
|
|
107
|
+
return Response.redirect(target.toString(), 302);
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=authorize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/oauth/authorize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Shared base64url helpers (WebCrypto / DOM — no Node Buffer). */
|
|
2
|
+
export declare const bytesToBase64Url: (bytes: ArrayBuffer | Uint8Array) => string;
|
|
3
|
+
export declare const textToBase64Url: (text: string) => string;
|
|
4
|
+
export declare const fromBase64Url: (text: string) => Uint8Array;
|
|
5
|
+
//# sourceMappingURL=base64url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64url.d.ts","sourceRoot":"","sources":["../../src/oauth/base64url.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,eAAO,MAAM,gBAAgB,GAAI,OAAO,WAAW,GAAG,UAAU,KAAG,MAOlE,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MACG,CAAC;AAEnD,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,UAM5C,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Shared base64url helpers (WebCrypto / DOM — no Node Buffer). */
|
|
2
|
+
export const bytesToBase64Url = (bytes) => {
|
|
3
|
+
const u8 = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
4
|
+
const binary = Array.from(u8, (byte) => String.fromCharCode(byte)).join("");
|
|
5
|
+
return btoa(binary)
|
|
6
|
+
.replace(/\+/g, "-")
|
|
7
|
+
.replace(/\//g, "_")
|
|
8
|
+
.replace(/=+$/, "");
|
|
9
|
+
};
|
|
10
|
+
export const textToBase64Url = (text) => bytesToBase64Url(new TextEncoder().encode(text));
|
|
11
|
+
export const fromBase64Url = (text) => {
|
|
12
|
+
const padded = text.replace(/-/g, "+").replace(/_/g, "/");
|
|
13
|
+
const pad = padded.length % 4 === 0 ? "" : "=".repeat(4 - (padded.length % 4));
|
|
14
|
+
const bin = atob(padded + pad);
|
|
15
|
+
return Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=base64url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64url.js","sourceRoot":"","sources":["../../src/oauth/base64url.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAA+B,EAAU,EAAE;IAC1E,MAAM,EAAE,GAAG,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,MAAM,CAAC;SAChB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAY,EAAU,EAAE,CACtD,gBAAgB,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AAEnD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAc,EAAE;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,GAAG,GACP,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IAC/B,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Token-endpoint client authentication (RFC 6749 §2.3.1). */
|
|
2
|
+
import { type TokenEndpointAuthMethod } from "./constants.js";
|
|
3
|
+
import { type OAuthRouterOptions } from "./types.js";
|
|
4
|
+
export type ClientAuth = {
|
|
5
|
+
clientId: string;
|
|
6
|
+
secret: string | null;
|
|
7
|
+
method: TokenEndpointAuthMethod;
|
|
8
|
+
};
|
|
9
|
+
/** Credentials from the Authorization header, else from the body. */
|
|
10
|
+
export declare const readClientAuth: (request: Request, body: Record<string, string>) => ClientAuth | null;
|
|
11
|
+
/**
|
|
12
|
+
* Enforce the registered auth method for known clients.
|
|
13
|
+
*
|
|
14
|
+
* An unknown client_id is treated as public — PKCE is its only proof —
|
|
15
|
+
* unless `allowUnregisteredClients` is false, in which case it is rejected
|
|
16
|
+
* rather than silently falling back to "no auth required".
|
|
17
|
+
*/
|
|
18
|
+
export declare const firstClientAuthError: (auth: ClientAuth, options: OAuthRouterOptions) => Promise<Response | null>;
|
|
19
|
+
//# sourceMappingURL=clientauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clientauth.d.ts","sourceRoot":"","sources":["../../src/oauth/clientauth.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAE9D,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,uBAAuB,CAAC;CACjC,CAAC;AAqBF,qEAAqE;AACrE,eAAO,MAAM,cAAc,GACzB,SAAS,OAAO,EAChB,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC3B,UAAU,GAAG,IAef,CAAC;AAKF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,UAAU,EAChB,SAAS,kBAAkB,KAC1B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAwBzB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Token-endpoint client authentication (RFC 6749 §2.3.1). */
|
|
2
|
+
import { OAUTH_ERRORS, TOKEN_ENDPOINT_AUTH_METHODS, } from "./constants.js";
|
|
3
|
+
import { oauthError, unregisteredClientError, } from "./types.js";
|
|
4
|
+
/** `Authorization: Basic base64(urlencode(id):urlencode(secret))`. */
|
|
5
|
+
const parseBasicAuth = (header) => {
|
|
6
|
+
if (!header?.startsWith("Basic "))
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
const decoded = atob(header.slice("Basic ".length).trim());
|
|
10
|
+
const separator = decoded.indexOf(":");
|
|
11
|
+
if (separator < 0)
|
|
12
|
+
return null;
|
|
13
|
+
const clientId = decodeURIComponent(decoded.slice(0, separator));
|
|
14
|
+
if (!clientId)
|
|
15
|
+
return null;
|
|
16
|
+
return {
|
|
17
|
+
clientId,
|
|
18
|
+
secret: decodeURIComponent(decoded.slice(separator + 1)),
|
|
19
|
+
method: TOKEN_ENDPOINT_AUTH_METHODS.basic,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
/** Credentials from the Authorization header, else from the body. */
|
|
27
|
+
export const readClientAuth = (request, body) => {
|
|
28
|
+
const basic = parseBasicAuth(request.headers.get("Authorization"));
|
|
29
|
+
if (basic)
|
|
30
|
+
return basic;
|
|
31
|
+
const clientId = body.client_id ?? "";
|
|
32
|
+
if (!clientId)
|
|
33
|
+
return null;
|
|
34
|
+
const secret = body.client_secret ?? "";
|
|
35
|
+
return {
|
|
36
|
+
clientId,
|
|
37
|
+
secret: secret.length > 0 ? secret : null,
|
|
38
|
+
method: secret.length > 0
|
|
39
|
+
? TOKEN_ENDPOINT_AUTH_METHODS.post
|
|
40
|
+
: TOKEN_ENDPOINT_AUTH_METHODS.none,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const invalidClient = (description) => oauthError(OAUTH_ERRORS.invalidClient, 401, description);
|
|
44
|
+
/**
|
|
45
|
+
* Enforce the registered auth method for known clients.
|
|
46
|
+
*
|
|
47
|
+
* An unknown client_id is treated as public — PKCE is its only proof —
|
|
48
|
+
* unless `allowUnregisteredClients` is false, in which case it is rejected
|
|
49
|
+
* rather than silently falling back to "no auth required".
|
|
50
|
+
*/
|
|
51
|
+
export const firstClientAuthError = async (auth, options) => {
|
|
52
|
+
const store = options.ports.clientStore;
|
|
53
|
+
const registered = (await store?.get(auth.clientId)) ?? null;
|
|
54
|
+
if (!registered) {
|
|
55
|
+
return unregisteredClientError(options, {
|
|
56
|
+
code: OAUTH_ERRORS.invalidClient,
|
|
57
|
+
status: 401,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const expected = registered.tokenEndpointAuthMethod;
|
|
61
|
+
if (expected === TOKEN_ENDPOINT_AUTH_METHODS.none)
|
|
62
|
+
return null;
|
|
63
|
+
if (auth.method !== expected) {
|
|
64
|
+
return invalidClient(`client requires ${expected}`);
|
|
65
|
+
}
|
|
66
|
+
if (!auth.secret)
|
|
67
|
+
return invalidClient("client_secret required");
|
|
68
|
+
if (!store?.verifySecret) {
|
|
69
|
+
return invalidClient("clientStore.verifySecret is not configured");
|
|
70
|
+
}
|
|
71
|
+
if (!(await store.verifySecret(auth.clientId, auth.secret))) {
|
|
72
|
+
return invalidClient("client authentication failed");
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=clientauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clientauth.js","sourceRoot":"","sources":["../../src/oauth/clientauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type AuthCodeRecord = {
|
|
2
|
+
clientId: string;
|
|
3
|
+
redirectUri: string;
|
|
4
|
+
codeChallenge: string;
|
|
5
|
+
/** Consenting user id — required so minted tokens are bound. */
|
|
6
|
+
userId: string;
|
|
7
|
+
/** RFC 8707 resource indicator — audience for the eventual access token. */
|
|
8
|
+
resource: string;
|
|
9
|
+
/** Space-delimited scope granted at authorize time. */
|
|
10
|
+
scope: string;
|
|
11
|
+
exp: number;
|
|
12
|
+
};
|
|
13
|
+
export type CodeStore = {
|
|
14
|
+
/**
|
|
15
|
+
* Mark a code jti as consumed; return false if already used.
|
|
16
|
+
* `expMs` is the code expiry (ms since epoch) for TTL-backed stores.
|
|
17
|
+
*/
|
|
18
|
+
consume: (jti: string, expMs: number) => Promise<boolean> | boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Issue an HMAC-signed auth code.
|
|
22
|
+
* Single-use via codeStore, or pruning in-memory Map when omitted.
|
|
23
|
+
*/
|
|
24
|
+
export declare const issueAuthCode: (secret: string, record: Omit<AuthCodeRecord, "exp">, nowMs?: number) => Promise<string>;
|
|
25
|
+
export declare const consumeAuthCode: (secret: string, code: string, options?: {
|
|
26
|
+
codeStore?: CodeStore;
|
|
27
|
+
nowMs?: number;
|
|
28
|
+
}) => Promise<AuthCodeRecord | null>;
|
|
29
|
+
export declare const newClientId: () => string;
|
|
30
|
+
//# sourceMappingURL=codes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../../src/oauth/codes.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB;;;OAGG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACrE,CAAC;AAkCF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,EACd,QAAQ,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,EACnC,cAAkB,KACjB,OAAO,CAAC,MAAM,CAShB,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,UAAS;IAAE,SAAS,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,KACtD,OAAO,CAAC,cAAc,GAAG,IAAI,CA8C/B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,MAA6B,CAAC"}
|