@n24q02m/mcp-core 1.18.0-beta.2 → 1.18.0-beta.4

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.
Files changed (49) hide show
  1. package/build/auth/credential-form.js +2 -2
  2. package/build/auth/delegated-oauth-app.d.ts.map +1 -1
  3. package/build/auth/delegated-oauth-app.js +6 -2
  4. package/build/auth/delegated-oauth-app.js.map +1 -1
  5. package/build/auth/local-oauth-app.d.ts.map +1 -1
  6. package/build/auth/local-oauth-app.js +6 -2
  7. package/build/auth/local-oauth-app.js.map +1 -1
  8. package/build/auth/relay-login.d.ts.map +1 -1
  9. package/build/auth/relay-login.js +19 -21
  10. package/build/auth/relay-login.js.map +1 -1
  11. package/build/auth/well-known.d.ts.map +1 -1
  12. package/build/auth/well-known.js +1 -0
  13. package/build/auth/well-known.js.map +1 -1
  14. package/build/crypto/index.d.ts +1 -1
  15. package/build/crypto/index.d.ts.map +1 -1
  16. package/build/crypto/index.js +1 -1
  17. package/build/crypto/index.js.map +1 -1
  18. package/build/crypto/kdf.d.ts +13 -0
  19. package/build/crypto/kdf.d.ts.map +1 -1
  20. package/build/crypto/kdf.js +18 -0
  21. package/build/crypto/kdf.js.map +1 -1
  22. package/build/oauth/jwt-issuer.d.ts +50 -24
  23. package/build/oauth/jwt-issuer.d.ts.map +1 -1
  24. package/build/oauth/jwt-issuer.js +91 -29
  25. package/build/oauth/jwt-issuer.js.map +1 -1
  26. package/build/storage/backends.d.ts +56 -0
  27. package/build/storage/backends.d.ts.map +1 -0
  28. package/build/storage/backends.js +167 -0
  29. package/build/storage/backends.js.map +1 -0
  30. package/build/storage/home-dir.d.ts +11 -0
  31. package/build/storage/home-dir.d.ts.map +1 -0
  32. package/build/storage/home-dir.js +18 -0
  33. package/build/storage/home-dir.js.map +1 -0
  34. package/build/storage/index.d.ts +1 -0
  35. package/build/storage/index.d.ts.map +1 -1
  36. package/build/storage/index.js +1 -0
  37. package/build/storage/index.js.map +1 -1
  38. package/build/storage/per-plugin-store.d.ts +5 -3
  39. package/build/storage/per-plugin-store.d.ts.map +1 -1
  40. package/build/storage/per-plugin-store.js +12 -27
  41. package/build/storage/per-plugin-store.js.map +1 -1
  42. package/build/transport/local-server.d.ts.map +1 -1
  43. package/build/transport/local-server.js +2 -2
  44. package/build/transport/local-server.js.map +1 -1
  45. package/build/transport/oauth-middleware.d.ts +1 -0
  46. package/build/transport/oauth-middleware.d.ts.map +1 -1
  47. package/build/transport/oauth-middleware.js +16 -4
  48. package/build/transport/oauth-middleware.js.map +1 -1
  49. package/package.json +3 -3
@@ -1,6 +1,24 @@
1
1
  /**
2
- * RSA JWT Issuer and JWKS generation for TypeScript MCP servers.
2
+ * JWT Issuer and JWKS generation for TypeScript MCP servers.
3
3
  * Uses jose (industry-standard, zero-dep, Web Crypto compatible).
4
+ *
5
+ * Two signing modes, selected at construction:
6
+ *
7
+ * - LOCAL single-user (credentialSecret unset): RSA-2048, RS256, kid="key-1",
8
+ * keys generated on first run and persisted to disk so they survive restarts
9
+ * on a real machine. Behavior is unchanged from the pre-stability-fix code.
10
+ *
11
+ * - HTTP multi-user (credentialSecret set): Ed25519, EdDSA, signing key DERIVED
12
+ * deterministically from CREDENTIAL_SECRET via HKDF-SHA256. No disk I/O. Every
13
+ * container replica converges on the same key without a shared volume or
14
+ * external secret store, so OAuth tokens survive container recreation
15
+ * (Watchtower :latest redeploys). kid is the base64url SHA-256 thumbprint of
16
+ * the raw public key.
17
+ *
18
+ * The two modes are different deployments that never exchange tokens
19
+ * (iss/aud are server-scoped); the per-mode algorithm split is permanent and
20
+ * intentional. Each process runs exactly one algorithm; the verify path uses
21
+ * that single alg, never a {RS256, EdDSA} union.
4
22
  */
5
23
  import * as jose from 'jose';
