@mcp-z/client 1.0.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/AGENTS.md +159 -0
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/cjs/auth/capability-discovery.d.cts +25 -0
- package/dist/cjs/auth/capability-discovery.d.ts +25 -0
- package/dist/cjs/auth/capability-discovery.js +280 -0
- package/dist/cjs/auth/capability-discovery.js.map +1 -0
- package/dist/cjs/auth/index.d.cts +9 -0
- package/dist/cjs/auth/index.d.ts +9 -0
- package/dist/cjs/auth/index.js +28 -0
- package/dist/cjs/auth/index.js.map +1 -0
- package/dist/cjs/auth/interactive-oauth-flow.d.cts +58 -0
- package/dist/cjs/auth/interactive-oauth-flow.d.ts +58 -0
- package/dist/cjs/auth/interactive-oauth-flow.js +537 -0
- package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -0
- package/dist/cjs/auth/oauth-callback-listener.d.cts +56 -0
- package/dist/cjs/auth/oauth-callback-listener.d.ts +56 -0
- package/dist/cjs/auth/oauth-callback-listener.js +333 -0
- package/dist/cjs/auth/oauth-callback-listener.js.map +1 -0
- package/dist/cjs/auth/pkce.d.cts +17 -0
- package/dist/cjs/auth/pkce.d.ts +17 -0
- package/dist/cjs/auth/pkce.js +192 -0
- package/dist/cjs/auth/pkce.js.map +1 -0
- package/dist/cjs/auth/rfc9728-discovery.d.cts +34 -0
- package/dist/cjs/auth/rfc9728-discovery.d.ts +34 -0
- package/dist/cjs/auth/rfc9728-discovery.js +436 -0
- package/dist/cjs/auth/rfc9728-discovery.js.map +1 -0
- package/dist/cjs/auth/types.d.cts +137 -0
- package/dist/cjs/auth/types.d.ts +137 -0
- package/dist/cjs/auth/types.js +9 -0
- package/dist/cjs/auth/types.js.map +1 -0
- package/dist/cjs/client-helpers.d.cts +55 -0
- package/dist/cjs/client-helpers.d.ts +55 -0
- package/dist/cjs/client-helpers.js +128 -0
- package/dist/cjs/client-helpers.js.map +1 -0
- package/dist/cjs/config/server-loader.d.cts +27 -0
- package/dist/cjs/config/server-loader.d.ts +27 -0
- package/dist/cjs/config/server-loader.js +111 -0
- package/dist/cjs/config/server-loader.js.map +1 -0
- package/dist/cjs/config/validate-config.d.cts +15 -0
- package/dist/cjs/config/validate-config.d.ts +15 -0
- package/dist/cjs/config/validate-config.js +128 -0
- package/dist/cjs/config/validate-config.js.map +1 -0
- package/dist/cjs/connection/connect-client.d.cts +59 -0
- package/dist/cjs/connection/connect-client.d.ts +59 -0
- package/dist/cjs/connection/connect-client.js +536 -0
- package/dist/cjs/connection/connect-client.js.map +1 -0
- package/dist/cjs/connection/existing-process-transport.d.cts +40 -0
- package/dist/cjs/connection/existing-process-transport.d.ts +40 -0
- package/dist/cjs/connection/existing-process-transport.js +274 -0
- package/dist/cjs/connection/existing-process-transport.js.map +1 -0
- package/dist/cjs/connection/types.d.cts +61 -0
- package/dist/cjs/connection/types.d.ts +61 -0
- package/dist/cjs/connection/types.js +53 -0
- package/dist/cjs/connection/types.js.map +1 -0
- package/dist/cjs/connection/wait-for-http-ready.d.cts +15 -0
- package/dist/cjs/connection/wait-for-http-ready.d.ts +15 -0
- package/dist/cjs/connection/wait-for-http-ready.js +232 -0
- package/dist/cjs/connection/wait-for-http-ready.js.map +1 -0
- package/dist/cjs/dcr/dcr-authenticator.d.cts +73 -0
- package/dist/cjs/dcr/dcr-authenticator.d.ts +73 -0
- package/dist/cjs/dcr/dcr-authenticator.js +655 -0
- package/dist/cjs/dcr/dcr-authenticator.js.map +1 -0
- package/dist/cjs/dcr/dynamic-client-registrar.d.cts +28 -0
- package/dist/cjs/dcr/dynamic-client-registrar.d.ts +28 -0
- package/dist/cjs/dcr/dynamic-client-registrar.js +245 -0
- package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -0
- package/dist/cjs/dcr/index.d.cts +8 -0
- package/dist/cjs/dcr/index.d.ts +8 -0
- package/dist/cjs/dcr/index.js +24 -0
- package/dist/cjs/dcr/index.js.map +1 -0
- package/dist/cjs/index.d.cts +21 -0
- package/dist/cjs/index.d.ts +21 -0
- package/dist/cjs/index.js +94 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/monkey-patches.d.cts +6 -0
- package/dist/cjs/monkey-patches.d.ts +6 -0
- package/dist/cjs/monkey-patches.js +236 -0
- package/dist/cjs/monkey-patches.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/response-wrappers.d.cts +41 -0
- package/dist/cjs/response-wrappers.d.ts +41 -0
- package/dist/cjs/response-wrappers.js +443 -0
- package/dist/cjs/response-wrappers.js.map +1 -0
- package/dist/cjs/search/index.d.cts +6 -0
- package/dist/cjs/search/index.d.ts +6 -0
- package/dist/cjs/search/index.js +25 -0
- package/dist/cjs/search/index.js.map +1 -0
- package/dist/cjs/search/search.d.cts +22 -0
- package/dist/cjs/search/search.d.ts +22 -0
- package/dist/cjs/search/search.js +630 -0
- package/dist/cjs/search/search.js.map +1 -0
- package/dist/cjs/search/types.d.cts +122 -0
- package/dist/cjs/search/types.d.ts +122 -0
- package/dist/cjs/search/types.js +10 -0
- package/dist/cjs/search/types.js.map +1 -0
- package/dist/cjs/spawn/spawn-server.d.cts +83 -0
- package/dist/cjs/spawn/spawn-server.d.ts +83 -0
- package/dist/cjs/spawn/spawn-server.js +410 -0
- package/dist/cjs/spawn/spawn-server.js.map +1 -0
- package/dist/cjs/spawn/spawn-servers.d.cts +151 -0
- package/dist/cjs/spawn/spawn-servers.d.ts +151 -0
- package/dist/cjs/spawn/spawn-servers.js +911 -0
- package/dist/cjs/spawn/spawn-servers.js.map +1 -0
- package/dist/cjs/types.d.cts +11 -0
- package/dist/cjs/types.d.ts +11 -0
- package/dist/cjs/types.js +10 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils/logger.d.cts +24 -0
- package/dist/cjs/utils/logger.d.ts +24 -0
- package/dist/cjs/utils/logger.js +80 -0
- package/dist/cjs/utils/logger.js.map +1 -0
- package/dist/cjs/utils/path-utils.d.cts +45 -0
- package/dist/cjs/utils/path-utils.d.ts +45 -0
- package/dist/cjs/utils/path-utils.js +158 -0
- package/dist/cjs/utils/path-utils.js.map +1 -0
- package/dist/cjs/utils/sanitizer.d.cts +30 -0
- package/dist/cjs/utils/sanitizer.d.ts +30 -0
- package/dist/cjs/utils/sanitizer.js +124 -0
- package/dist/cjs/utils/sanitizer.js.map +1 -0
- package/dist/esm/auth/capability-discovery.d.ts +25 -0
- package/dist/esm/auth/capability-discovery.js +110 -0
- package/dist/esm/auth/capability-discovery.js.map +1 -0
- package/dist/esm/auth/index.d.ts +9 -0
- package/dist/esm/auth/index.js +6 -0
- package/dist/esm/auth/index.js.map +1 -0
- package/dist/esm/auth/interactive-oauth-flow.d.ts +58 -0
- package/dist/esm/auth/interactive-oauth-flow.js +217 -0
- package/dist/esm/auth/interactive-oauth-flow.js.map +1 -0
- package/dist/esm/auth/oauth-callback-listener.d.ts +56 -0
- package/dist/esm/auth/oauth-callback-listener.js +166 -0
- package/dist/esm/auth/oauth-callback-listener.js.map +1 -0
- package/dist/esm/auth/pkce.d.ts +17 -0
- package/dist/esm/auth/pkce.js +41 -0
- package/dist/esm/auth/pkce.js.map +1 -0
- package/dist/esm/auth/rfc9728-discovery.d.ts +34 -0
- package/dist/esm/auth/rfc9728-discovery.js +157 -0
- package/dist/esm/auth/rfc9728-discovery.js.map +1 -0
- package/dist/esm/auth/types.d.ts +137 -0
- package/dist/esm/auth/types.js +7 -0
- package/dist/esm/auth/types.js.map +1 -0
- package/dist/esm/client-helpers.d.ts +55 -0
- package/dist/esm/client-helpers.js +81 -0
- package/dist/esm/client-helpers.js.map +1 -0
- package/dist/esm/config/server-loader.d.ts +27 -0
- package/dist/esm/config/server-loader.js +49 -0
- package/dist/esm/config/server-loader.js.map +1 -0
- package/dist/esm/config/validate-config.d.ts +15 -0
- package/dist/esm/config/validate-config.js +76 -0
- package/dist/esm/config/validate-config.js.map +1 -0
- package/dist/esm/connection/connect-client.d.ts +59 -0
- package/dist/esm/connection/connect-client.js +272 -0
- package/dist/esm/connection/connect-client.js.map +1 -0
- package/dist/esm/connection/existing-process-transport.d.ts +40 -0
- package/dist/esm/connection/existing-process-transport.js +103 -0
- package/dist/esm/connection/existing-process-transport.js.map +1 -0
- package/dist/esm/connection/types.d.ts +61 -0
- package/dist/esm/connection/types.js +34 -0
- package/dist/esm/connection/types.js.map +1 -0
- package/dist/esm/connection/wait-for-http-ready.d.ts +15 -0
- package/dist/esm/connection/wait-for-http-ready.js +43 -0
- package/dist/esm/connection/wait-for-http-ready.js.map +1 -0
- package/dist/esm/dcr/dcr-authenticator.d.ts +73 -0
- package/dist/esm/dcr/dcr-authenticator.js +235 -0
- package/dist/esm/dcr/dcr-authenticator.js.map +1 -0
- package/dist/esm/dcr/dynamic-client-registrar.d.ts +28 -0
- package/dist/esm/dcr/dynamic-client-registrar.js +66 -0
- package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -0
- package/dist/esm/dcr/index.d.ts +8 -0
- package/dist/esm/dcr/index.js +5 -0
- package/dist/esm/dcr/index.js.map +1 -0
- package/dist/esm/index.d.ts +21 -0
- package/dist/esm/index.js +22 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/monkey-patches.d.ts +6 -0
- package/dist/esm/monkey-patches.js +32 -0
- package/dist/esm/monkey-patches.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/response-wrappers.d.ts +41 -0
- package/dist/esm/response-wrappers.js +201 -0
- package/dist/esm/response-wrappers.js.map +1 -0
- package/dist/esm/search/index.d.ts +6 -0
- package/dist/esm/search/index.js +3 -0
- package/dist/esm/search/index.js.map +1 -0
- package/dist/esm/search/search.d.ts +22 -0
- package/dist/esm/search/search.js +236 -0
- package/dist/esm/search/search.js.map +1 -0
- package/dist/esm/search/types.d.ts +122 -0
- package/dist/esm/search/types.js +8 -0
- package/dist/esm/search/types.js.map +1 -0
- package/dist/esm/spawn/spawn-server.d.ts +83 -0
- package/dist/esm/spawn/spawn-server.js +145 -0
- package/dist/esm/spawn/spawn-server.js.map +1 -0
- package/dist/esm/spawn/spawn-servers.d.ts +151 -0
- package/dist/esm/spawn/spawn-servers.js +406 -0
- package/dist/esm/spawn/spawn-servers.js.map +1 -0
- package/dist/esm/types.d.ts +11 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/logger.d.ts +24 -0
- package/dist/esm/utils/logger.js +59 -0
- package/dist/esm/utils/logger.js.map +1 -0
- package/dist/esm/utils/path-utils.d.ts +45 -0
- package/dist/esm/utils/path-utils.js +89 -0
- package/dist/esm/utils/path-utils.js.map +1 -0
- package/dist/esm/utils/sanitizer.d.ts +30 -0
- package/dist/esm/utils/sanitizer.js +43 -0
- package/dist/esm/utils/sanitizer.js.map +1 -0
- package/package.json +92 -0
- package/schemas/servers.d.ts +90 -0
- package/schemas/servers.schema.json +104 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal log sanitization for spawn operations
|
|
3
|
+
* Redacts credentials from environment variables and command arguments
|
|
4
|
+
*/ "use strict";
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
function _export(target, all) {
|
|
9
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
_export(exports, {
|
|
15
|
+
get sanitizeForLogging () {
|
|
16
|
+
return sanitizeForLogging;
|
|
17
|
+
},
|
|
18
|
+
get sanitizeForLoggingFormatter () {
|
|
19
|
+
return sanitizeForLoggingFormatter;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
function _define_property(obj, key, value) {
|
|
23
|
+
if (key in obj) {
|
|
24
|
+
Object.defineProperty(obj, key, {
|
|
25
|
+
value: value,
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
obj[key] = value;
|
|
32
|
+
}
|
|
33
|
+
return obj;
|
|
34
|
+
}
|
|
35
|
+
function _object_spread(target) {
|
|
36
|
+
for(var i = 1; i < arguments.length; i++){
|
|
37
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
38
|
+
var ownKeys = Object.keys(source);
|
|
39
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
40
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
41
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
ownKeys.forEach(function(key) {
|
|
45
|
+
_define_property(target, key, source[key]);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return target;
|
|
49
|
+
}
|
|
50
|
+
function ownKeys(object, enumerableOnly) {
|
|
51
|
+
var keys = Object.keys(object);
|
|
52
|
+
if (Object.getOwnPropertySymbols) {
|
|
53
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
54
|
+
if (enumerableOnly) {
|
|
55
|
+
symbols = symbols.filter(function(sym) {
|
|
56
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
keys.push.apply(keys, symbols);
|
|
60
|
+
}
|
|
61
|
+
return keys;
|
|
62
|
+
}
|
|
63
|
+
function _object_spread_props(target, source) {
|
|
64
|
+
source = source != null ? source : {};
|
|
65
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
66
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
67
|
+
} else {
|
|
68
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
69
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return target;
|
|
73
|
+
}
|
|
74
|
+
function _type_of(obj) {
|
|
75
|
+
"@swc/helpers - typeof";
|
|
76
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
77
|
+
}
|
|
78
|
+
function sanitizeForLogging(message, obj) {
|
|
79
|
+
// Redact common credential patterns in message
|
|
80
|
+
var cleanMessage = message.replace(/key[=:]\S+/gi, 'key=[REDACTED]').replace(/secret[=:]\S+/gi, 'secret=[REDACTED]').replace(/token[=:]\S+/gi, 'token=[REDACTED]').replace(/password[=:]\S+/gi, 'password=[REDACTED]').replace(/auth[=:]\S+/gi, 'auth=[REDACTED]');
|
|
81
|
+
// Deep clone and redact sensitive env var keys
|
|
82
|
+
var cleanMeta = JSON.parse(JSON.stringify(obj));
|
|
83
|
+
// Redact sensitive environment variable values
|
|
84
|
+
if (cleanMeta.env && _type_of(cleanMeta.env) === 'object') {
|
|
85
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
86
|
+
try {
|
|
87
|
+
for(var _iterator = Object.keys(cleanMeta.env)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
88
|
+
var envKey = _step.value;
|
|
89
|
+
if (/key|secret|token|password|auth|credential/i.test(envKey)) {
|
|
90
|
+
cleanMeta.env[envKey] = '[REDACTED]';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
_didIteratorError = true;
|
|
95
|
+
_iteratorError = err;
|
|
96
|
+
} finally{
|
|
97
|
+
try {
|
|
98
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
99
|
+
_iterator.return();
|
|
100
|
+
}
|
|
101
|
+
} finally{
|
|
102
|
+
if (_didIteratorError) {
|
|
103
|
+
throw _iteratorError;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
message: cleanMessage,
|
|
110
|
+
meta: cleanMeta
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function sanitizeForLoggingFormatter() {
|
|
114
|
+
return {
|
|
115
|
+
log: function(obj) {
|
|
116
|
+
var message = obj.msg || obj.message || '';
|
|
117
|
+
var _sanitizeForLogging = sanitizeForLogging(message, obj), clean = _sanitizeForLogging.message, meta = _sanitizeForLogging.meta;
|
|
118
|
+
return _object_spread_props(_object_spread({}, meta), {
|
|
119
|
+
msg: clean
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/utils/sanitizer.ts"],"sourcesContent":["/**\n * Minimal log sanitization for spawn operations\n * Redacts credentials from environment variables and command arguments\n */\n\nimport type { SpawnMetadata } from '../connection/types.ts';\n\n/**\n * Sanitize log messages and metadata to prevent credential leakage\n *\n * Redacts common credential patterns:\n * - key=value, secret=value, token=value, password=value\n * - Environment variables with sensitive names\n *\n * @param message - Log message to sanitize\n * @param obj - Metadata object to sanitize\n * @returns Sanitized message and metadata\n */\nexport function sanitizeForLogging(message: string, obj: SpawnMetadata): { message: string; meta: SpawnMetadata } {\n // Redact common credential patterns in message\n const cleanMessage = message\n .replace(/key[=:]\\S+/gi, 'key=[REDACTED]')\n .replace(/secret[=:]\\S+/gi, 'secret=[REDACTED]')\n .replace(/token[=:]\\S+/gi, 'token=[REDACTED]')\n .replace(/password[=:]\\S+/gi, 'password=[REDACTED]')\n .replace(/auth[=:]\\S+/gi, 'auth=[REDACTED]');\n\n // Deep clone and redact sensitive env var keys\n const cleanMeta = JSON.parse(JSON.stringify(obj));\n\n // Redact sensitive environment variable values\n if (cleanMeta.env && typeof cleanMeta.env === 'object') {\n for (const envKey of Object.keys(cleanMeta.env)) {\n if (/key|secret|token|password|auth|credential/i.test(envKey)) {\n cleanMeta.env[envKey] = '[REDACTED]';\n }\n }\n }\n\n return { message: cleanMessage, meta: cleanMeta };\n}\n\nexport function sanitizeForLoggingFormatter() {\n return {\n log: (obj: SpawnMetadata) => {\n const message = (obj.msg || obj.message || '') as string;\n const { message: clean, meta } = sanitizeForLogging(message, obj);\n return { ...meta, msg: clean };\n },\n };\n}\n"],"names":["sanitizeForLogging","sanitizeForLoggingFormatter","message","obj","cleanMessage","replace","cleanMeta","JSON","parse","stringify","env","Object","keys","envKey","test","meta","log","msg","clean"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QAeeA;eAAAA;;QAwBAC;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAxBT,SAASD,mBAAmBE,OAAe,EAAEC,GAAkB;IACpE,+CAA+C;IAC/C,IAAMC,eAAeF,QAClBG,OAAO,CAAC,gBAAgB,kBACxBA,OAAO,CAAC,mBAAmB,qBAC3BA,OAAO,CAAC,kBAAkB,oBAC1BA,OAAO,CAAC,qBAAqB,uBAC7BA,OAAO,CAAC,iBAAiB;IAE5B,+CAA+C;IAC/C,IAAMC,YAAYC,KAAKC,KAAK,CAACD,KAAKE,SAAS,CAACN;IAE5C,+CAA+C;IAC/C,IAAIG,UAAUI,GAAG,IAAI,SAAOJ,UAAUI,GAAG,MAAK,UAAU;YACjD,kCAAA,2BAAA;;YAAL,QAAK,YAAgBC,OAAOC,IAAI,CAACN,UAAUI,GAAG,sBAAzC,SAAA,6BAAA,QAAA,yBAAA,iCAA4C;gBAA5C,IAAMG,SAAN;gBACH,IAAI,6CAA6CC,IAAI,CAACD,SAAS;oBAC7DP,UAAUI,GAAG,CAACG,OAAO,GAAG;gBAC1B;YACF;;YAJK;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IAKP;IAEA,OAAO;QAAEX,SAASE;QAAcW,MAAMT;IAAU;AAClD;AAEO,SAASL;IACd,OAAO;QACLe,KAAK,SAACb;YACJ,IAAMD,UAAWC,IAAIc,GAAG,IAAId,IAAID,OAAO,IAAI;YAC3C,IAAiCF,sBAAAA,mBAAmBE,SAASC,MAArDD,AAASgB,QAAgBlB,oBAAzBE,SAAgBa,OAASf,oBAATe;YACxB,OAAO,wCAAKA;gBAAME,KAAKC;;QACzB;IACF;AACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Server Capability Discovery
|
|
3
|
+
* Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata
|
|
4
|
+
*/
|
|
5
|
+
import type { AuthCapabilities } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Probe OAuth server capabilities using RFC 9728 → RFC 8414 discovery chain
|
|
8
|
+
* Returns capabilities including DCR support detection
|
|
9
|
+
*
|
|
10
|
+
* Discovery Strategy:
|
|
11
|
+
* 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)
|
|
12
|
+
* 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata
|
|
13
|
+
* 3. Fall back to direct RFC 8414 discovery at resource origin
|
|
14
|
+
*
|
|
15
|
+
* @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)
|
|
16
|
+
* @returns AuthCapabilities object with discovered endpoints and features
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com
|
|
20
|
+
* const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');
|
|
21
|
+
* if (caps.supportsDcr) {
|
|
22
|
+
* console.log('Registration endpoint:', caps.registrationEndpoint);
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
export declare function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Server Capability Discovery
|
|
3
|
+
* Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata
|
|
4
|
+
*/ import { discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.js';
|
|
5
|
+
/**
|
|
6
|
+
* Extract origin (protocol + host) from a URL
|
|
7
|
+
* @param url - Full URL that may include a path
|
|
8
|
+
* @returns Origin (e.g., "https://example.com") or original string if invalid URL
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* getOrigin('https://example.com/mcp') // → 'https://example.com'
|
|
12
|
+
* getOrigin('http://localhost:9999/api/v1/mcp') // → 'http://localhost:9999'
|
|
13
|
+
*/ function getOrigin(url) {
|
|
14
|
+
try {
|
|
15
|
+
return new URL(url).origin;
|
|
16
|
+
} catch {
|
|
17
|
+
// Invalid URL - return as-is for graceful degradation
|
|
18
|
+
return url;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Probe OAuth server capabilities using RFC 9728 → RFC 8414 discovery chain
|
|
23
|
+
* Returns capabilities including DCR support detection
|
|
24
|
+
*
|
|
25
|
+
* Discovery Strategy:
|
|
26
|
+
* 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)
|
|
27
|
+
* 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata
|
|
28
|
+
* 3. Fall back to direct RFC 8414 discovery at resource origin
|
|
29
|
+
*
|
|
30
|
+
* @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)
|
|
31
|
+
* @returns AuthCapabilities object with discovered endpoints and features
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com
|
|
35
|
+
* const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');
|
|
36
|
+
* if (caps.supportsDcr) {
|
|
37
|
+
* console.log('Registration endpoint:', caps.registrationEndpoint);
|
|
38
|
+
* }
|
|
39
|
+
*/ export async function probeAuthCapabilities(baseUrl) {
|
|
40
|
+
try {
|
|
41
|
+
// Strategy 1: Try RFC 9728 Protected Resource Metadata discovery
|
|
42
|
+
// This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp → todoist.com)
|
|
43
|
+
const resourceMetadata = await discoverProtectedResourceMetadata(baseUrl);
|
|
44
|
+
if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {
|
|
45
|
+
// Found protected resource metadata with authorization servers
|
|
46
|
+
// Discover the authorization server's metadata (RFC 8414)
|
|
47
|
+
const authServerUrl = resourceMetadata.authorization_servers[0];
|
|
48
|
+
if (!authServerUrl) {
|
|
49
|
+
// Array has length > 0 but first element is undefined/null - skip this path
|
|
50
|
+
return {
|
|
51
|
+
supportsDcr: false
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const authServerMetadata = await discoverAuthorizationServerMetadata(authServerUrl);
|
|
55
|
+
if (authServerMetadata) {
|
|
56
|
+
// Successfully discovered full OAuth metadata via RFC 9728 → RFC 8414 chain
|
|
57
|
+
const supportsDcr = !!authServerMetadata.registration_endpoint;
|
|
58
|
+
const capabilities = {
|
|
59
|
+
supportsDcr
|
|
60
|
+
};
|
|
61
|
+
if (authServerMetadata.registration_endpoint) {
|
|
62
|
+
capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;
|
|
63
|
+
}
|
|
64
|
+
if (authServerMetadata.authorization_endpoint) {
|
|
65
|
+
capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;
|
|
66
|
+
}
|
|
67
|
+
if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;
|
|
68
|
+
if (authServerMetadata.introspection_endpoint) {
|
|
69
|
+
capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;
|
|
70
|
+
}
|
|
71
|
+
// Prefer resource scopes over auth server scopes
|
|
72
|
+
const scopes = resourceMetadata.scopes_supported || authServerMetadata.scopes_supported;
|
|
73
|
+
if (scopes) capabilities.scopes = scopes;
|
|
74
|
+
return capabilities;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Strategy 2: Fall back to direct RFC 8414 discovery at resource origin
|
|
78
|
+
// This handles same-domain OAuth (traditional setup)
|
|
79
|
+
const origin = getOrigin(baseUrl);
|
|
80
|
+
const authServerMetadata = await discoverAuthorizationServerMetadata(origin);
|
|
81
|
+
if (authServerMetadata) {
|
|
82
|
+
const supportsDcr = !!authServerMetadata.registration_endpoint;
|
|
83
|
+
const capabilities = {
|
|
84
|
+
supportsDcr
|
|
85
|
+
};
|
|
86
|
+
if (authServerMetadata.registration_endpoint) {
|
|
87
|
+
capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;
|
|
88
|
+
}
|
|
89
|
+
if (authServerMetadata.authorization_endpoint) {
|
|
90
|
+
capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;
|
|
91
|
+
}
|
|
92
|
+
if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;
|
|
93
|
+
if (authServerMetadata.introspection_endpoint) {
|
|
94
|
+
capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;
|
|
95
|
+
}
|
|
96
|
+
if (authServerMetadata.scopes_supported) capabilities.scopes = authServerMetadata.scopes_supported;
|
|
97
|
+
return capabilities;
|
|
98
|
+
}
|
|
99
|
+
// No OAuth metadata found
|
|
100
|
+
return {
|
|
101
|
+
supportsDcr: false
|
|
102
|
+
};
|
|
103
|
+
} catch (_error) {
|
|
104
|
+
// Network error, invalid JSON, or other fetch failure
|
|
105
|
+
// Gracefully degrade - assume no DCR support
|
|
106
|
+
return {
|
|
107
|
+
supportsDcr: false
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/capability-discovery.ts"],"sourcesContent":["/**\n * OAuth Server Capability Discovery\n * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata\n */\n\nimport { discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.ts';\nimport type { AuthCapabilities } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // → 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // → 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Probe OAuth server capabilities using RFC 9728 → RFC 8414 discovery chain\n * Returns capabilities including DCR support detection\n *\n * Discovery Strategy:\n * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)\n * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata\n * 3. Fall back to direct RFC 8414 discovery at resource origin\n *\n * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns AuthCapabilities object with discovered endpoints and features\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');\n * if (caps.supportsDcr) {\n * console.log('Registration endpoint:', caps.registrationEndpoint);\n * }\n */\nexport async function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities> {\n try {\n // Strategy 1: Try RFC 9728 Protected Resource Metadata discovery\n // This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp → todoist.com)\n const resourceMetadata = await discoverProtectedResourceMetadata(baseUrl);\n\n if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {\n // Found protected resource metadata with authorization servers\n // Discover the authorization server's metadata (RFC 8414)\n const authServerUrl = resourceMetadata.authorization_servers[0];\n if (!authServerUrl) {\n // Array has length > 0 but first element is undefined/null - skip this path\n return { supportsDcr: false };\n }\n const authServerMetadata = await discoverAuthorizationServerMetadata(authServerUrl);\n\n if (authServerMetadata) {\n // Successfully discovered full OAuth metadata via RFC 9728 → RFC 8414 chain\n const supportsDcr = !!authServerMetadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (authServerMetadata.registration_endpoint) {\n capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;\n }\n if (authServerMetadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;\n }\n if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;\n if (authServerMetadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;\n }\n\n // Prefer resource scopes over auth server scopes\n const scopes = resourceMetadata.scopes_supported || authServerMetadata.scopes_supported;\n if (scopes) capabilities.scopes = scopes;\n\n return capabilities;\n }\n }\n\n // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin\n // This handles same-domain OAuth (traditional setup)\n const origin = getOrigin(baseUrl);\n const authServerMetadata = await discoverAuthorizationServerMetadata(origin);\n\n if (authServerMetadata) {\n const supportsDcr = !!authServerMetadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (authServerMetadata.registration_endpoint) {\n capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;\n }\n if (authServerMetadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;\n }\n if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;\n if (authServerMetadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;\n }\n if (authServerMetadata.scopes_supported) capabilities.scopes = authServerMetadata.scopes_supported;\n\n return capabilities;\n }\n\n // No OAuth metadata found\n return { supportsDcr: false };\n } catch (_error) {\n // Network error, invalid JSON, or other fetch failure\n // Gracefully degrade - assume no DCR support\n return { supportsDcr: false };\n }\n}\n"],"names":["discoverAuthorizationServerMetadata","discoverProtectedResourceMetadata","getOrigin","url","URL","origin","probeAuthCapabilities","baseUrl","resourceMetadata","authorization_servers","length","authServerUrl","supportsDcr","authServerMetadata","registration_endpoint","capabilities","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","scopes","scopes_supported","_error"],"mappings":"AAAA;;;CAGC,GAED,SAASA,mCAAmC,EAAEC,iCAAiC,QAAQ,yBAAyB;AAGhH;;;;;;;;CAQC,GACD,SAASC,UAAUC,GAAW;IAC5B,IAAI;QACF,OAAO,IAAIC,IAAID,KAAKE,MAAM;IAC5B,EAAE,OAAM;QACN,sDAAsD;QACtD,OAAOF;IACT;AACF;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,eAAeG,sBAAsBC,OAAe;IACzD,IAAI;QACF,iEAAiE;QACjE,oFAAoF;QACpF,MAAMC,mBAAmB,MAAMP,kCAAkCM;QAEjE,IAAIC,oBAAoBA,iBAAiBC,qBAAqB,CAACC,MAAM,GAAG,GAAG;YACzE,+DAA+D;YAC/D,0DAA0D;YAC1D,MAAMC,gBAAgBH,iBAAiBC,qBAAqB,CAAC,EAAE;YAC/D,IAAI,CAACE,eAAe;gBAClB,4EAA4E;gBAC5E,OAAO;oBAAEC,aAAa;gBAAM;YAC9B;YACA,MAAMC,qBAAqB,MAAMb,oCAAoCW;YAErE,IAAIE,oBAAoB;gBACtB,4EAA4E;gBAC5E,MAAMD,cAAc,CAAC,CAACC,mBAAmBC,qBAAqB;gBAC9D,MAAMC,eAAiC;oBAAEH;gBAAY;gBAErD,IAAIC,mBAAmBC,qBAAqB,EAAE;oBAC5CC,aAAaC,oBAAoB,GAAGH,mBAAmBC,qBAAqB;gBAC9E;gBACA,IAAID,mBAAmBI,sBAAsB,EAAE;oBAC7CF,aAAaG,qBAAqB,GAAGL,mBAAmBI,sBAAsB;gBAChF;gBACA,IAAIJ,mBAAmBM,cAAc,EAAEJ,aAAaK,aAAa,GAAGP,mBAAmBM,cAAc;gBACrG,IAAIN,mBAAmBQ,sBAAsB,EAAE;oBAC7CN,aAAaO,qBAAqB,GAAGT,mBAAmBQ,sBAAsB;gBAChF;gBAEA,iDAAiD;gBACjD,MAAME,SAASf,iBAAiBgB,gBAAgB,IAAIX,mBAAmBW,gBAAgB;gBACvF,IAAID,QAAQR,aAAaQ,MAAM,GAAGA;gBAElC,OAAOR;YACT;QACF;QAEA,wEAAwE;QACxE,qDAAqD;QACrD,MAAMV,SAASH,UAAUK;QACzB,MAAMM,qBAAqB,MAAMb,oCAAoCK;QAErE,IAAIQ,oBAAoB;YACtB,MAAMD,cAAc,CAAC,CAACC,mBAAmBC,qBAAqB;YAC9D,MAAMC,eAAiC;gBAAEH;YAAY;YAErD,IAAIC,mBAAmBC,qBAAqB,EAAE;gBAC5CC,aAAaC,oBAAoB,GAAGH,mBAAmBC,qBAAqB;YAC9E;YACA,IAAID,mBAAmBI,sBAAsB,EAAE;gBAC7CF,aAAaG,qBAAqB,GAAGL,mBAAmBI,sBAAsB;YAChF;YACA,IAAIJ,mBAAmBM,cAAc,EAAEJ,aAAaK,aAAa,GAAGP,mBAAmBM,cAAc;YACrG,IAAIN,mBAAmBQ,sBAAsB,EAAE;gBAC7CN,aAAaO,qBAAqB,GAAGT,mBAAmBQ,sBAAsB;YAChF;YACA,IAAIR,mBAAmBW,gBAAgB,EAAET,aAAaQ,MAAM,GAAGV,mBAAmBW,gBAAgB;YAElG,OAAOT;QACT;QAEA,0BAA0B;QAC1B,OAAO;YAAEH,aAAa;QAAM;IAC9B,EAAE,OAAOa,QAAQ;QACf,sDAAsD;QACtD,6CAA6C;QAC7C,OAAO;YAAEb,aAAa;QAAM;IAC9B;AACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Module
|
|
3
|
+
* Exports public API for OAuth authentication only (DCR moved to ../dcr/)
|
|
4
|
+
*/
|
|
5
|
+
export { probeAuthCapabilities } from './capability-discovery.js';
|
|
6
|
+
export { InteractiveOAuthFlow } from './interactive-oauth-flow.js';
|
|
7
|
+
export type { OAuthCallbackListenerOptions } from './oauth-callback-listener.js';
|
|
8
|
+
export { OAuthCallbackListener } from './oauth-callback-listener.js';
|
|
9
|
+
export type { AuthCapabilities, CallbackResult, OAuthFlowOptions, TokenSet } from './types.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Module
|
|
3
|
+
* Exports public API for OAuth authentication only (DCR moved to ../dcr/)
|
|
4
|
+
*/ export { probeAuthCapabilities } from './capability-discovery.js';
|
|
5
|
+
export { InteractiveOAuthFlow } from './interactive-oauth-flow.js';
|
|
6
|
+
export { OAuthCallbackListener } from './oauth-callback-listener.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/index.ts"],"sourcesContent":["/**\n * Authentication Module\n * Exports public API for OAuth authentication only (DCR moved to ../dcr/)\n */\n\nexport { probeAuthCapabilities } from './capability-discovery.ts';\nexport { InteractiveOAuthFlow } from './interactive-oauth-flow.ts';\nexport type { OAuthCallbackListenerOptions } from './oauth-callback-listener.ts';\nexport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nexport type { AuthCapabilities, CallbackResult, OAuthFlowOptions, TokenSet } from './types.ts';\n"],"names":["probeAuthCapabilities","InteractiveOAuthFlow","OAuthCallbackListener"],"mappings":"AAAA;;;CAGC,GAED,SAASA,qBAAqB,QAAQ,4BAA4B;AAClE,SAASC,oBAAoB,QAAQ,8BAA8B;AAEnE,SAASC,qBAAqB,QAAQ,+BAA+B"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Authorization Flow Handler
|
|
3
|
+
* Manages browser-based OAuth flows and token exchange with PKCE support
|
|
4
|
+
*/
|
|
5
|
+
import type { OAuthFlowOptions, TokenSet } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* InteractiveOAuthFlow manages the complete OAuth authorization code flow
|
|
8
|
+
*/
|
|
9
|
+
export declare class InteractiveOAuthFlow {
|
|
10
|
+
/**
|
|
11
|
+
* Perform OAuth authorization code flow
|
|
12
|
+
*
|
|
13
|
+
* @param authorizationEndpoint - OAuth authorization endpoint URL
|
|
14
|
+
* @param tokenEndpoint - OAuth token endpoint URL
|
|
15
|
+
* @param clientId - OAuth client ID
|
|
16
|
+
* @param clientSecret - OAuth client secret
|
|
17
|
+
* @param options - Flow options (port is required - use get-port to find available port)
|
|
18
|
+
* @returns Token set with access and refresh tokens
|
|
19
|
+
*
|
|
20
|
+
* @throws Error if flow fails or times out
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* import getPort from 'get-port';
|
|
24
|
+
*
|
|
25
|
+
* const flow = new InteractiveOAuthFlow();
|
|
26
|
+
* const port = await getPort();
|
|
27
|
+
* const tokens = await flow.performAuthFlow(
|
|
28
|
+
* 'https://example.com/oauth/authorize',
|
|
29
|
+
* 'https://example.com/oauth/token',
|
|
30
|
+
* 'client-id',
|
|
31
|
+
* 'client-secret',
|
|
32
|
+
* { port, scopes: ['read', 'write'] }
|
|
33
|
+
* );
|
|
34
|
+
*/
|
|
35
|
+
performAuthFlow(authorizationEndpoint: string, tokenEndpoint: string, clientId: string, clientSecret: string, options: OAuthFlowOptions): Promise<TokenSet>;
|
|
36
|
+
/**
|
|
37
|
+
* Exchange authorization code for access and refresh tokens
|
|
38
|
+
* @param codeVerifier - Optional PKCE code verifier (RFC 7636)
|
|
39
|
+
*/
|
|
40
|
+
private exchangeCodeForTokens;
|
|
41
|
+
/**
|
|
42
|
+
* Refresh access token using refresh token
|
|
43
|
+
*
|
|
44
|
+
* @param tokenEndpoint - OAuth token endpoint URL
|
|
45
|
+
* @param refreshToken - Refresh token from previous token set
|
|
46
|
+
* @param clientId - OAuth client ID
|
|
47
|
+
* @param clientSecret - OAuth client secret
|
|
48
|
+
* @returns New token set with refreshed access token
|
|
49
|
+
*
|
|
50
|
+
* @throws Error if refresh fails
|
|
51
|
+
*/
|
|
52
|
+
refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string): Promise<TokenSet>;
|
|
53
|
+
/**
|
|
54
|
+
* Open browser to authorization URL
|
|
55
|
+
* Uses platform-specific command to open default browser
|
|
56
|
+
*/
|
|
57
|
+
private openBrowser;
|
|
58
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Authorization Flow Handler
|
|
3
|
+
* Manages browser-based OAuth flows and token exchange with PKCE support
|
|
4
|
+
*/ import * as child_process from 'node:child_process';
|
|
5
|
+
import { logger as defaultLogger } from '../utils/logger.js';
|
|
6
|
+
import { OAuthCallbackListener } from './oauth-callback-listener.js';
|
|
7
|
+
import { generatePkce } from './pkce.js';
|
|
8
|
+
/**
|
|
9
|
+
* InteractiveOAuthFlow manages the complete OAuth authorization code flow
|
|
10
|
+
*/ export class InteractiveOAuthFlow {
|
|
11
|
+
/**
|
|
12
|
+
* Perform OAuth authorization code flow
|
|
13
|
+
*
|
|
14
|
+
* @param authorizationEndpoint - OAuth authorization endpoint URL
|
|
15
|
+
* @param tokenEndpoint - OAuth token endpoint URL
|
|
16
|
+
* @param clientId - OAuth client ID
|
|
17
|
+
* @param clientSecret - OAuth client secret
|
|
18
|
+
* @param options - Flow options (port is required - use get-port to find available port)
|
|
19
|
+
* @returns Token set with access and refresh tokens
|
|
20
|
+
*
|
|
21
|
+
* @throws Error if flow fails or times out
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* import getPort from 'get-port';
|
|
25
|
+
*
|
|
26
|
+
* const flow = new InteractiveOAuthFlow();
|
|
27
|
+
* const port = await getPort();
|
|
28
|
+
* const tokens = await flow.performAuthFlow(
|
|
29
|
+
* 'https://example.com/oauth/authorize',
|
|
30
|
+
* 'https://example.com/oauth/token',
|
|
31
|
+
* 'client-id',
|
|
32
|
+
* 'client-secret',
|
|
33
|
+
* { port, scopes: ['read', 'write'] }
|
|
34
|
+
* );
|
|
35
|
+
*/ async performAuthFlow(authorizationEndpoint, tokenEndpoint, clientId, clientSecret, options) {
|
|
36
|
+
var _options_logger;
|
|
37
|
+
const logger = (_options_logger = options.logger) !== null && _options_logger !== void 0 ? _options_logger : defaultLogger;
|
|
38
|
+
const callbackListener = new OAuthCallbackListener({
|
|
39
|
+
port: options.port,
|
|
40
|
+
logger
|
|
41
|
+
});
|
|
42
|
+
// Generate PKCE parameters if requested (RFC 7636)
|
|
43
|
+
let pkce;
|
|
44
|
+
if (options.pkce) {
|
|
45
|
+
logger.debug('🔐 Generating PKCE parameters...');
|
|
46
|
+
pkce = await generatePkce();
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
// Start callback server
|
|
50
|
+
await callbackListener.start();
|
|
51
|
+
// Build redirect URI
|
|
52
|
+
const redirectUri = options.redirectUri || `http://localhost:${options.port}/callback`;
|
|
53
|
+
// Build authorization URL
|
|
54
|
+
const authUrl = new URL(authorizationEndpoint);
|
|
55
|
+
authUrl.searchParams.set('client_id', clientId);
|
|
56
|
+
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
57
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
58
|
+
if (options.scopes && options.scopes.length > 0) {
|
|
59
|
+
authUrl.searchParams.set('scope', options.scopes.join(' '));
|
|
60
|
+
}
|
|
61
|
+
// Add resource parameter if specified (RFC 8707)
|
|
62
|
+
if (options.resource) {
|
|
63
|
+
authUrl.searchParams.set('resource', options.resource);
|
|
64
|
+
}
|
|
65
|
+
// Add PKCE parameters if generated (RFC 7636)
|
|
66
|
+
if (pkce) {
|
|
67
|
+
authUrl.searchParams.set('code_challenge', pkce.codeChallenge);
|
|
68
|
+
authUrl.searchParams.set('code_challenge_method', pkce.codeChallengeMethod);
|
|
69
|
+
}
|
|
70
|
+
// Open browser or print URL for headless mode
|
|
71
|
+
if (options.headless) {
|
|
72
|
+
logger.info('🔗 Please visit this URL to authorize:');
|
|
73
|
+
logger.info(authUrl.toString());
|
|
74
|
+
logger.info('Waiting for callback...');
|
|
75
|
+
} else {
|
|
76
|
+
logger.debug('🌐 Opening browser for OAuth authorization...');
|
|
77
|
+
// Try to open browser (requires 'open' package or native command)
|
|
78
|
+
await this.openBrowser(authUrl.toString());
|
|
79
|
+
}
|
|
80
|
+
// Wait for callback with timeout
|
|
81
|
+
const timeout = options.timeout || (options.headless ? 60000 : 300000);
|
|
82
|
+
const result = await callbackListener.waitForCallback(timeout);
|
|
83
|
+
// Exchange authorization code for tokens (with PKCE verifier if used)
|
|
84
|
+
const tokens = await this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, pkce === null || pkce === void 0 ? void 0 : pkce.codeVerifier);
|
|
85
|
+
return tokens;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
logger.error('❌ OAuth flow failed:', error instanceof Error ? error.message : String(error));
|
|
88
|
+
throw error;
|
|
89
|
+
} finally{
|
|
90
|
+
// Always close callback server
|
|
91
|
+
await callbackListener.stop();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Exchange authorization code for access and refresh tokens
|
|
96
|
+
* @param codeVerifier - Optional PKCE code verifier (RFC 7636)
|
|
97
|
+
*/ async exchangeCodeForTokens(tokenEndpoint, code, clientId, clientSecret, redirectUri, codeVerifier) {
|
|
98
|
+
const params = new URLSearchParams({
|
|
99
|
+
grant_type: 'authorization_code',
|
|
100
|
+
code,
|
|
101
|
+
redirect_uri: redirectUri,
|
|
102
|
+
client_id: clientId,
|
|
103
|
+
client_secret: clientSecret
|
|
104
|
+
});
|
|
105
|
+
// Add PKCE code verifier if provided (RFC 7636)
|
|
106
|
+
if (codeVerifier) {
|
|
107
|
+
params.set('code_verifier', codeVerifier);
|
|
108
|
+
}
|
|
109
|
+
const response = await fetch(tokenEndpoint, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: {
|
|
112
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
113
|
+
Accept: 'application/json',
|
|
114
|
+
Connection: 'close'
|
|
115
|
+
},
|
|
116
|
+
body: params
|
|
117
|
+
});
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
const errorText = await response.text();
|
|
120
|
+
throw new Error(`Token exchange failed (${response.status}): ${errorText}`);
|
|
121
|
+
}
|
|
122
|
+
const data = await response.json();
|
|
123
|
+
if (!data.access_token) {
|
|
124
|
+
throw new Error('Token response missing access_token');
|
|
125
|
+
}
|
|
126
|
+
const tokenSet = {
|
|
127
|
+
accessToken: data.access_token,
|
|
128
|
+
refreshToken: data.refresh_token || '',
|
|
129
|
+
expiresAt: Date.now() + data.expires_in * 1000,
|
|
130
|
+
clientId,
|
|
131
|
+
clientSecret
|
|
132
|
+
};
|
|
133
|
+
if (data.scope) {
|
|
134
|
+
tokenSet.scopes = data.scope.split(' ');
|
|
135
|
+
}
|
|
136
|
+
return tokenSet;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Refresh access token using refresh token
|
|
140
|
+
*
|
|
141
|
+
* @param tokenEndpoint - OAuth token endpoint URL
|
|
142
|
+
* @param refreshToken - Refresh token from previous token set
|
|
143
|
+
* @param clientId - OAuth client ID
|
|
144
|
+
* @param clientSecret - OAuth client secret
|
|
145
|
+
* @returns New token set with refreshed access token
|
|
146
|
+
*
|
|
147
|
+
* @throws Error if refresh fails
|
|
148
|
+
*/ async refreshTokens(tokenEndpoint, refreshToken, clientId, clientSecret) {
|
|
149
|
+
const response = await fetch(tokenEndpoint, {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
headers: {
|
|
152
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
153
|
+
Accept: 'application/json',
|
|
154
|
+
Connection: 'close'
|
|
155
|
+
},
|
|
156
|
+
body: new URLSearchParams({
|
|
157
|
+
grant_type: 'refresh_token',
|
|
158
|
+
refresh_token: refreshToken,
|
|
159
|
+
client_id: clientId,
|
|
160
|
+
client_secret: clientSecret
|
|
161
|
+
})
|
|
162
|
+
});
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
const errorText = await response.text();
|
|
165
|
+
throw new Error(`Token refresh failed (${response.status}): ${errorText}`);
|
|
166
|
+
}
|
|
167
|
+
const data = await response.json();
|
|
168
|
+
if (!data.access_token) {
|
|
169
|
+
throw new Error('Token refresh response missing access_token');
|
|
170
|
+
}
|
|
171
|
+
const tokenSet = {
|
|
172
|
+
accessToken: data.access_token,
|
|
173
|
+
refreshToken: data.refresh_token || refreshToken,
|
|
174
|
+
expiresAt: Date.now() + data.expires_in * 1000,
|
|
175
|
+
clientId,
|
|
176
|
+
clientSecret
|
|
177
|
+
};
|
|
178
|
+
if (data.scope) {
|
|
179
|
+
tokenSet.scopes = data.scope.split(' ');
|
|
180
|
+
}
|
|
181
|
+
return tokenSet;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Open browser to authorization URL
|
|
185
|
+
* Uses platform-specific command to open default browser
|
|
186
|
+
*/ async openBrowser(url) {
|
|
187
|
+
// Determine platform-specific command
|
|
188
|
+
const platform = process.platform;
|
|
189
|
+
let command;
|
|
190
|
+
let args;
|
|
191
|
+
if (platform === 'darwin') {
|
|
192
|
+
command = 'open';
|
|
193
|
+
args = [
|
|
194
|
+
url
|
|
195
|
+
];
|
|
196
|
+
} else if (platform === 'win32') {
|
|
197
|
+
command = 'cmd';
|
|
198
|
+
args = [
|
|
199
|
+
'/c',
|
|
200
|
+
'start',
|
|
201
|
+
url
|
|
202
|
+
];
|
|
203
|
+
} else {
|
|
204
|
+
// Linux and others
|
|
205
|
+
command = 'xdg-open';
|
|
206
|
+
args = [
|
|
207
|
+
url
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
// Spawn browser process
|
|
211
|
+
const child = child_process.spawn(command, args, {
|
|
212
|
+
detached: true,
|
|
213
|
+
stdio: 'ignore'
|
|
214
|
+
});
|
|
215
|
+
child.unref();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/interactive-oauth-flow.ts"],"sourcesContent":["/**\n * OAuth Authorization Flow Handler\n * Manages browser-based OAuth flows and token exchange with PKCE support\n */\n\nimport * as child_process from 'node:child_process';\nimport { logger as defaultLogger } from '../utils/logger.ts';\nimport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nimport { generatePkce } from './pkce.ts';\nimport type { OAuthFlowOptions, PkceParams, TokenSet } from './types.ts';\n\n/**\n * OAuth token response from token endpoint\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * InteractiveOAuthFlow manages the complete OAuth authorization code flow\n */\nexport class InteractiveOAuthFlow {\n /**\n * Perform OAuth authorization code flow\n *\n * @param authorizationEndpoint - OAuth authorization endpoint URL\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @param options - Flow options (port is required - use get-port to find available port)\n * @returns Token set with access and refresh tokens\n *\n * @throws Error if flow fails or times out\n *\n * @example\n * import getPort from 'get-port';\n *\n * const flow = new InteractiveOAuthFlow();\n * const port = await getPort();\n * const tokens = await flow.performAuthFlow(\n * 'https://example.com/oauth/authorize',\n * 'https://example.com/oauth/token',\n * 'client-id',\n * 'client-secret',\n * { port, scopes: ['read', 'write'] }\n * );\n */\n async performAuthFlow(authorizationEndpoint: string, tokenEndpoint: string, clientId: string, clientSecret: string, options: OAuthFlowOptions): Promise<TokenSet> {\n const logger = options.logger ?? defaultLogger;\n const callbackListener = new OAuthCallbackListener({ port: options.port, logger });\n\n // Generate PKCE parameters if requested (RFC 7636)\n let pkce: PkceParams | undefined;\n if (options.pkce) {\n logger.debug('🔐 Generating PKCE parameters...');\n pkce = await generatePkce();\n }\n\n try {\n // Start callback server\n await callbackListener.start();\n\n // Build redirect URI\n const redirectUri = options.redirectUri || `http://localhost:${options.port}/callback`;\n\n // Build authorization URL\n const authUrl = new URL(authorizationEndpoint);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n\n if (options.scopes && options.scopes.length > 0) {\n authUrl.searchParams.set('scope', options.scopes.join(' '));\n }\n\n // Add resource parameter if specified (RFC 8707)\n if (options.resource) {\n authUrl.searchParams.set('resource', options.resource);\n }\n\n // Add PKCE parameters if generated (RFC 7636)\n if (pkce) {\n authUrl.searchParams.set('code_challenge', pkce.codeChallenge);\n authUrl.searchParams.set('code_challenge_method', pkce.codeChallengeMethod);\n }\n\n // Open browser or print URL for headless mode\n if (options.headless) {\n logger.info('🔗 Please visit this URL to authorize:');\n logger.info(authUrl.toString());\n logger.info('Waiting for callback...');\n } else {\n logger.debug('🌐 Opening browser for OAuth authorization...');\n // Try to open browser (requires 'open' package or native command)\n await this.openBrowser(authUrl.toString());\n }\n\n // Wait for callback with timeout\n const timeout = options.timeout || (options.headless ? 60000 : 300000);\n const result = await callbackListener.waitForCallback(timeout);\n\n // Exchange authorization code for tokens (with PKCE verifier if used)\n const tokens = await this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, pkce?.codeVerifier);\n\n return tokens;\n } catch (error) {\n logger.error('❌ OAuth flow failed:', error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n // Always close callback server\n await callbackListener.stop();\n }\n }\n\n /**\n * Exchange authorization code for access and refresh tokens\n * @param codeVerifier - Optional PKCE code verifier (RFC 7636)\n */\n private async exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, codeVerifier?: string): Promise<TokenSet> {\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n client_secret: clientSecret,\n });\n\n // Add PKCE code verifier if provided (RFC 7636)\n if (codeVerifier) {\n params.set('code_verifier', codeVerifier);\n }\n\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: params,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || '',\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Refresh access token using refresh token\n *\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param refreshToken - Refresh token from previous token set\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @returns New token set with refreshed access token\n *\n * @throws Error if refresh fails\n */\n async refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string): Promise<TokenSet> {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token refresh response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || refreshToken, // Reuse old refresh token if not provided\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Open browser to authorization URL\n * Uses platform-specific command to open default browser\n */\n private async openBrowser(url: string): Promise<void> {\n // Determine platform-specific command\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === 'darwin') {\n command = 'open';\n args = [url];\n } else if (platform === 'win32') {\n command = 'cmd';\n args = ['/c', 'start', url];\n } else {\n // Linux and others\n command = 'xdg-open';\n args = [url];\n }\n\n // Spawn browser process\n const child = child_process.spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.unref();\n }\n}\n"],"names":["child_process","logger","defaultLogger","OAuthCallbackListener","generatePkce","InteractiveOAuthFlow","performAuthFlow","authorizationEndpoint","tokenEndpoint","clientId","clientSecret","options","callbackListener","port","pkce","debug","start","redirectUri","authUrl","URL","searchParams","set","scopes","length","join","resource","codeChallenge","codeChallengeMethod","headless","info","toString","openBrowser","timeout","result","waitForCallback","tokens","exchangeCodeForTokens","code","codeVerifier","error","Error","message","String","stop","params","URLSearchParams","grant_type","redirect_uri","client_id","client_secret","response","fetch","method","headers","Accept","Connection","body","ok","errorText","text","status","data","json","access_token","tokenSet","accessToken","refreshToken","refresh_token","expiresAt","Date","now","expires_in","scope","split","refreshTokens","url","platform","process","command","args","child","spawn","detached","stdio","unref"],"mappings":"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"}
|