@n24q02m/mcp-core 1.18.0-beta.2 → 1.18.0-beta.21
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/build/auth/credential-form.d.ts.map +1 -1
- package/build/auth/credential-form.js +9 -6
- package/build/auth/credential-form.js.map +1 -1
- package/build/auth/delegated-oauth-app.d.ts.map +1 -1
- package/build/auth/delegated-oauth-app.js +26 -10
- package/build/auth/delegated-oauth-app.js.map +1 -1
- package/build/auth/local-oauth-app.d.ts.map +1 -1
- package/build/auth/local-oauth-app.js +27 -11
- package/build/auth/local-oauth-app.js.map +1 -1
- package/build/auth/relay-login.d.ts.map +1 -1
- package/build/auth/relay-login.js +24 -22
- package/build/auth/relay-login.js.map +1 -1
- package/build/auth/well-known.d.ts.map +1 -1
- package/build/auth/well-known.js +1 -0
- package/build/auth/well-known.js.map +1 -1
- package/build/crypto/index.d.ts +1 -1
- package/build/crypto/index.d.ts.map +1 -1
- package/build/crypto/index.js +1 -1
- package/build/crypto/index.js.map +1 -1
- package/build/crypto/kdf.d.ts +13 -0
- package/build/crypto/kdf.d.ts.map +1 -1
- package/build/crypto/kdf.js +18 -0
- package/build/crypto/kdf.js.map +1 -1
- package/build/oauth/jwt-issuer.d.ts +58 -25
- package/build/oauth/jwt-issuer.d.ts.map +1 -1
- package/build/oauth/jwt-issuer.js +100 -31
- package/build/oauth/jwt-issuer.js.map +1 -1
- package/build/storage/backends.d.ts +56 -0
- package/build/storage/backends.d.ts.map +1 -0
- package/build/storage/backends.js +167 -0
- package/build/storage/backends.js.map +1 -0
- package/build/storage/encryption.js.map +1 -1
- package/build/storage/home-dir.d.ts +11 -0
- package/build/storage/home-dir.d.ts.map +1 -0
- package/build/storage/home-dir.js +18 -0
- package/build/storage/home-dir.js.map +1 -0
- package/build/storage/index.d.ts +1 -0
- package/build/storage/index.d.ts.map +1 -1
- package/build/storage/index.js +1 -0
- package/build/storage/index.js.map +1 -1
- package/build/storage/per-plugin-store.d.ts +5 -3
- package/build/storage/per-plugin-store.d.ts.map +1 -1
- package/build/storage/per-plugin-store.js +19 -28
- package/build/storage/per-plugin-store.js.map +1 -1
- package/build/transport/cache.js +5 -5
- package/build/transport/cache.js.map +1 -1
- package/build/transport/local-server.d.ts +3 -1
- package/build/transport/local-server.d.ts.map +1 -1
- package/build/transport/local-server.js +2 -2
- package/build/transport/local-server.js.map +1 -1
- package/build/transport/oauth-middleware.d.ts +2 -1
- package/build/transport/oauth-middleware.d.ts.map +1 -1
- package/build/transport/oauth-middleware.js +17 -5
- package/build/transport/oauth-middleware.js.map +1 -1
- package/package.json +9 -9
|
@@ -113,31 +113,26 @@ function getSafeNext(input) {
|
|
|
113
113
|
}
|
|
114
114
|
return next;
|
|
115
115
|
}
|
|
116
|
-
|
|
117
|
-
const next = getSafeNext(req.query?.next);
|
|
118
|
-
res.set?.('Content-Type', 'text/html');
|
|
119
|
-
// ``next`` flows from the query string but every interpolation is run
|
|
120
|
-
// through ``escapeHtml`` (defined above) which replaces &, <, >, ", and
|
|
121
|
-
// ' with their HTML entity equivalents — the same character set the
|
|
122
|
-
// raw-html-format rule expects sanitisers to handle. Semgrep can't see
|
|
123
|
-
// the custom helper so we suppress the warning here.
|
|
116
|
+
function renderLoginForm(next, errorMsg) {
|
|
124
117
|
const safeNext = escapeHtml(String(next));
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
118
|
+
const errorHtml = errorMsg
|
|
119
|
+
? `\n <div id="login-error" class="status-box error" role="alert" style="display: block; margin-bottom: 1.25rem; margin-top: 0;">\n ${escapeHtml(errorMsg)}\n </div>`
|
|
120
|
+
: '';
|
|
121
|
+
const ariaAttributes = errorMsg
|
|
122
|
+
? ' aria-invalid="true" aria-errormessage="login-error" aria-describedby="login-error relay-desc"'
|
|
123
|
+
: ' aria-describedby="relay-desc"';
|
|
124
|
+
return ` <div class="container">
|
|
130
125
|
<div class="card">
|
|
131
126
|
<div class="server-header">
|
|
132
127
|
<h1 class="server-name">Relay login</h1>
|
|
133
128
|
<div class="server-id">mcp-relay</div>
|
|
134
|
-
<p class="server-description">Enter the relay password shared by your deployer.</p>
|
|
135
|
-
</div
|
|
129
|
+
<p class="server-description" id="relay-desc">Enter the relay password shared by your deployer.</p>
|
|
130
|
+
</div>${errorHtml}
|
|
136
131
|
|
|
137
|
-
<p class="form-title">Authenticate</p>
|
|
132
|
+
<p class="form-title" id="form-title">Authenticate</p>
|
|
138
133
|
|
|
139
|
-
<form method="POST" action="/login"
|
|
140
|
-
<!-- nosemgrep: javascript.express.security.injection.raw-html-format.raw-html-format -- safeNext is escapeHtml(next)
|
|
134
|
+
<form method="POST" action="/login" aria-labelledby="form-title">
|
|
135
|
+
<!-- nosemgrep: javascript.express.security.injection.raw-html-format.raw-html-format -- safeNext is escapeHtml(next) -->
|
|
141
136
|
<input type="hidden" name="next" value="${safeNext}">
|
|
142
137
|
<div class="field-group">
|
|
143
138
|
<label for="field-password" class="field-label">
|
|
@@ -155,15 +150,20 @@ export async function loginGetHandler(req, res) {
|
|
|
155
150
|
autocapitalize="off"
|
|
156
151
|
spellcheck="false"
|
|
157
152
|
required
|
|
158
|
-
autofocus
|
|
153
|
+
autofocus${ariaAttributes}
|
|
159
154
|
/>
|
|
160
155
|
</div>
|
|
161
156
|
|
|
162
157
|
<button type="submit" class="submit-btn">Continue</button>
|
|
163
158
|
</form>
|
|
164
159
|
</div>
|
|
165
|
-
</div>`;
|
|
166
|
-
|
|
160
|
+
</div>`;
|
|
161
|
+
}
|
|
162
|
+
export async function loginGetHandler(req, res) {
|
|
163
|
+
const next = getSafeNext(req.query?.next);
|
|
164
|
+
res.set?.('Content-Type', 'text/html');
|
|
165
|
+
res.set?.('X-Frame-Options', 'DENY');
|
|
166
|
+
res.send(renderFormShell('Relay login', renderLoginForm(next)));
|
|
167
167
|
}
|
|
168
168
|
export async function loginPostHandler(req, res) {
|
|
169
169
|
const ip = req.ip ?? 'unknown';
|
|
@@ -178,7 +178,9 @@ export async function loginPostHandler(req, res) {
|
|
|
178
178
|
const next = getSafeNext(req.body?.next);
|
|
179
179
|
if (!configuredPassword || !timingSafeEqual(password, configuredPassword)) {
|
|
180
180
|
bumpFail(ip);
|
|
181
|
-
res.
|
|
181
|
+
res.set?.('Content-Type', 'text/html');
|
|
182
|
+
res.set?.('X-Frame-Options', 'DENY');
|
|
183
|
+
res.status(401).send(renderFormShell('Relay login', renderLoginForm(next, 'Invalid password. Please try again.')));
|
|
182
184
|
return;
|
|
183
185
|
}
|
|
184
186
|
clearFail(ip);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-login.js","sourceRoot":"","sources":["../../src/auth/relay-login.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"relay-login.js","sourceRoot":"","sources":["../../src/auth/relay-login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,MAAM,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAUtD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAA;AAChD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAA;AAC1C,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAC1C,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AACrC,MAAM,UAAU,GAAG,CAAC,CAAA;AAEpB,IAAI,kBAAkB,GAAG,EAAE,CAAA;AAE3B,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,kBAAkB,GAAG,QAAQ,IAAI,EAAE,CAAA;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,QAAQ,CAAC,KAAK,EAAE,CAAA;IAChB,KAAK,CAAC,KAAK,EAAE,CAAA;IACb,kBAAkB,GAAG,EAAE,CAAA;AACzB,CAAC;AAED,SAAS,eAAe,CAAC,CAAS,EAAE,CAAS;IAC3C,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACzB,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACzB,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,CAAA;IAC7C,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACxC,OAAO,MAAM,CAAC,eAAe,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,aAAa,CAAA;AAC9D,CAAC;AAED,SAAS,QAAQ,CAAC,EAAU;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC3B,IAAI,CAAC,KAAK,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,GAAG,cAAc,EAAE,CAAC;QACnD,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAA;QACzC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,CAAA;IAC1C,CAAC;IACD,KAAK,CAAC,KAAK,IAAI,CAAC,CAAA;IAChB,IAAI,KAAK,CAAC,KAAK,GAAG,UAAU,EAAE,CAAC;QAC7B,OAAO;YACL,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,cAAc,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;SACvE,CAAA;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,CAAA;AAC1C,CAAC;AAED,SAAS,SAAS,CAAC,EAAU;IAC3B,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAClB,CAAC;AAgCD;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAA0B;IACnE,OAAO,KAAK,EAAE,GAAsB,EAAE,GAAyC,EAAE,IAAgB,EAAiB,EAAE;QAClH,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,EAAE,CAAA;YACN,OAAM;QACR,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,iBAAiB,CAAA;QAC1C,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QACjD,IAAI,KAAK,IAAI,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC1C,IAAI,EAAE,CAAA;YACN,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,WAAW,IAAI,YAAY,CAAC,CAAA;QACjE,GAAG,CAAC,QAAQ,CAAC,eAAe,KAAK,EAAE,CAAC,CAAA;IACtC,CAAC,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE;QACjC,MAAM,QAAQ,GAA2B;YACvC,GAAG,EAAE,OAAO;YACZ,GAAG,EAAE,MAAM;YACX,GAAG,EAAE,MAAM;YACX,GAAG,EAAE,QAAQ;YACb,GAAG,EAAE,OAAO;SACb,CAAA;QACD,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,KAAc;IACjC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,YAAY,CAAC,CAAA;IAC1C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxG,OAAO,YAAY,CAAA;IACrB,CAAC;IACD,oEAAoE;IACpE,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAChD,OAAO,YAAY,CAAA;IACrB,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,QAAiB;IACtD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IACzC,MAAM,SAAS,GAAG,QAAQ;QACxB,CAAC,CAAC,8JAA8J,UAAU,CAAC,QAAQ,CAAC,sBAAsB;QAC1M,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,cAAc,GAAG,QAAQ;QAC7B,CAAC,CAAC,gGAAgG;QAClG,CAAC,CAAC,gCAAgC,CAAA;IAEpC,OAAO;;;;;;oBAMW,SAAS;;;;;;0DAM6B,QAAQ;;;;;;;;;;;;;;;;;mCAiB/B,cAAc;;;;;;;WAOtC,CAAA;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAqC,EACrC,GAA6C;IAE7C,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACzC,GAAG,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;IACtC,GAAG,CAAC,GAAG,EAAE,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAA;IACpC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAA2C,EAC3C,GAAuB;IAEvB,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,IAAI,SAAS,CAAA;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC/B,IAAI,SAAS,IAAI,GAAG,GAAG,SAAS,CAAC,OAAO,GAAG,cAAc,IAAI,SAAS,CAAC,KAAK,IAAI,UAAU,EAAE,CAAC;QAC3F,GAAG,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,cAAc,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QACnG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;QACjE,OAAM;IACR,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAA;IACjD,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACxC,IAAI,CAAC,kBAAkB,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EAAE,CAAC;QAC1E,QAAQ,CAAC,EAAE,CAAC,CAAA;QACZ,GAAG,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QACtC,GAAG,CAAC,GAAG,EAAE,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAA;QACpC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,eAAe,CAAC,IAAI,EAAE,qCAAqC,CAAC,CAAC,CAAC,CAAA;QAClH,OAAM;IACR,CAAC;IACD,SAAS,CAAC,EAAE,CAAC,CAAA;IACb,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAClD,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC,CAAA;IAC7D,GAAG,CAAC,MAAM,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACnC,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,cAAc;KACvB,CAAC,CAAA;IACF,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AACpB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"well-known.d.ts","sourceRoot":"","sources":["../../src/auth/well-known.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"well-known.d.ts","sourceRoot":"","sources":["../../src/auth/well-known.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAatF;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMnH"}
|
package/build/auth/well-known.js
CHANGED
|
@@ -5,6 +5,7 @@ export function authorizationServerMetadata(issuerUrl) {
|
|
|
5
5
|
authorization_endpoint: `${issuerUrl}/authorize`,
|
|
6
6
|
token_endpoint: `${issuerUrl}/token`,
|
|
7
7
|
registration_endpoint: `${issuerUrl}/register`,
|
|
8
|
+
jwks_uri: `${issuerUrl}/.well-known/jwks.json`,
|
|
8
9
|
response_types_supported: ['code'],
|
|
9
10
|
grant_types_supported: ['authorization_code', 'refresh_token'],
|
|
10
11
|
code_challenge_methods_supported: ['S256'],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"well-known.js","sourceRoot":"","sources":["../../src/auth/well-known.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,MAAM,UAAU,2BAA2B,CAAC,SAAiB;IAC3D,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,sBAAsB,EAAE,GAAG,SAAS,YAAY;QAChD,cAAc,EAAE,GAAG,SAAS,QAAQ;QACpC,qBAAqB,EAAE,GAAG,SAAS,WAAW;QAC9C,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,qBAAqB,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;QAC9D,gCAAgC,EAAE,CAAC,MAAM,CAAC;QAC1C,qCAAqC,EAAE,CAAC,MAAM,CAAC;QAC/C,gBAAgB,EAAE,CAAC,gBAAgB,CAAC;KACrC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,QAAgB,EAAE,oBAA8B;IACxF,OAAO;QACL,QAAQ;QACR,qBAAqB,EAAE,oBAAoB;QAC3C,wBAAwB,EAAE,CAAC,QAAQ,CAAC;KACrC,CAAA;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"well-known.js","sourceRoot":"","sources":["../../src/auth/well-known.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,MAAM,UAAU,2BAA2B,CAAC,SAAiB;IAC3D,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,sBAAsB,EAAE,GAAG,SAAS,YAAY;QAChD,cAAc,EAAE,GAAG,SAAS,QAAQ;QACpC,qBAAqB,EAAE,GAAG,SAAS,WAAW;QAC9C,QAAQ,EAAE,GAAG,SAAS,wBAAwB;QAC9C,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,qBAAqB,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;QAC9D,gCAAgC,EAAE,CAAC,MAAM,CAAC;QAC1C,qCAAqC,EAAE,CAAC,MAAM,CAAC;QAC/C,gBAAgB,EAAE,CAAC,gBAAgB,CAAC;KACrC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,QAAgB,EAAE,oBAA8B;IACxF,OAAO;QACL,QAAQ;QACR,qBAAqB,EAAE,oBAAoB;QAC3C,wBAAwB,EAAE,CAAC,QAAQ,CAAC;KACrC,CAAA;AACH,CAAC"}
|
package/build/crypto/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { decrypt, type EncryptResult, encrypt } from './aes.js';
|
|
2
2
|
export { deriveSharedSecret, exportPublicKey, generateKeyPair, importPublicKey } from './ecdh.js';
|
|
3
|
-
export { deriveAesKey } from './kdf.js';
|
|
3
|
+
export { deriveAesKey, deriveJwtSigningSeed } from './kdf.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACjG,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA"}
|
package/build/crypto/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { decrypt, encrypt } from './aes.js';
|
|
2
2
|
export { deriveSharedSecret, exportPublicKey, generateKeyPair, importPublicKey } from './ecdh.js';
|
|
3
|
-
export { deriveAesKey } from './kdf.js';
|
|
3
|
+
export { deriveAesKey, deriveJwtSigningSeed } from './kdf.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAsB,OAAO,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAsB,OAAO,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACjG,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA"}
|
package/build/crypto/kdf.d.ts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
export declare function deriveAesKey(sharedSecret: ArrayBuffer, passphrase: string): Promise<CryptoKey>;
|
|
2
|
+
/**
|
|
3
|
+
* Derive a deterministic 32-byte Ed25519 seed for OAuth JWT signing.
|
|
4
|
+
*
|
|
5
|
+
* Used in HTTP multi-user mode (CREDENTIAL_SECRET set) so every container
|
|
6
|
+
* replica converges on the SAME signing key without a shared volume or
|
|
7
|
+
* external secret store. Domain-separated per server via the HKDF info label.
|
|
8
|
+
*
|
|
9
|
+
* HKDF-SHA256 with IKM = secret, empty salt, info =
|
|
10
|
+
* `mcp-jwt-signing-key-v1:<serverName>`. Byte-identical to core-py's
|
|
11
|
+
* `derive_jwt_signing_seed` for the same inputs (enforced by the shared
|
|
12
|
+
* crypto-vectors fixture).
|
|
13
|
+
*/
|
|
14
|
+
export declare function deriveJwtSigningSeed(secret: string, serverName: string): Buffer;
|
|
2
15
|
//# sourceMappingURL=kdf.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kdf.d.ts","sourceRoot":"","sources":["../../src/crypto/kdf.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"kdf.d.ts","sourceRoot":"","sources":["../../src/crypto/kdf.ts"],"names":[],"mappings":"AAOA,wBAAsB,YAAY,CAAC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAWpG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG/E"}
|
package/build/crypto/kdf.js
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
|
+
import { hkdfSync } from 'node:crypto';
|
|
1
2
|
const encoder = new TextEncoder();
|
|
2
3
|
const INFO = encoder.encode('mcp-relay');
|
|
4
|
+
const JWT_SIGNING_INFO_PREFIX = 'mcp-jwt-signing-key-v1:';
|
|
3
5
|
export async function deriveAesKey(sharedSecret, passphrase) {
|
|
4
6
|
const salt = encoder.encode(passphrase);
|
|
5
7
|
const keyMaterial = await crypto.subtle.importKey('raw', sharedSecret, 'HKDF', false, ['deriveKey']);
|
|
6
8
|
return crypto.subtle.deriveKey({ name: 'HKDF', hash: 'SHA-256', salt, info: INFO }, keyMaterial, { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
|
|
7
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Derive a deterministic 32-byte Ed25519 seed for OAuth JWT signing.
|
|
12
|
+
*
|
|
13
|
+
* Used in HTTP multi-user mode (CREDENTIAL_SECRET set) so every container
|
|
14
|
+
* replica converges on the SAME signing key without a shared volume or
|
|
15
|
+
* external secret store. Domain-separated per server via the HKDF info label.
|
|
16
|
+
*
|
|
17
|
+
* HKDF-SHA256 with IKM = secret, empty salt, info =
|
|
18
|
+
* `mcp-jwt-signing-key-v1:<serverName>`. Byte-identical to core-py's
|
|
19
|
+
* `derive_jwt_signing_seed` for the same inputs (enforced by the shared
|
|
20
|
+
* crypto-vectors fixture).
|
|
21
|
+
*/
|
|
22
|
+
export function deriveJwtSigningSeed(secret, serverName) {
|
|
23
|
+
const info = encoder.encode(`${JWT_SIGNING_INFO_PREFIX}${serverName}`);
|
|
24
|
+
return Buffer.from(hkdfSync('sha256', Buffer.from(secret, 'utf-8'), Buffer.alloc(0), info, 32));
|
|
25
|
+
}
|
|
8
26
|
//# sourceMappingURL=kdf.js.map
|
package/build/crypto/kdf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kdf.js","sourceRoot":"","sources":["../../src/crypto/kdf.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;AAEjC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"kdf.js","sourceRoot":"","sources":["../../src/crypto/kdf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;AAEjC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;AACxC,MAAM,uBAAuB,GAAG,yBAAyB,CAAA;AAEzD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,YAAyB,EAAE,UAAkB;IAC9E,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACvC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;IAEpG,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5B,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EACnD,WAAW,EACX,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,EAChC,IAAI,EACJ,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,CAAA;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,UAAkB;IACrE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,uBAAuB,GAAG,UAAU,EAAE,CAAC,CAAA;IACtE,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;AACjG,CAAC"}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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,61 @@ 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
|
|
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
|
-
|
|
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
|
|
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
|
|
50
|
+
* Issue a JWT refresh token (typ="refresh") signed with the active alg.
|
|
24
51
|
*
|
|
25
|
-
* Defaults to a
|
|
26
|
-
* mint fresh access tokens without forcing the user back through the
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
52
|
+
* Defaults to a 1-year (31536000s) lifetime so long-running MCP clients can
|
|
53
|
+
* mint fresh access tokens without forcing the user back through the browser
|
|
54
|
+
* PKCE flow. The access token stays short-lived (1h); the refresh token is the
|
|
55
|
+
* renewal credential and is rotated on every use (the /token refresh handler
|
|
56
|
+
* issues a new refresh token each time), so the security control is rotation,
|
|
57
|
+
* not a short TTL. A short refresh TTL was the residual re-auth driver: a
|
|
58
|
+
* self-hosted server a user touches only intermittently (less than once a
|
|
59
|
+
* month) would silently expire its refresh token between sessions, forcing a
|
|
60
|
+
* fresh browser OAuth tab on the next use. With a 1-year floor, only a
|
|
61
|
+
* genuinely long idle gap re-prompts. Same key / iss / aud as access tokens;
|
|
62
|
+
* the typ claim is the only thing distinguishing them, and verifyAccessToken
|
|
63
|
+
* rejects typ="refresh" so a refresh token can never be used as an access
|
|
64
|
+
* token at the /mcp resource.
|
|
31
65
|
*/
|
|
32
66
|
issueRefreshToken(sub: string, expiresInSeconds?: number): Promise<string>;
|
|
33
67
|
/**
|
|
34
|
-
* Verify a JWT access token and return its payload. Throws on failure
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* so a refresh token cannot
|
|
39
|
-
*
|
|
40
|
-
* keeps already-issued pre-refresh-support
|
|
68
|
+
* Verify a JWT access token and return its payload. Throws on failure (bad
|
|
69
|
+
* signature, wrong issuer/audience, expired) — the same jose errors existing
|
|
70
|
+
* callers already catch. The verify path uses the single active algorithm,
|
|
71
|
+
* never a union. Additionally rejects tokens whose typ claim is "refresh"
|
|
72
|
+
* (throwing jose.errors.JWTClaimValidationFailed) so a refresh token cannot
|
|
73
|
+
* be replayed as an access token. Tokens with typ="access" OR a missing typ
|
|
74
|
+
* claim are accepted (the latter keeps already-issued pre-refresh-support
|
|
75
|
+
* tokens valid).
|
|
41
76
|
*/
|
|
42
77
|
verifyAccessToken(token: string): Promise<jose.JWTPayload>;
|
|
43
78
|
/**
|
|
44
79
|
* Verify a JWT refresh token and return its payload. Same key / audience /
|
|
45
|
-
* issuer checks as
|
|
46
|
-
* failure. Additionally asserts
|
|
47
|
-
*
|
|
48
|
-
* can never be exchanged at the refresh grant.
|
|
49
|
-
* extends ``jose.errors.JOSEError``, so callers' existing ``catch`` clauses
|
|
50
|
-
* around ``verifyAccessToken`` already catch it.
|
|
80
|
+
* issuer checks as verifyAccessToken and throws the same jose errors on
|
|
81
|
+
* failure; uses the single active algorithm. Additionally asserts
|
|
82
|
+
* typ=="refresh" (throwing jose.errors.JWTClaimValidationFailed otherwise) so
|
|
83
|
+
* an access token can never be exchanged at the refresh grant.
|
|
51
84
|
*/
|
|
52
85
|
verifyRefreshToken(token: string): Promise<jose.JWTPayload>;
|
|
53
86
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-issuer.d.ts","sourceRoot":"","sources":["../../src/oauth/jwt-issuer.ts"],"names":[],"mappings":"AAAA
|
|
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;;;;;;;;;;;;;;;;OAgBG;IACG,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,SAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAWlF;;;;;;;;;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
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
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:
|
|
123
|
+
.setProtectedHeader({ alg: this.alg, kid: this.kid })
|
|
63
124
|
.setIssuer(this.serverName)
|
|
64
125
|
.setAudience(this.serverName)
|
|
65
126
|
.setIssuedAt()
|
|
@@ -67,20 +128,27 @@ export class JWTIssuer {
|
|
|
67
128
|
.sign(this.privateKey);
|
|
68
129
|
}
|
|
69
130
|
/**
|
|
70
|
-
* Issue
|
|
131
|
+
* Issue a JWT refresh token (typ="refresh") signed with the active alg.
|
|
71
132
|
*
|
|
72
|
-
* Defaults to a
|
|
73
|
-
* mint fresh access tokens without forcing the user back through the
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
133
|
+
* Defaults to a 1-year (31536000s) lifetime so long-running MCP clients can
|
|
134
|
+
* mint fresh access tokens without forcing the user back through the browser
|
|
135
|
+
* PKCE flow. The access token stays short-lived (1h); the refresh token is the
|
|
136
|
+
* renewal credential and is rotated on every use (the /token refresh handler
|
|
137
|
+
* issues a new refresh token each time), so the security control is rotation,
|
|
138
|
+
* not a short TTL. A short refresh TTL was the residual re-auth driver: a
|
|
139
|
+
* self-hosted server a user touches only intermittently (less than once a
|
|
140
|
+
* month) would silently expire its refresh token between sessions, forcing a
|
|
141
|
+
* fresh browser OAuth tab on the next use. With a 1-year floor, only a
|
|
142
|
+
* genuinely long idle gap re-prompts. Same key / iss / aud as access tokens;
|
|
143
|
+
* the typ claim is the only thing distinguishing them, and verifyAccessToken
|
|
144
|
+
* rejects typ="refresh" so a refresh token can never be used as an access
|
|
145
|
+
* token at the /mcp resource.
|
|
78
146
|
*/
|
|
79
|
-
async issueRefreshToken(sub, expiresInSeconds =
|
|
147
|
+
async issueRefreshToken(sub, expiresInSeconds = 31536000) {
|
|
80
148
|
if (!this.privateKey)
|
|
81
149
|
throw new Error('JWTIssuer not initialized');
|
|
82
150
|
return new jose.SignJWT({ sub, typ: 'refresh' })
|
|
83
|
-
.setProtectedHeader({ alg:
|
|
151
|
+
.setProtectedHeader({ alg: this.alg, kid: this.kid })
|
|
84
152
|
.setIssuer(this.serverName)
|
|
85
153
|
.setAudience(this.serverName)
|
|
86
154
|
.setIssuedAt()
|
|
@@ -88,20 +156,22 @@ export class JWTIssuer {
|
|
|
88
156
|
.sign(this.privateKey);
|
|
89
157
|
}
|
|
90
158
|
/**
|
|
91
|
-
* Verify a JWT access token and return its payload. Throws on failure
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* so a refresh token cannot
|
|
96
|
-
*
|
|
97
|
-
* keeps already-issued pre-refresh-support
|
|
159
|
+
* Verify a JWT access token and return its payload. Throws on failure (bad
|
|
160
|
+
* signature, wrong issuer/audience, expired) — the same jose errors existing
|
|
161
|
+
* callers already catch. The verify path uses the single active algorithm,
|
|
162
|
+
* never a union. Additionally rejects tokens whose typ claim is "refresh"
|
|
163
|
+
* (throwing jose.errors.JWTClaimValidationFailed) so a refresh token cannot
|
|
164
|
+
* be replayed as an access token. Tokens with typ="access" OR a missing typ
|
|
165
|
+
* claim are accepted (the latter keeps already-issued pre-refresh-support
|
|
166
|
+
* tokens valid).
|
|
98
167
|
*/
|
|
99
168
|
async verifyAccessToken(token) {
|
|
100
169
|
if (!this.publicKey)
|
|
101
170
|
throw new Error('JWTIssuer not initialized');
|
|
102
171
|
const { payload } = await jose.jwtVerify(token, this.publicKey, {
|
|
103
172
|
issuer: this.serverName,
|
|
104
|
-
audience: this.serverName
|
|
173
|
+
audience: this.serverName,
|
|
174
|
+
algorithms: [this.alg]
|
|
105
175
|
});
|
|
106
176
|
if (payload.typ === 'refresh') {
|
|
107
177
|
throw new jose.errors.JWTClaimValidationFailed('Refresh token cannot be used as an access token', payload, 'typ');
|
|
@@ -110,19 +180,18 @@ export class JWTIssuer {
|
|
|
110
180
|
}
|
|
111
181
|
/**
|
|
112
182
|
* Verify a JWT refresh token and return its payload. Same key / audience /
|
|
113
|
-
* issuer checks as
|
|
114
|
-
* failure. Additionally asserts
|
|
115
|
-
*
|
|
116
|
-
* can never be exchanged at the refresh grant.
|
|
117
|
-
* extends ``jose.errors.JOSEError``, so callers' existing ``catch`` clauses
|
|
118
|
-
* around ``verifyAccessToken`` already catch it.
|
|
183
|
+
* issuer checks as verifyAccessToken and throws the same jose errors on
|
|
184
|
+
* failure; uses the single active algorithm. Additionally asserts
|
|
185
|
+
* typ=="refresh" (throwing jose.errors.JWTClaimValidationFailed otherwise) so
|
|
186
|
+
* an access token can never be exchanged at the refresh grant.
|
|
119
187
|
*/
|
|
120
188
|
async verifyRefreshToken(token) {
|
|
121
189
|
if (!this.publicKey)
|
|
122
190
|
throw new Error('JWTIssuer not initialized');
|
|
123
191
|
const { payload } = await jose.jwtVerify(token, this.publicKey, {
|
|
124
192
|
issuer: this.serverName,
|
|
125
|
-
audience: this.serverName
|
|
193
|
+
audience: this.serverName,
|
|
194
|
+
algorithms: [this.alg]
|
|
126
195
|
});
|
|
127
196
|
if (payload.typ !== 'refresh') {
|
|
128
197
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"jwt-issuer.js","sourceRoot":"","sources":["../../src/oauth/jwt-issuer.ts"],"names":[],"mappings":"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"}
|