6
24
  export declare class JWTIssuer {
@@ -8,46 +26,54 @@ export declare class JWTIssuer {
8
26
  private keysDir;
9
27
  private privateKeyPath;
10
28
  private publicKeyPath;
29
+ private credentialSecret;
30
+ alg: 'RS256' | 'EdDSA';
11
31
  private kid;
12
32
  private privateKey;
13
33
  private publicKey;
34
+ private okpX;
14
35
  private _initialized;
15
- constructor(serverName: string, keysDir?: string);
16
- /** Must call before using issuer — loads or generates RSA keys. */
36
+ constructor(serverName: string, keysDir?: string, credentialSecret?: string | null);
37
+ /** Must call before using issuer — loads/generates (RSA) or derives (EdDSA) keys. */
17
38
  init(): Promise<void>;
18
- /** Return JWKS payload for /.well-known/jwks.json */
39
+ private deriveEddsaKeys;
40
+ private loadOrGenerateRsaKeys;
41
+ /**
42
+ * Return JWKS payload for /.well-known/jwks.json. Always a `keys` array
43
+ * (multi-key-aware for future rotation). RSA mode emits an RSA JWK; EdDSA
44
+ * mode emits an OKP JWK.
45
+ */
19
46
  getJwks(): Promise<jose.JSONWebKeySet>;
20
- /** Issue an RS256 JWT access token (``typ="access"``). */
47
+ /** Issue a JWT access token (typ="access") signed with the active alg. */
21
48
  issueAccessToken(sub: string, expiresInSeconds?: number): Promise<string>;
22
49
  /**
23
- * Issue an RS256 JWT refresh token (``typ="refresh"``).
50
+ * Issue a JWT refresh token (typ="refresh") signed with the active alg.
24
51
  *
25
52
  * Defaults to a 30-day (2592000s) lifetime so long-running MCP clients can
26
- * mint fresh access tokens without forcing the user back through the
27
- * browser PKCE flow every hour. Same key / iss / aud as access tokens; the
28
- * ``typ`` claim is the only thing distinguishing them, and
29
- * ``verifyAccessToken`` rejects ``typ="refresh"`` so a refresh token can
30
- * never be used as an access token at the ``/mcp`` resource.
53
+ * mint fresh access tokens without forcing the user back through the browser
54
+ * PKCE flow every hour. Same key / iss / aud as access tokens; the typ claim
55
+ * is the only thing distinguishing them, and verifyAccessToken rejects
56
+ * typ="refresh" so a refresh token can never be used as an access token at
57
+ * the /mcp resource.
31
58
  */
32
59
  issueRefreshToken(sub: string, expiresInSeconds?: number): Promise<string>;
33
60
  /**
34
- * Verify a JWT access token and return its payload. Throws on failure
35
- * (bad signature, wrong issuer/audience, expired) — the same jose errors
36
- * existing callers already catch. Additionally rejects tokens whose
37
- * ``typ`` claim is ``"refresh"`` (throwing ``jose.errors.JWTClaimValidationFailed``)
38
- * so a refresh token cannot be replayed as an access token. Tokens with
39
- * ``typ="access"`` OR a missing ``typ`` claim are accepted (the latter
40
- * keeps already-issued pre-refresh-support tokens valid).
61
+ * Verify a JWT access token and return its payload. Throws on failure (bad
62
+ * signature, wrong issuer/audience, expired) — the same jose errors existing
63
+ * callers already catch. The verify path uses the single active algorithm,
64
+ * never a union. Additionally rejects tokens whose typ claim is "refresh"
65
+ * (throwing jose.errors.JWTClaimValidationFailed) so a refresh token cannot
66
+ * be replayed as an access token. Tokens with typ="access" OR a missing typ
67
+ * claim are accepted (the latter keeps already-issued pre-refresh-support
68
+ * tokens valid).
41
69
  */
42
70
  verifyAccessToken(token: string): Promise<jose.JWTPayload>;
43
71
  /**
44
72
  * Verify a JWT refresh token and return its payload. Same key / audience /
45
- * issuer checks as ``verifyAccessToken`` and throws the same jose errors on
46
- * failure. Additionally asserts ``typ=="refresh"`` (throwing
47
- * ``jose.errors.JWTClaimValidationFailed`` otherwise) so an access token
48
- * can never be exchanged at the refresh grant. ``JWTClaimValidationFailed``
49
- * extends ``jose.errors.JOSEError``, so callers' existing ``catch`` clauses
50
- * around ``verifyAccessToken`` already catch it.
73
+ * issuer checks as verifyAccessToken and throws the same jose errors on
74
+ * failure; uses the single active algorithm. Additionally asserts
75
+ * typ=="refresh" (throwing jose.errors.JWTClaimValidationFailed otherwise) so
76
+ * an access token can never be exchanged at the refresh grant.
51
77
  */
52
78
  verifyRefreshToken(token: string): Promise<jose.JWTPayload>;
53
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-issuer.d.ts","sourceRoot":"","sources":["../../src/oauth/jwt-issuer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAI5B,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,YAAY,CAAQ;gBAEhB,UAAU,EAAE,MAAM,EAAE,OAAO,SAAmB;IAO1D,mEAAmE;IAC7D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B3B,qDAAqD;IAC/C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC;IAS5C,0DAA0D;IACpD,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAW7E;;;;;;;;;OASG;IACG,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,SAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAWjF;;;;;;;;OAQG;IACG,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;IAYhE;;;;;;;;OAQG;IACG,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;CAWlE"}
1
+ {"version":3,"file":"jwt-issuer.d.ts","sourceRoot":"","sources":["../../src/oauth/jwt-issuer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAU5B,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,gBAAgB,CAAe;IACvC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAA;IACtB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,YAAY,CAAQ;gBAEhB,UAAU,EAAE,MAAM,EAAE,OAAO,SAAmB,EAAE,gBAAgB,GAAE,MAAM,GAAG,IAAW;IASlG,qFAAqF;IAC/E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAgBb,eAAe;YAYf,qBAAqB;IA0BnC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC;IAc5C,0EAA0E;IACpE,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAW7E;;;;;;;;;OASG;IACG,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,SAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAWjF;;;;;;;;;OASG;IACG,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;IAahE;;;;;;OAMG;IACG,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;CAYlE"}
@@ -1,27 +1,80 @@
1
1
  /**
2
- * RSA JWT Issuer and JWKS generation for TypeScript MCP servers.
2
+ * JWT Issuer and JWKS generation for TypeScript MCP servers.
3
3
  * Uses jose (industry-standard, zero-dep, Web Crypto compatible).
4
+ *
5
+ * Two signing modes, selected at construction:
6
+ *
7
+ * - LOCAL single-user (credentialSecret unset): RSA-2048, RS256, kid="key-1",
8
+ * keys generated on first run and persisted to disk so they survive restarts
9
+ * on a real machine. Behavior is unchanged from the pre-stability-fix code.
10
+ *
11
+ * - HTTP multi-user (credentialSecret set): Ed25519, EdDSA, signing key DERIVED
12
+ * deterministically from CREDENTIAL_SECRET via HKDF-SHA256. No disk I/O. Every
13
+ * container replica converges on the same key without a shared volume or
14
+ * external secret store, so OAuth tokens survive container recreation
15
+ * (Watchtower :latest redeploys). kid is the base64url SHA-256 thumbprint of
16
+ * the raw public key.
17
+ *
18
+ * The two modes are different deployments that never exchange tokens
19
+ * (iss/aud are server-scoped); the per-mode algorithm split is permanent and
20
+ * intentional. Each process runs exactly one algorithm; the verify path uses
21
+ * that single alg, never a {RS256, EdDSA} union.
4
22
  */
23
+ import { createHash, createPrivateKey } from 'node:crypto';
5
24
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
6
25
  import { homedir } from 'node:os';
7
26
  import { join } from 'node:path';
8
27
  import * as jose from 'jose';
28
+ import { deriveJwtSigningSeed } from '../crypto/kdf.js';
9
29
  const DEFAULT_KEYS_DIR = join(homedir(), '.mcp-core', 'jwt-keys');
30
+ // RFC 8410 PKCS8 DER prefix for an Ed25519 private key. Prepended to the
31
+ // 32-byte HKDF seed to build an importable PKCS8 key (jose 6.2.3 cannot import
32
+ // a raw seed via importJWK d-only).
33
+ const ED25519_PKCS8_PREFIX = Buffer.from('302e020100300506032b657004220420', 'hex');
10
34
  export class JWTIssuer {
11
- constructor(serverName, keysDir = DEFAULT_KEYS_DIR) {
35
+ constructor(serverName, keysDir = DEFAULT_KEYS_DIR, credentialSecret = null) {
12
36
  this.kid = 'key-1';
13
37
  this.privateKey = null;
14
38
  this.publicKey = null;
39
+ this.okpX = null;
15
40
  this._initialized = false;
16
41
  this.serverName = serverName;
17
42
  this.keysDir = keysDir;
43
+ this.credentialSecret = credentialSecret;
44
+ this.alg = credentialSecret ? 'EdDSA' : 'RS256';
18
45
  this.privateKeyPath = join(this.keysDir, `${serverName}_private.pem`);
19
46
  this.publicKeyPath = join(this.keysDir, `${serverName}_public.pem`);
20
47
  }
21
- /** Must call before using issuer — loads or generates RSA keys. */
48
+ /** Must call before using issuer — loads/generates (RSA) or derives (EdDSA) keys. */
22
49
  async init() {
23
50
  if (this._initialized)
24
51
  return;
52
+ if (this.credentialSecret) {
53
+ await this.deriveEddsaKeys(this.credentialSecret);
54
+ // Don't name the source env var in the message: only serverName + the
55
+ // public kid thumbprint are logged (never the secret), and the literal
56
+ // "CREDENTIAL_SECRET" token trips the SAST logger-credential heuristic
57
+ // for no real benefit (parity with core-py jwt_issuer.py).
58
+ console.error(`JWTIssuer[${this.serverName}]: HTTP multi-user mode, EdDSA signing key derived (kid=${this.kid})`);
59
+ }
60
+ else {
61
+ await this.loadOrGenerateRsaKeys();
62
+ console.error(`JWTIssuer[${this.serverName}]: local single-user mode, RS256 key on disk (${this.keysDir})`);
63
+ }
64
+ this._initialized = true;
65
+ }
66
+ async deriveEddsaKeys(credentialSecret) {
67
+ const seed = deriveJwtSigningSeed(credentialSecret, this.serverName);
68
+ const der = Buffer.concat([ED25519_PKCS8_PREFIX, seed]);
69
+ const pem = `-----BEGIN PRIVATE KEY-----\n${der.toString('base64')}\n-----END PRIVATE KEY-----\n`;
70
+ this.privateKey = await jose.importPKCS8(pem, 'EdDSA', { extractable: false });
71
+ // Recover the public JWK x via node:crypto (jose has no seed->public path).
72
+ const jwk = createPrivateKey(pem).export({ format: 'jwk' });
73
+ this.okpX = jwk.x;
74
+ this.publicKey = (await jose.importJWK({ kty: 'OKP', crv: 'Ed25519', x: jwk.x }, 'EdDSA'));
75
+ this.kid = createHash('sha256').update(Buffer.from(jwk.x, 'base64url')).digest('base64url').slice(0, 16);
76
+ }
77
+ async loadOrGenerateRsaKeys() {
25
78
  mkdirSync(this.keysDir, { recursive: true, mode: 0o700 });
26
79
  if (existsSync(this.privateKeyPath) && existsSync(this.publicKeyPath)) {
27
80
  const privatePem = readFileSync(this.privateKeyPath, 'utf-8');
@@ -42,24 +95,32 @@ export class JWTIssuer {
42
95
  this.privateKey = await jose.importPKCS8(privatePem, 'RS256', { extractable: false });
43
96
  this.publicKey = await jose.importSPKI(publicPem, 'RS256', { extractable: true });
44
97
  }
45
- this._initialized = true;
46
98
  }
47
- /** Return JWKS payload for /.well-known/jwks.json */
99
+ /**
100
+ * Return JWKS payload for /.well-known/jwks.json. Always a `keys` array
101
+ * (multi-key-aware for future rotation). RSA mode emits an RSA JWK; EdDSA
102
+ * mode emits an OKP JWK.
103
+ */
48
104
  async getJwks() {
49
105
  if (!this.publicKey)
50
106
  throw new Error('JWTIssuer not initialized');
107
+ if (this.alg === 'EdDSA') {
108
+ return {
109
+ keys: [{ kty: 'OKP', crv: 'Ed25519', x: this.okpX, use: 'sig', alg: 'EdDSA', kid: this.kid }]
110
+ };
111
+ }
51
112
  const jwk = await jose.exportJWK(this.publicKey);
52
113
  jwk.kid = this.kid;
53
114
  jwk.use = 'sig';
54
115
  jwk.alg = 'RS256';
55
116
  return { keys: [jwk] };
56
117
  }
57
- /** Issue an RS256 JWT access token (``typ="access"``). */
118
+ /** Issue a JWT access token (typ="access") signed with the active alg. */
58
119
  async issueAccessToken(sub, expiresInSeconds = 3600) {
59
120
  if (!this.privateKey)
60
121
  throw new Error('JWTIssuer not initialized');
61
122
  return new jose.SignJWT({ sub, typ: 'access' })
62
- .setProtectedHeader({ alg: 'RS256', kid: this.kid })
123
+ .setProtectedHeader({ alg: this.alg, kid: this.kid })
63
124
  .setIssuer(this.serverName)
64
125
  .setAudience(this.serverName)
65
126
  .setIssuedAt()
@@ -67,20 +128,20 @@ export class JWTIssuer {
67
128
  .sign(this.privateKey);
68
129
  }
69
130
  /**
70
- * Issue an RS256 JWT refresh token (``typ="refresh"``).
131
+ * Issue a JWT refresh token (typ="refresh") signed with the active alg.
71
132
  *
72
133
  * Defaults to a 30-day (2592000s) lifetime so long-running MCP clients can
73
- * mint fresh access tokens without forcing the user back through the
74
- * browser PKCE flow every hour. Same key / iss / aud as access tokens; the
75
- * ``typ`` claim is the only thing distinguishing them, and
76
- * ``verifyAccessToken`` rejects ``typ="refresh"`` so a refresh token can
77
- * never be used as an access token at the ``/mcp`` resource.
134
+ * mint fresh access tokens without forcing the user back through the browser
135
+ * PKCE flow every hour. Same key / iss / aud as access tokens; the typ claim
136
+ * is the only thing distinguishing them, and verifyAccessToken rejects
137
+ * typ="refresh" so a refresh token can never be used as an access token at
138
+ * the /mcp resource.
78
139
  */
79
140
  async issueRefreshToken(sub, expiresInSeconds = 2592000) {
80
141
  if (!this.privateKey)
81
142
  throw new Error('JWTIssuer not initialized');
82
143
  return new jose.SignJWT({ sub, typ: 'refresh' })
83
- .setProtectedHeader({ alg: 'RS256', kid: this.kid })
144
+ .setProtectedHeader({ alg: this.alg, kid: this.kid })
84
145
  .setIssuer(this.serverName)
85
146
  .setAudience(this.serverName)
86
147
  .setIssuedAt()
@@ -88,20 +149,22 @@ export class JWTIssuer {
88
149
  .sign(this.privateKey);
89
150
  }
90
151
  /**
91
- * Verify a JWT access token and return its payload. Throws on failure
92
- * (bad signature, wrong issuer/audience, expired) — the same jose errors
93
- * existing callers already catch. Additionally rejects tokens whose
94
- * ``typ`` claim is ``"refresh"`` (throwing ``jose.errors.JWTClaimValidationFailed``)
95
- * so a refresh token cannot be replayed as an access token. Tokens with
96
- * ``typ="access"`` OR a missing ``typ`` claim are accepted (the latter
97
- * keeps already-issued pre-refresh-support tokens valid).
152
+ * Verify a JWT access token and return its payload. Throws on failure (bad
153
+ * signature, wrong issuer/audience, expired) — the same jose errors existing
154
+ * callers already catch. The verify path uses the single active algorithm,
155
+ * never a union. Additionally rejects tokens whose typ claim is "refresh"
156
+ * (throwing jose.errors.JWTClaimValidationFailed) so a refresh token cannot
157
+ * be replayed as an access token. Tokens with typ="access" OR a missing typ
158
+ * claim are accepted (the latter keeps already-issued pre-refresh-support
159
+ * tokens valid).
98
160
  */
99
161
  async verifyAccessToken(token) {
100
162
  if (!this.publicKey)
101
163
  throw new Error('JWTIssuer not initialized');
102
164
  const { payload } = await jose.jwtVerify(token, this.publicKey, {
103
165
  issuer: this.serverName,
104
- audience: this.serverName
166
+ audience: this.serverName,
167
+ algorithms: [this.alg]
105
168
  });
106
169
  if (payload.typ === 'refresh') {
107
170
  throw new jose.errors.JWTClaimValidationFailed('Refresh token cannot be used as an access token', payload, 'typ');
@@ -110,19 +173,18 @@ export class JWTIssuer {
110
173
  }
111
174
  /**
112
175
  * Verify a JWT refresh token and return its payload. Same key / audience /
113
- * issuer checks as ``verifyAccessToken`` and throws the same jose errors on
114
- * failure. Additionally asserts ``typ=="refresh"`` (throwing
115
- * ``jose.errors.JWTClaimValidationFailed`` otherwise) so an access token
116
- * can never be exchanged at the refresh grant. ``JWTClaimValidationFailed``
117
- * extends ``jose.errors.JOSEError``, so callers' existing ``catch`` clauses
118
- * around ``verifyAccessToken`` already catch it.
176
+ * issuer checks as verifyAccessToken and throws the same jose errors on
177
+ * failure; uses the single active algorithm. Additionally asserts
178
+ * typ=="refresh" (throwing jose.errors.JWTClaimValidationFailed otherwise) so
179
+ * an access token can never be exchanged at the refresh grant.
119
180
  */
120
181
  async verifyRefreshToken(token) {
121
182
  if (!this.publicKey)
122
183
  throw new Error('JWTIssuer not initialized');
123
184
  const { payload } = await jose.jwtVerify(token, this.publicKey, {
124
185
  issuer: this.serverName,
125
- audience: this.serverName
186
+ audience: this.serverName,
187
+ algorithms: [this.alg]
126
188
  });
127
189
  if (payload.typ !== 'refresh') {
128
190
  throw new jose.errors.JWTClaimValidationFailed('Token is not a refresh token', payload, 'typ');
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-issuer.js","sourceRoot":"","sources":["../../src/oauth/jwt-issuer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jwt-issuer.js","sourceRoot":"","sources":["../../src/oauth/jwt-issuer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Pluggable credential backends.
3
+ *
4
+ * A backend stores and retrieves OPAQUE ciphertext blobs keyed by a string.
5
+ * Encryption and key derivation live in PerPluginStore; backends never see
6
+ * plaintext. This seam lets MCP servers run serverless (e.g. Cloudflare Workers
7
+ * KV) while stdio/VM deployments keep the on-disk layout via LocalFsBackend.
8
+ */
9
+ /** Stores opaque ciphertext blobs keyed by a string. */
10
+ export interface CredentialBackend {
11
+ get(key: string): Promise<Buffer | null>;
12
+ put(key: string, blob: Buffer): Promise<void>;
13
+ delete(key: string): Promise<void>;
14
+ }
15
+ /** Map-backed backend, primarily for tests. */
16
+ export declare class InMemoryBackend implements CredentialBackend {
17
+ private store;
18
+ get(key: string): Promise<Buffer | null>;
19
+ put(key: string, blob: Buffer): Promise<void>;
20
+ delete(key: string): Promise<void>;
21
+ }
22
+ /** Stores blobs on local disk, preserving the per-plugin layout. */
23
+ export declare class LocalFsBackend implements CredentialBackend {
24
+ get(key: string): Promise<Buffer | null>;
25
+ put(key: string, blob: Buffer): Promise<void>;
26
+ delete(key: string): Promise<void>;
27
+ }
28
+ /** Minimal injectable HTTP client returning status + body. */
29
+ export interface Http {
30
+ request(method: string, url: string, data?: Buffer, headers?: Record<string, string>): Promise<{
31
+ status: number;
32
+ body: Buffer;
33
+ }>;
34
+ }
35
+ /**
36
+ * Stores blobs in a Cloudflare Workers KV namespace over HTTP.
37
+ *
38
+ * Error contract: 200 -> data, 404 -> absent (null for get, no-op for delete).
39
+ * Every other HTTP status, plus transport errors from the HTTP client, raises --
40
+ * failures are loud, never silently returned as data. Error messages carry the
41
+ * status only; never the token or the response body.
42
+ */
43
+ export declare class CfKvBackend implements CredentialBackend {
44
+ private readonly baseUrl;
45
+ private readonly token?;
46
+ private readonly http;
47
+ constructor(baseUrl: string, token?: string, http?: Http);
48
+ private url;
49
+ private headers;
50
+ get(key: string): Promise<Buffer | null>;
51
+ put(key: string, blob: Buffer): Promise<void>;
52
+ delete(key: string): Promise<void>;
53
+ }
54
+ /** Select a credential backend from the MCP_STORAGE_BACKEND env var. */
55
+ export declare function backendFromEnv(): CredentialBackend;
56
+ //# sourceMappingURL=backends.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backends.d.ts","sourceRoot":"","sources":["../../src/storage/backends.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACnC;AAED,+CAA+C;AAC/C,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,OAAO,CAAC,KAAK,CAA4B;IAEnC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGzC;AA0CD,oEAAoE;AACpE,qBAAa,cAAe,YAAW,iBAAiB;IAChD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQzC;AAED,8DAA8D;AAC9D,MAAM,WAAW,IAAI;IACnB,OAAO,CACL,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC7C;AAqBD;;;;;;;GAOG;AACH,qBAAa,WAAY,YAAW,iBAAiB;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAM;gBAEf,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,IAAgB;IAMnE,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,OAAO;IAIT,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAOxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMzC;AAED,wEAAwE;AACxE,wBAAgB,cAAc,IAAI,iBAAiB,CAWlD"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Pluggable credential backends.
3
+ *
4
+ * A backend stores and retrieves OPAQUE ciphertext blobs keyed by a string.
5
+ * Encryption and key derivation live in PerPluginStore; backends never see
6
+ * plaintext. This seam lets MCP servers run serverless (e.g. Cloudflare Workers
7
+ * KV) while stdio/VM deployments keep the on-disk layout via LocalFsBackend.
8
+ */
9
+ import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
10
+ import { dirname, join, resolve, sep } from 'node:path';
11
+ import { getHomeDir } from './home-dir.js';
12
+ /** Map-backed backend, primarily for tests. */
13
+ export class InMemoryBackend {
14
+ constructor() {
15
+ this.store = new Map();
16
+ }
17
+ async get(key) {
18
+ return this.store.get(key) ?? null;
19
+ }
20
+ async put(key, blob) {
21
+ this.store.set(key, blob);
22
+ }
23
+ async delete(key) {
24
+ this.store.delete(key);
25
+ }
26
+ }
27
+ function validateComponent(name) {
28
+ if (!name || name === '.' || name === '..' || name.includes('/') || name.includes('\\') || name.includes('\x00')) {
29
+ throw new Error(`Invalid path component: ${name}`);
30
+ }
31
+ return name;
32
+ }
33
+ /**
34
+ * Map a backend key to its on-disk path.
35
+ *
36
+ * "<plugin>/config" -> ~/.<plugin>-mcp/config.json
37
+ * "<plugin>/subs/<sub>/config" -> ~/.<plugin>-mcp/subs/<sub>/config.json
38
+ */
39
+ function keyToPath(key) {
40
+ const slash = key.indexOf('/');
41
+ const plugin = slash === -1 ? key : key.slice(0, slash);
42
+ const rest = slash === -1 ? '' : key.slice(slash + 1);
43
+ validateComponent(plugin);
44
+ const base = join(getHomeDir(), `.${plugin}-mcp`);
45
+ let path;
46
+ if (rest === 'config') {
47
+ path = join(base, 'config.json');
48
+ }
49
+ else if (rest.startsWith('subs/') && rest.endsWith('/config')) {
50
+ const sub = rest.slice('subs/'.length, -'/config'.length);
51
+ validateComponent(sub);
52
+ path = join(base, 'subs', sub, 'config.json');
53
+ }
54
+ else {
55
+ throw new Error(`Invalid backend key: ${key}`);
56
+ }
57
+ // Defense-in-depth: validateComponent already rejects separators/./../empty/NUL.
58
+ const baseResolved = resolve(base);
59
+ const pathResolved = resolve(path);
60
+ if (pathResolved !== baseResolved && !pathResolved.startsWith(baseResolved + sep)) {
61
+ throw new Error(`Path escapes base: ${key}`);
62
+ }
63
+ return path;
64
+ }
65
+ /** Stores blobs on local disk, preserving the per-plugin layout. */
66
+ export class LocalFsBackend {
67
+ async get(key) {
68
+ const path = keyToPath(key);
69
+ try {
70
+ return await readFile(path);
71
+ }
72
+ catch (err) {
73
+ if (err.code === 'ENOENT')
74
+ return null;
75
+ throw err;
76
+ }
77
+ }
78
+ async put(key, blob) {
79
+ const path = keyToPath(key);
80
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
81
+ await writeFile(path, blob, { mode: 0o600 });
82
+ }
83
+ async delete(key) {
84
+ const path = keyToPath(key);
85
+ try {
86
+ await unlink(path);
87
+ }
88
+ catch (err) {
89
+ if (err.code !== 'ENOENT')
90
+ throw err;
91
+ }
92
+ }
93
+ }
94
+ /**
95
+ * Default HTTP implementation over global fetch.
96
+ *
97
+ * fetch resolves on 4xx/5xx and rejects only on network error, so status checks
98
+ * in CfKvBackend handle HTTP errors while transport errors propagate -- matching
99
+ * the Python httpx client behavior.
100
+ */
101
+ const fetchHttp = {
102
+ async request(method, url, data, headers) {
103
+ const resp = await fetch(url, {
104
+ method,
105
+ body: data ? new Uint8Array(data) : undefined,
106
+ headers
107
+ });
108
+ const body = Buffer.from(await resp.arrayBuffer());
109
+ return { status: resp.status, body };
110
+ }
111
+ };
112
+ /**
113
+ * Stores blobs in a Cloudflare Workers KV namespace over HTTP.
114
+ *
115
+ * Error contract: 200 -> data, 404 -> absent (null for get, no-op for delete).
116
+ * Every other HTTP status, plus transport errors from the HTTP client, raises --
117
+ * failures are loud, never silently returned as data. Error messages carry the
118
+ * status only; never the token or the response body.
119
+ */
120
+ export class CfKvBackend {
121
+ constructor(baseUrl, token, http = fetchHttp) {
122
+ this.baseUrl = baseUrl.replace(/\/+$/, '');
123
+ this.token = token;
124
+ this.http = http;
125
+ }
126
+ url(key) {
127
+ return `${this.baseUrl}/${encodeURIComponent(key)}`;
128
+ }
129
+ headers() {
130
+ return this.token ? { Authorization: `Bearer ${this.token}` } : {};
131
+ }
132
+ async get(key) {
133
+ const { status, body } = await this.http.request('GET', this.url(key), undefined, this.headers());
134
+ if (status === 200)
135
+ return body;
136
+ if (status === 404)
137
+ return null;
138
+ throw new Error(`CfKvBackend get failed: HTTP ${status}`);
139
+ }
140
+ async put(key, blob) {
141
+ const { status } = await this.http.request('PUT', this.url(key), blob, this.headers());
142
+ if (status !== 200 && status !== 204) {
143
+ throw new Error(`CfKvBackend put failed: HTTP ${status}`);
144
+ }
145
+ }
146
+ async delete(key) {
147
+ const { status } = await this.http.request('DELETE', this.url(key), undefined, this.headers());
148
+ if (status !== 200 && status !== 204 && status !== 404) {
149
+ throw new Error(`CfKvBackend delete failed: HTTP ${status}`);
150
+ }
151
+ }
152
+ }
153
+ /** Select a credential backend from the MCP_STORAGE_BACKEND env var. */
154
+ export function backendFromEnv() {
155
+ const kind = (process.env.MCP_STORAGE_BACKEND ?? 'local').toLowerCase();
156
+ if (kind === 'local' || kind === 'local-fs' || kind === '') {
157
+ return new LocalFsBackend();
158
+ }
159
+ if (kind === 'cf-kv') {
160
+ const baseUrl = process.env.MCP_KV_BASE_URL;
161
+ if (!baseUrl)
162
+ throw new Error('MCP_KV_BASE_URL is required when MCP_STORAGE_BACKEND=cf-kv');
163
+ return new CfKvBackend(baseUrl, process.env.MCP_KV_TOKEN);
164
+ }
165
+ throw new Error(`Unknown MCP_STORAGE_BACKEND: ${kind}`);
166
+ }
167
+ //# sourceMappingURL=backends.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backends.js","sourceRoot":"","sources":["../../src/storage/backends.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Shared home-directory resolution with a testing override.
3
+ *
4
+ * Extracted out of per-plugin-store.ts so that both PerPluginStore and the
5
+ * LocalFsBackend in backends.ts can honor the same override without a circular
6
+ * value import (per-plugin-store imports backendFromEnv from backends).
7
+ */
8
+ /** Override the home directory used by storage modules. Pass null to reset. */
9
+ export declare function setHomeDirForTesting(dir: string | null): void;
10
+ export declare function getHomeDir(): string;
11
+ //# sourceMappingURL=home-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"home-dir.d.ts","sourceRoot":"","sources":["../../src/storage/home-dir.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE7D;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shared home-directory resolution with a testing override.
3
+ *
4
+ * Extracted out of per-plugin-store.ts so that both PerPluginStore and the
5
+ * LocalFsBackend in backends.ts can honor the same override without a circular
6
+ * value import (per-plugin-store imports backendFromEnv from backends).
7
+ */
8
+ import { homedir } from 'node:os';
9
+ // Module-level override for testing.
10
+ let homeDirOverride = null;
11
+ /** Override the home directory used by storage modules. Pass null to reset. */
12
+ export function setHomeDirForTesting(dir) {
13
+ homeDirOverride = dir;
14
+ }
15
+ export function getHomeDir() {
16
+ return homeDirOverride ?? homedir();
17
+ }
18
+ //# sourceMappingURL=home-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"home-dir.js","sourceRoot":"","sources":["../../src/storage/home-dir.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEjC,qCAAqC;AACrC,IAAI,eAAe,GAAkB,IAAI,CAAA;AAEzC,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,GAAkB;IACrD,eAAe,GAAG,GAAG,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,eAAe,IAAI,OAAO,EAAE,CAAA;AACrC,CAAC"}
@@ -1,3 +1,4 @@
1
+ export { backendFromEnv, CfKvBackend, type CredentialBackend, InMemoryBackend, LocalFsBackend } from './backends.js';
1
2
  export { deleteConfig, exportConfig, importConfig, listConfigs, readConfig, setConfigPath, writeConfig } from './config-file.js';
2
3
  export { decryptData, deriveFileKey, derivePassphraseKey, encryptData } from './encryption.js';
3
4
  export { getMachineId, getUsername } from './machine-id.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACZ,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACrF,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,WAAW,EAChB,UAAU,EACV,gBAAgB,EACjB,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,WAAW,EACX,KAAK,iBAAiB,EACtB,eAAe,EACf,cAAc,EACf,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACZ,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACrF,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,WAAW,EAChB,UAAU,EACV,gBAAgB,EACjB,MAAM,mBAAmB,CAAA"}