@sonoma-security/mcp-gateway 0.1.11 → 0.1.13
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/dist/__tests__/plugin-discovery.test.d.ts +12 -0
- package/dist/__tests__/plugin-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-discovery.test.js +367 -0
- package/dist/__tests__/plugin-discovery.test.js.map +1 -0
- package/dist/__tests__/tool-blocking.test.d.ts +2 -0
- package/dist/__tests__/tool-blocking.test.d.ts.map +1 -0
- package/dist/__tests__/tool-blocking.test.js +256 -0
- package/dist/__tests__/tool-blocking.test.js.map +1 -0
- package/dist/auth/client.d.ts.map +1 -1
- package/dist/auth/client.js +49 -6
- package/dist/auth/client.js.map +1 -1
- package/dist/auth/keychain.d.ts +34 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +305 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/server.d.ts +15 -3
- package/dist/auth/server.d.ts.map +1 -1
- package/dist/auth/server.js +110 -23
- package/dist/auth/server.js.map +1 -1
- package/dist/auth/storage.d.ts +5 -6
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/auth/storage.js +72 -21
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/upstream-oauth.d.ts +2 -0
- package/dist/auth/upstream-oauth.d.ts.map +1 -1
- package/dist/auth/upstream-oauth.js +58 -8
- package/dist/auth/upstream-oauth.js.map +1 -1
- package/dist/auth/upstream-token-store.d.ts +18 -6
- package/dist/auth/upstream-token-store.d.ts.map +1 -1
- package/dist/auth/upstream-token-store.js +127 -35
- package/dist/auth/upstream-token-store.js.map +1 -1
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -7
- package/dist/config.js.map +1 -1
- package/dist/gateway.d.ts +17 -0
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +302 -66
- package/dist/gateway.js.map +1 -1
- package/dist/http-proxy.d.ts +76 -0
- package/dist/http-proxy.d.ts.map +1 -0
- package/dist/http-proxy.js +316 -0
- package/dist/http-proxy.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pattern-matcher.d.ts +25 -0
- package/dist/pattern-matcher.d.ts.map +1 -1
- package/dist/pattern-matcher.js +65 -0
- package/dist/pattern-matcher.js.map +1 -1
- package/dist/prompt-guard.d.ts +24 -0
- package/dist/prompt-guard.d.ts.map +1 -0
- package/dist/prompt-guard.js +161 -0
- package/dist/prompt-guard.js.map +1 -0
- package/dist/sonoma-client.d.ts +28 -1
- package/dist/sonoma-client.d.ts.map +1 -1
- package/dist/sonoma-client.js +67 -43
- package/dist/sonoma-client.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/auth/storage.js
CHANGED
|
@@ -1,47 +1,98 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Secure token storage for MCP Gateway OAuth credentials
|
|
3
3
|
*
|
|
4
|
-
* Stores tokens in
|
|
5
|
-
*
|
|
6
|
-
* tokens from being copied between machines.
|
|
4
|
+
* Stores tokens in the OS keychain (macOS Keychain, Windows Credential Manager).
|
|
5
|
+
* Falls back to encrypted file storage on unsupported platforms.
|
|
7
6
|
*/
|
|
8
7
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
9
8
|
import { join } from "node:path";
|
|
10
9
|
import { SONOMA_DIR, ensureSonomaDir, getDeviceId, encrypt, decrypt } from "./crypto.js";
|
|
11
|
-
|
|
10
|
+
import { keychainAvailable, keychainGet, keychainSet, keychainDelete } from "./keychain.js";
|
|
11
|
+
const KEYCHAIN_ACCOUNT = "sonoma-auth";
|
|
12
|
+
// Legacy file path (for migration)
|
|
13
|
+
const LEGACY_CREDENTIALS_PATH = join(SONOMA_DIR, "gateway-credentials.json");
|
|
12
14
|
/**
|
|
13
|
-
* Load stored credentials from
|
|
15
|
+
* Load stored credentials from OS keychain (or legacy file).
|
|
14
16
|
*/
|
|
15
17
|
export function loadCredentials() {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
// Try OS keychain first
|
|
19
|
+
if (keychainAvailable()) {
|
|
20
|
+
try {
|
|
21
|
+
const creds = keychainGet(KEYCHAIN_ACCOUNT);
|
|
22
|
+
if (creds)
|
|
23
|
+
return creds;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Keychain access failed, try legacy
|
|
27
|
+
}
|
|
28
|
+
// Migrate from legacy encrypted file if it exists
|
|
29
|
+
const legacy = loadLegacyCredentials();
|
|
30
|
+
if (legacy) {
|
|
31
|
+
try {
|
|
32
|
+
keychainSet(KEYCHAIN_ACCOUNT, legacy);
|
|
33
|
+
// Clean up legacy file after successful migration
|
|
34
|
+
unlinkSync(LEGACY_CREDENTIALS_PATH);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Migration failed, return legacy credentials anyway
|
|
38
|
+
}
|
|
39
|
+
return legacy;
|
|
40
|
+
}
|
|
27
41
|
return null;
|
|
28
42
|
}
|
|
43
|
+
// Fallback: encrypted file (Linux, etc.)
|
|
44
|
+
return loadLegacyCredentials();
|
|
29
45
|
}
|
|
30
46
|
/**
|
|
31
|
-
* Save credentials to
|
|
47
|
+
* Save credentials to OS keychain (or fallback file).
|
|
32
48
|
*/
|
|
33
49
|
export function saveCredentials(credentials) {
|
|
50
|
+
if (keychainAvailable()) {
|
|
51
|
+
keychainSet(KEYCHAIN_ACCOUNT, credentials);
|
|
52
|
+
// Clean up legacy file if it exists
|
|
53
|
+
if (existsSync(LEGACY_CREDENTIALS_PATH)) {
|
|
54
|
+
try {
|
|
55
|
+
unlinkSync(LEGACY_CREDENTIALS_PATH);
|
|
56
|
+
}
|
|
57
|
+
catch { /* ignore */ }
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Fallback: encrypted file
|
|
34
62
|
ensureSonomaDir();
|
|
35
63
|
const data = JSON.stringify(credentials, null, 2);
|
|
36
64
|
const encrypted = encrypt(data);
|
|
37
|
-
writeFileSync(
|
|
65
|
+
writeFileSync(LEGACY_CREDENTIALS_PATH, encrypted, { mode: 0o600 });
|
|
38
66
|
}
|
|
39
67
|
/**
|
|
40
|
-
* Clear all stored credentials
|
|
68
|
+
* Clear all stored credentials.
|
|
41
69
|
*/
|
|
42
70
|
export function clearCredentials() {
|
|
43
|
-
if (
|
|
44
|
-
|
|
71
|
+
if (keychainAvailable()) {
|
|
72
|
+
try {
|
|
73
|
+
keychainDelete(KEYCHAIN_ACCOUNT);
|
|
74
|
+
}
|
|
75
|
+
catch { /* ignore */ }
|
|
76
|
+
}
|
|
77
|
+
// Always clean up legacy file too
|
|
78
|
+
if (existsSync(LEGACY_CREDENTIALS_PATH)) {
|
|
79
|
+
unlinkSync(LEGACY_CREDENTIALS_PATH);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Load from legacy encrypted file (pre-keychain migration).
|
|
84
|
+
*/
|
|
85
|
+
function loadLegacyCredentials() {
|
|
86
|
+
if (!existsSync(LEGACY_CREDENTIALS_PATH)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const encryptedData = readFileSync(LEGACY_CREDENTIALS_PATH, "utf-8");
|
|
91
|
+
const decrypted = decrypt(encryptedData);
|
|
92
|
+
return JSON.parse(decrypted);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
45
96
|
}
|
|
46
97
|
}
|
|
47
98
|
/**
|
package/dist/auth/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,8 @@ export interface AuthenticateUpstreamOptions {
|
|
|
20
20
|
clientId: string;
|
|
21
21
|
callbackPort?: number;
|
|
22
22
|
};
|
|
23
|
+
/** Override the default callback port (19843). Used to give each server a unique port for parallel auth. */
|
|
24
|
+
callbackPort?: number;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Authenticate with an upstream MCP server that requires OAuth.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-oauth.d.ts","sourceRoot":"","sources":["../../src/auth/upstream-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAMpE,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8GAA8G;IAC9G,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2FAA2F;IAC3F,KAAK,CAAC,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;
|
|
1
|
+
{"version":3,"file":"upstream-oauth.d.ts","sourceRoot":"","sources":["../../src/auth/upstream-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAMpE,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8GAA8G;IAC9G,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2FAA2F;IAC3F,KAAK,CAAC,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAwGhC"}
|
|
@@ -33,7 +33,9 @@ export async function authenticateUpstream(options) {
|
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
store.setServerName(serverUrl, serverName);
|
|
36
|
-
|
|
36
|
+
// Pre-registered OAuth callbackPort takes priority because the redirect URI
|
|
37
|
+
// is fixed in the OAuth provider's app config (e.g., Slack only allows port 3118)
|
|
38
|
+
const callbackPort = oauth?.callbackPort ?? options.callbackPort ?? UPSTREAM_CALLBACK_PORT;
|
|
37
39
|
const provider = new UpstreamOAuthProvider({
|
|
38
40
|
serverUrl,
|
|
39
41
|
store,
|
|
@@ -56,17 +58,49 @@ export async function authenticateUpstream(options) {
|
|
|
56
58
|
throw new Error(`Auth flow returned REDIRECT but no authorization URL was captured for ${serverName}`);
|
|
57
59
|
}
|
|
58
60
|
const authUrl = provider.pendingAuthorizationUrl.toString();
|
|
61
|
+
// Try to start callback server (acquires the port atomically)
|
|
62
|
+
let handle;
|
|
63
|
+
try {
|
|
64
|
+
handle = await startCallbackServer({
|
|
65
|
+
port: callbackPort,
|
|
66
|
+
debug,
|
|
67
|
+
serverName,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
// Port taken: another gateway may be authenticating this server, or a
|
|
72
|
+
// stale process is holding the port. Wait briefly (15s) for tokens to
|
|
73
|
+
// appear; if not, try to take the port anyway (the stale process may
|
|
74
|
+
// have died by then).
|
|
75
|
+
if (err instanceof Error && err.message.includes("already in use")) {
|
|
76
|
+
log(`Port ${callbackPort} in use for ${serverName}, waiting briefly for external auth...`);
|
|
77
|
+
console.error(`\nPort ${callbackPort} in use. Waiting briefly for ${serverName} auth to complete...`);
|
|
78
|
+
const completed = await waitForUpstreamAuth(store, serverUrl, callbackPort, debug, 15_000);
|
|
79
|
+
if (completed) {
|
|
80
|
+
log(`Upstream auth for ${serverName} completed by another process`);
|
|
81
|
+
const retryResult = await auth(provider, { serverUrl });
|
|
82
|
+
if (retryResult === "AUTHORIZED")
|
|
83
|
+
return provider;
|
|
84
|
+
}
|
|
85
|
+
log(`External auth did not complete for ${serverName}, retrying port...`);
|
|
86
|
+
handle = await startCallbackServer({
|
|
87
|
+
port: callbackPort,
|
|
88
|
+
debug,
|
|
89
|
+
serverName,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Set auth URL on handle so the timeout page can offer a retry button
|
|
97
|
+
handle.setAuthUrl(authUrl);
|
|
98
|
+
// Port acquired: now safe to open browser (no duplicate tabs possible)
|
|
59
99
|
console.error(`\nOpening browser to authenticate with ${serverName}...`);
|
|
60
100
|
console.error(`If browser doesn't open, visit:\n${authUrl}\n`);
|
|
61
|
-
// Start callback server and open browser
|
|
62
|
-
const callbackPromise = startCallbackServer({
|
|
63
|
-
port: callbackPort,
|
|
64
|
-
debug,
|
|
65
|
-
serverName,
|
|
66
|
-
});
|
|
67
101
|
await openBrowser(authUrl);
|
|
68
102
|
// Wait for the authorization code
|
|
69
|
-
const callbackResult = await
|
|
103
|
+
const callbackResult = await handle.waitForCallback();
|
|
70
104
|
// State validation is handled internally by the MCP SDK's auth() function.
|
|
71
105
|
// The provider's codeVerifier storage implicitly binds the session.
|
|
72
106
|
log(`Received authorization code from ${serverName}`);
|
|
@@ -81,4 +115,20 @@ export async function authenticateUpstream(options) {
|
|
|
81
115
|
log(`Successfully authenticated with ${serverName}`);
|
|
82
116
|
return provider;
|
|
83
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Wait for another gateway process to complete upstream OAuth.
|
|
120
|
+
* Polls the token store until tokens appear or timeout.
|
|
121
|
+
*/
|
|
122
|
+
async function waitForUpstreamAuth(store, serverUrl, _port, _debug, timeoutMs = 5 * 60 * 1000) {
|
|
123
|
+
const start = Date.now();
|
|
124
|
+
const pollInterval = 2000;
|
|
125
|
+
while (Date.now() - start < timeoutMs) {
|
|
126
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
127
|
+
const tokens = store.getTokens(serverUrl);
|
|
128
|
+
if (tokens?.access_token) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
84
134
|
//# sourceMappingURL=upstream-oauth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-oauth.js","sourceRoot":"","sources":["../../src/auth/upstream-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,0CAA0C,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,sBAAsB,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"upstream-oauth.js","sourceRoot":"","sources":["../../src/auth/upstream-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-server token storage for upstream MCP servers that require OAuth.
|
|
3
3
|
*
|
|
4
|
-
* Stores credentials in
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Stores credentials in the OS keychain (macOS Keychain, Windows Credential Manager),
|
|
5
|
+
* keyed by server URL origin (e.g., "https://mcp.slack.com").
|
|
6
|
+
*
|
|
7
|
+
* Falls back to encrypted file storage on unsupported platforms.
|
|
7
8
|
*/
|
|
8
9
|
import type { OAuthTokens, OAuthClientInformationMixed } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
9
10
|
export declare class UpstreamTokenStore {
|
|
10
|
-
private
|
|
11
|
-
private
|
|
12
|
-
private
|
|
11
|
+
private useKeychain;
|
|
12
|
+
private fileStore;
|
|
13
|
+
private migrated;
|
|
14
|
+
constructor();
|
|
15
|
+
/**
|
|
16
|
+
* Migrate credentials from legacy encrypted file to OS keychain.
|
|
17
|
+
*/
|
|
18
|
+
private migrateLegacy;
|
|
19
|
+
private getServerFromKeychain;
|
|
20
|
+
private saveServerToKeychain;
|
|
21
|
+
private loadFile;
|
|
22
|
+
private saveFile;
|
|
23
|
+
private getServerFromFile;
|
|
13
24
|
private getServer;
|
|
25
|
+
private saveServer;
|
|
14
26
|
getTokens(serverUrl: string): OAuthTokens | undefined;
|
|
15
27
|
saveTokens(serverUrl: string, tokens: OAuthTokens): void;
|
|
16
28
|
clearTokens(serverUrl: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-token-store.d.ts","sourceRoot":"","sources":["../../src/auth/upstream-token-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"upstream-token-store.d.ts","sourceRoot":"","sources":["../../src/auth/upstream-token-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AA4BzG,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,WAAW,CAAU;IAE7B,OAAO,CAAC,SAAS,CAAwC;IAEzD,OAAO,CAAC,QAAQ,CAAS;;IAYzB;;OAEG;IACH,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,QAAQ;IAmBhB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,UAAU;IAWlB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIrD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IAOxD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMpC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS;IAIzE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,GAAG,IAAI;IAM1E,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMxC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAItD,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAM3D,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMpD,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAYjC,mDAAmD;IACnD,eAAe,IAAI,IAAI;CAQxB"}
|
|
@@ -1,53 +1,131 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-server token storage for upstream MCP servers that require OAuth.
|
|
3
3
|
*
|
|
4
|
-
* Stores credentials in
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Stores credentials in the OS keychain (macOS Keychain, Windows Credential Manager),
|
|
5
|
+
* keyed by server URL origin (e.g., "https://mcp.slack.com").
|
|
6
|
+
*
|
|
7
|
+
* Falls back to encrypted file storage on unsupported platforms.
|
|
7
8
|
*/
|
|
8
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
9
10
|
import { join } from "node:path";
|
|
10
11
|
import { SONOMA_DIR, ensureSonomaDir, encrypt, decrypt } from "./crypto.js";
|
|
11
|
-
|
|
12
|
+
import { keychainAvailable, keychainGet, keychainSet, keychainDelete, keychainDeleteAll } from "./keychain.js";
|
|
13
|
+
// Legacy file path (for migration from encrypted file)
|
|
14
|
+
const LEGACY_CREDENTIALS_PATH = join(SONOMA_DIR, "upstream-credentials.json");
|
|
12
15
|
function getServerKey(serverUrl) {
|
|
13
16
|
const url = new URL(serverUrl);
|
|
14
17
|
return url.origin;
|
|
15
18
|
}
|
|
19
|
+
function keychainAccount(serverKey) {
|
|
20
|
+
return `upstream:${serverKey}`;
|
|
21
|
+
}
|
|
16
22
|
export class UpstreamTokenStore {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
useKeychain;
|
|
24
|
+
// In-memory cache for file-based fallback only
|
|
25
|
+
fileStore = null;
|
|
26
|
+
// Track whether legacy migration has been attempted
|
|
27
|
+
migrated = false;
|
|
28
|
+
constructor() {
|
|
29
|
+
this.useKeychain = keychainAvailable();
|
|
30
|
+
// Migrate legacy file to keychain on first use
|
|
31
|
+
if (this.useKeychain && !this.migrated) {
|
|
32
|
+
this.migrateLegacy();
|
|
33
|
+
this.migrated = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Migrate credentials from legacy encrypted file to OS keychain.
|
|
38
|
+
*/
|
|
39
|
+
migrateLegacy() {
|
|
40
|
+
if (!existsSync(LEGACY_CREDENTIALS_PATH))
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
const encryptedData = readFileSync(LEGACY_CREDENTIALS_PATH, "utf-8");
|
|
44
|
+
const decrypted = decrypt(encryptedData);
|
|
45
|
+
const store = JSON.parse(decrypted);
|
|
46
|
+
if (store.servers) {
|
|
47
|
+
for (const [key, creds] of Object.entries(store.servers)) {
|
|
48
|
+
try {
|
|
49
|
+
keychainSet(keychainAccount(key), creds);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Skip entries that fail to migrate
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Clean up legacy file after successful migration
|
|
57
|
+
unlinkSync(LEGACY_CREDENTIALS_PATH);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Migration failed, legacy file may be corrupt
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Keychain-backed operations
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
getServerFromKeychain(serverUrl) {
|
|
67
|
+
const key = getServerKey(serverUrl);
|
|
68
|
+
return keychainGet(keychainAccount(key)) ?? {};
|
|
69
|
+
}
|
|
70
|
+
saveServerToKeychain(serverUrl, creds) {
|
|
71
|
+
const key = getServerKey(serverUrl);
|
|
72
|
+
keychainSet(keychainAccount(key), creds);
|
|
73
|
+
}
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// File-backed fallback (Linux, etc.)
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
loadFile() {
|
|
78
|
+
if (this.fileStore)
|
|
79
|
+
return this.fileStore;
|
|
80
|
+
if (!existsSync(LEGACY_CREDENTIALS_PATH)) {
|
|
81
|
+
this.fileStore = { servers: {} };
|
|
82
|
+
return this.fileStore;
|
|
24
83
|
}
|
|
25
84
|
try {
|
|
26
|
-
const encryptedData = readFileSync(
|
|
85
|
+
const encryptedData = readFileSync(LEGACY_CREDENTIALS_PATH, "utf-8");
|
|
27
86
|
const decrypted = decrypt(encryptedData);
|
|
28
|
-
this.
|
|
29
|
-
return this.
|
|
87
|
+
this.fileStore = JSON.parse(decrypted);
|
|
88
|
+
return this.fileStore;
|
|
30
89
|
}
|
|
31
90
|
catch {
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
return this.store;
|
|
91
|
+
this.fileStore = { servers: {} };
|
|
92
|
+
return this.fileStore;
|
|
35
93
|
}
|
|
36
94
|
}
|
|
37
|
-
|
|
95
|
+
saveFile() {
|
|
38
96
|
ensureSonomaDir();
|
|
39
|
-
const data = JSON.stringify(this.
|
|
97
|
+
const data = JSON.stringify(this.loadFile(), null, 2);
|
|
40
98
|
const encrypted = encrypt(data);
|
|
41
|
-
writeFileSync(
|
|
99
|
+
writeFileSync(LEGACY_CREDENTIALS_PATH, encrypted, { mode: 0o600 });
|
|
42
100
|
}
|
|
43
|
-
|
|
44
|
-
const store = this.
|
|
101
|
+
getServerFromFile(serverUrl) {
|
|
102
|
+
const store = this.loadFile();
|
|
45
103
|
const key = getServerKey(serverUrl);
|
|
46
104
|
if (!store.servers[key]) {
|
|
47
105
|
store.servers[key] = {};
|
|
48
106
|
}
|
|
49
107
|
return store.servers[key];
|
|
50
108
|
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Public API (delegates to keychain or file based on platform)
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
getServer(serverUrl) {
|
|
113
|
+
if (this.useKeychain) {
|
|
114
|
+
return this.getServerFromKeychain(serverUrl);
|
|
115
|
+
}
|
|
116
|
+
return this.getServerFromFile(serverUrl);
|
|
117
|
+
}
|
|
118
|
+
saveServer(serverUrl, creds) {
|
|
119
|
+
if (this.useKeychain) {
|
|
120
|
+
this.saveServerToKeychain(serverUrl, creds);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
const store = this.loadFile();
|
|
124
|
+
const key = getServerKey(serverUrl);
|
|
125
|
+
store.servers[key] = creds;
|
|
126
|
+
this.saveFile();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
51
129
|
getTokens(serverUrl) {
|
|
52
130
|
return this.getServer(serverUrl).tokens;
|
|
53
131
|
}
|
|
@@ -55,12 +133,12 @@ export class UpstreamTokenStore {
|
|
|
55
133
|
const server = this.getServer(serverUrl);
|
|
56
134
|
server.tokens = tokens;
|
|
57
135
|
server.lastAuthenticated = Date.now();
|
|
58
|
-
this.
|
|
136
|
+
this.saveServer(serverUrl, server);
|
|
59
137
|
}
|
|
60
138
|
clearTokens(serverUrl) {
|
|
61
139
|
const server = this.getServer(serverUrl);
|
|
62
140
|
delete server.tokens;
|
|
63
|
-
this.
|
|
141
|
+
this.saveServer(serverUrl, server);
|
|
64
142
|
}
|
|
65
143
|
getClientInfo(serverUrl) {
|
|
66
144
|
return this.getServer(serverUrl).clientInfo;
|
|
@@ -68,12 +146,12 @@ export class UpstreamTokenStore {
|
|
|
68
146
|
saveClientInfo(serverUrl, info) {
|
|
69
147
|
const server = this.getServer(serverUrl);
|
|
70
148
|
server.clientInfo = info;
|
|
71
|
-
this.
|
|
149
|
+
this.saveServer(serverUrl, server);
|
|
72
150
|
}
|
|
73
151
|
clearClientInfo(serverUrl) {
|
|
74
152
|
const server = this.getServer(serverUrl);
|
|
75
153
|
delete server.clientInfo;
|
|
76
|
-
this.
|
|
154
|
+
this.saveServer(serverUrl, server);
|
|
77
155
|
}
|
|
78
156
|
getCodeVerifier(serverUrl) {
|
|
79
157
|
return this.getServer(serverUrl).codeVerifier;
|
|
@@ -81,26 +159,40 @@ export class UpstreamTokenStore {
|
|
|
81
159
|
saveCodeVerifier(serverUrl, verifier) {
|
|
82
160
|
const server = this.getServer(serverUrl);
|
|
83
161
|
server.codeVerifier = verifier;
|
|
84
|
-
this.
|
|
162
|
+
this.saveServer(serverUrl, server);
|
|
85
163
|
}
|
|
86
164
|
setServerName(serverUrl, name) {
|
|
87
165
|
const server = this.getServer(serverUrl);
|
|
88
166
|
server.serverName = name;
|
|
89
|
-
this.
|
|
167
|
+
this.saveServer(serverUrl, server);
|
|
90
168
|
}
|
|
91
169
|
getLastAuthenticated(serverUrl) {
|
|
92
170
|
return this.getServer(serverUrl).lastAuthenticated;
|
|
93
171
|
}
|
|
94
172
|
clearAll(serverUrl) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
173
|
+
if (this.useKeychain) {
|
|
174
|
+
const key = getServerKey(serverUrl);
|
|
175
|
+
try {
|
|
176
|
+
keychainDelete(keychainAccount(key));
|
|
177
|
+
}
|
|
178
|
+
catch { /* ignore */ }
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const store = this.loadFile();
|
|
182
|
+
const key = getServerKey(serverUrl);
|
|
183
|
+
delete store.servers[key];
|
|
184
|
+
this.saveFile();
|
|
185
|
+
}
|
|
99
186
|
}
|
|
100
187
|
/** Clear credentials for every upstream server. */
|
|
101
188
|
clearAllServers() {
|
|
102
|
-
this.
|
|
103
|
-
|
|
189
|
+
if (this.useKeychain) {
|
|
190
|
+
keychainDeleteAll();
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
this.fileStore = { servers: {} };
|
|
194
|
+
this.saveFile();
|
|
195
|
+
}
|
|
104
196
|
}
|
|
105
197
|
}
|
|
106
198
|
//# sourceMappingURL=upstream-token-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-token-store.js","sourceRoot":"","sources":["../../src/auth/upstream-token-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"upstream-token-store.js","sourceRoot":"","sources":["../../src/auth/upstream-token-store.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -123,6 +123,7 @@ async function main() {
|
|
|
123
123
|
status: { type: "boolean" },
|
|
124
124
|
reauth: { type: "boolean" },
|
|
125
125
|
endpoint: { type: "string", short: "e" },
|
|
126
|
+
"http-proxy-port": { type: "string" },
|
|
126
127
|
},
|
|
127
128
|
strict: true,
|
|
128
129
|
});
|
|
@@ -362,6 +363,16 @@ async function main() {
|
|
|
362
363
|
}
|
|
363
364
|
}
|
|
364
365
|
}
|
|
366
|
+
// HTTP proxy mode: expose per-server HTTP endpoints for plugin URL rewriting
|
|
367
|
+
if (values["http-proxy-port"]) {
|
|
368
|
+
const port = parseInt(values["http-proxy-port"], 10);
|
|
369
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
370
|
+
console.error(`Invalid HTTP proxy port: ${values["http-proxy-port"]}`);
|
|
371
|
+
process.exit(2);
|
|
372
|
+
}
|
|
373
|
+
config.httpProxyPort = port;
|
|
374
|
+
console.error(`[cli] HTTP proxy mode enabled on port ${port}`);
|
|
375
|
+
}
|
|
365
376
|
const gateway = new McpGateway(config);
|
|
366
377
|
// Handle shutdown gracefully
|
|
367
378
|
const shutdown = async () => {
|
|
@@ -408,6 +419,11 @@ AUTH OPTIONS:
|
|
|
408
419
|
--reauth Clear upstream OAuth tokens and re-authenticate
|
|
409
420
|
-e, --endpoint <url> Sonoma API endpoint (default: https://app.sonoma.dev)
|
|
410
421
|
|
|
422
|
+
PROXY OPTIONS:
|
|
423
|
+
--http-proxy-port <port> Expose per-server HTTP endpoints for plugin proxying.
|
|
424
|
+
Each server is available at http://localhost:{port}/proxy/{name}/mcp.
|
|
425
|
+
Plugins can rewrite URLs to these endpoints to keep skills active.
|
|
426
|
+
|
|
411
427
|
QUICKSTART (zero-config):
|
|
412
428
|
Add to your ~/.cursor/mcp.json or Claude Desktop config:
|
|
413
429
|
{
|