@ni-c/mcp-hub 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +388 -0
- package/README.md +77 -12
- package/dist/admin.js +1 -1
- package/dist/admin.js.map +1 -1
- package/dist/auth/api-tokens.js +27 -0
- package/dist/auth/api-tokens.js.map +1 -0
- package/dist/auth/headers.js +22 -1
- package/dist/auth/headers.js.map +1 -1
- package/dist/auth/oidc/adapter.js +135 -0
- package/dist/auth/oidc/adapter.js.map +1 -0
- package/dist/auth/oidc/interactions.js +187 -0
- package/dist/auth/oidc/interactions.js.map +1 -0
- package/dist/auth/oidc/mount.js +144 -0
- package/dist/auth/oidc/mount.js.map +1 -0
- package/dist/auth/oidc/provider.js +440 -0
- package/dist/auth/oidc/provider.js.map +1 -0
- package/dist/auth/oidc/quirks.js +234 -0
- package/dist/auth/oidc/quirks.js.map +1 -0
- package/dist/auth/oidc/verifier.js +121 -0
- package/dist/auth/oidc/verifier.js.map +1 -0
- package/dist/auth/protected-resource.js +41 -0
- package/dist/auth/protected-resource.js.map +1 -0
- package/dist/auth/rate-limit.js +115 -2
- package/dist/auth/rate-limit.js.map +1 -1
- package/dist/auth/redirect-uri.js +33 -1
- package/dist/auth/redirect-uri.js.map +1 -1
- package/dist/auth/registration.js +1 -1
- package/dist/auth/registration.js.map +1 -1
- package/dist/auth/session.js +43 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/store.js +160 -1
- package/dist/auth/store.js.map +1 -1
- package/dist/config.js +55 -2
- package/dist/config.js.map +1 -1
- package/dist/docker-proxy/policy.js +1 -0
- package/dist/docker-proxy/policy.js.map +1 -1
- package/dist/docker-proxy/server.js +1 -0
- package/dist/docker-proxy/server.js.map +1 -1
- package/dist/elicitation.js +0 -0
- package/dist/elicitation.js.map +1 -0
- package/dist/forward.js +0 -0
- package/dist/forward.js.map +1 -0
- package/dist/hub.js +309 -29
- package/dist/hub.js.map +1 -1
- package/dist/index.js +103 -21
- package/dist/index.js.map +1 -1
- package/dist/limits.js +13 -1
- package/dist/limits.js.map +1 -1
- package/dist/mcp-limits.js +14 -2
- package/dist/mcp-limits.js.map +1 -1
- package/dist/proxy.js +246 -40
- package/dist/proxy.js.map +1 -1
- package/dist/stdio.js +71 -6
- package/dist/stdio.js.map +1 -1
- package/dist/subscriptions.js +236 -0
- package/dist/subscriptions.js.map +1 -0
- package/dist/supervisor.js +322 -22
- package/dist/supervisor.js.map +1 -1
- package/dist/timings.js +61 -0
- package/dist/timings.js.map +1 -0
- package/dist/tool-filter.js +1 -0
- package/dist/tool-filter.js.map +1 -1
- package/dist/transports/docker.js.map +1 -1
- package/dist/transports/stream.js +33 -20
- package/dist/transports/stream.js.map +1 -1
- package/dist/upstream/auth.js +1 -1
- package/dist/upstream/auth.js.map +1 -1
- package/dist/upstream/provider.js.map +1 -1
- package/dist/upstream/routes.js +3 -2
- package/dist/upstream/routes.js.map +1 -1
- package/package.json +19 -6
- package/dist/auth/private-key-jwt.js +0 -213
- package/dist/auth/private-key-jwt.js.map +0 -1
- package/dist/auth/provider.js +0 -437
- package/dist/auth/provider.js.map +0 -1
- package/dist/auth/routes.js +0 -249
- package/dist/auth/routes.js.map +0 -1
package/dist/auth/provider.js
DELETED
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
|
-
import { SignJWT, jwtVerify } from 'jose';
|
|
3
|
-
import { InvalidClientMetadataError, InvalidGrantError, InvalidTargetError, InvalidTokenError, TooManyRequestsError } from '@modelcontextprotocol/sdk/server/auth/errors.js';
|
|
4
|
-
import { redirectUriMatches } from '@modelcontextprotocol/sdk/server/auth/handlers/authorize.js';
|
|
5
|
-
import { isClientIdMetadataUrl } from './cimd.js';
|
|
6
|
-
import { isLoopbackOnly, isSafeRedirectUri } from './redirect-uri.js';
|
|
7
|
-
import { registrationClientUri } from './registration.js';
|
|
8
|
-
import { readSignedPayload, sign, signPayload, signatureMatches } from './signed-token.js';
|
|
9
|
-
import { clampDisplayName, logSafe } from './text.js';
|
|
10
|
-
import { renderLoginPage } from './login-page.js';
|
|
11
|
-
import { renderConsentPage } from './consent-page.js';
|
|
12
|
-
import { allowFormActionTo } from './headers.js';
|
|
13
|
-
const ACCESS_TOKEN_TTL_S = 15 * 60;
|
|
14
|
-
const REFRESH_TOKEN_TTL_S = 30 * 24 * 3600;
|
|
15
|
-
const CODE_TTL_MS = 10 * 60_000;
|
|
16
|
-
export const SESSION_TTL_MS = 30 * 60_000;
|
|
17
|
-
export const SESSION_COOKIE = 'mcp_hub_session';
|
|
18
|
-
/**
|
|
19
|
-
* The `__Host-` prefix pins the cookie to this exact origin, so no neighbouring
|
|
20
|
-
* subdomain can plant one. It requires Secure, which rules it out over plain
|
|
21
|
-
* http (local development and the test suite).
|
|
22
|
-
*/
|
|
23
|
-
export function sessionCookieName(externalUrl) {
|
|
24
|
-
return new URL(externalUrl).protocol === 'https:' ? `__Host-${SESSION_COOKIE}` : SESSION_COOKIE;
|
|
25
|
-
}
|
|
26
|
-
/** Distinguishes admin-minted API tokens from interactive OAuth tokens. */
|
|
27
|
-
export const API_TOKEN_SUBJECT = 'mcp-hub-token';
|
|
28
|
-
/**
|
|
29
|
-
* Mint a long-lived, resource-bound API token for clients that cannot do
|
|
30
|
-
* OAuth (OpenAI Responses API, xAI API, Gemini API, plain-header clients).
|
|
31
|
-
* The JWT is returned exactly once; only its record (jti) is persisted, which
|
|
32
|
-
* is what `tokens list` shows and `tokens revoke` deletes.
|
|
33
|
-
*/
|
|
34
|
-
export async function mintApiToken(store, externalUrl, resource, days, label) {
|
|
35
|
-
const id = crypto.randomBytes(8).toString('base64url');
|
|
36
|
-
const now = Math.floor(Date.now() / 1000);
|
|
37
|
-
const expiresAt = now + days * 86_400;
|
|
38
|
-
const token = await new SignJWT({})
|
|
39
|
-
.setProtectedHeader({ alg: 'EdDSA' })
|
|
40
|
-
.setIssuer(new URL(externalUrl).href)
|
|
41
|
-
.setAudience(resource.href)
|
|
42
|
-
.setSubject(API_TOKEN_SUBJECT)
|
|
43
|
-
.setIssuedAt(now)
|
|
44
|
-
.setExpirationTime(expiresAt)
|
|
45
|
-
.setJti(id)
|
|
46
|
-
.sign(store.privateKey);
|
|
47
|
-
store.saveApiToken(id, { label, resource: resource.href, createdAt: now, expiresAt });
|
|
48
|
-
return { id, token, expiresAt };
|
|
49
|
-
}
|
|
50
|
-
export class HubOAuthProvider {
|
|
51
|
-
store;
|
|
52
|
-
externalUrl;
|
|
53
|
-
options;
|
|
54
|
-
/** Short-lived authorization codes; in-memory on purpose (10 min lifetime). */
|
|
55
|
-
codes = new Map();
|
|
56
|
-
sessionCookieName;
|
|
57
|
-
constructor(store, externalUrl, options = {}) {
|
|
58
|
-
this.store = store;
|
|
59
|
-
this.externalUrl = externalUrl;
|
|
60
|
-
this.options = options;
|
|
61
|
-
this.sessionCookieName = sessionCookieName(externalUrl);
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Both registration mechanisms meet here, and this is the only place the SDK
|
|
65
|
-
* looks a client up — the authorize handler, the token handler's client
|
|
66
|
-
* authentication and the revocation endpoint all go through it. A client_id
|
|
67
|
-
* that is an https URL is a Client ID Metadata Document and is fetched from
|
|
68
|
-
* the client; anything else is a locally registered DCR client.
|
|
69
|
-
*/
|
|
70
|
-
get clientsStore() {
|
|
71
|
-
const store = this.store;
|
|
72
|
-
const cimd = this.options.cimd;
|
|
73
|
-
const registerClient = (client) => {
|
|
74
|
-
// The SDK only keeps javascript:, data: and vbscript: out, so without
|
|
75
|
-
// this a client could register a plaintext http:// callback on a remote
|
|
76
|
-
// host and have its authorization code delivered in the clear. Metadata
|
|
77
|
-
// documents have always been held to this; dynamic registration was not.
|
|
78
|
-
for (const uri of client.redirect_uris ?? []) {
|
|
79
|
-
if (!isSafeRedirectUri(uri, { allowPrivateUseSchemes: true })) {
|
|
80
|
-
throw new InvalidClientMetadataError(`redirect_uri ${uri} must be https, a loopback address or a private-use scheme`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const isPublic = client.token_endpoint_auth_method === 'none';
|
|
84
|
-
const stored = {
|
|
85
|
-
...client,
|
|
86
|
-
// Self-declared and shown on the consent page, so held to one line.
|
|
87
|
-
client_name: clampDisplayName(client.client_name),
|
|
88
|
-
client_id: crypto.randomBytes(16).toString('base64url'),
|
|
89
|
-
client_id_issued_at: Math.floor(Date.now() / 1000),
|
|
90
|
-
// Secrets never expire: ChatGPT registers once per connector and
|
|
91
|
-
// never re-registers, so an expiring secret bricks the connector
|
|
92
|
-
// when it runs out (the SDK default is 30 days).
|
|
93
|
-
client_secret_expires_at: 0
|
|
94
|
-
};
|
|
95
|
-
// A stored secret makes the SDK demand it on every token request, so a
|
|
96
|
-
// public client must be persisted without one — but ChatGPT expects a
|
|
97
|
-
// client_secret in the registration response even for "none" and
|
|
98
|
-
// refuses its own registration otherwise. Hand it one in the response
|
|
99
|
-
// only; token requests from public clients ignore it.
|
|
100
|
-
if (isPublic)
|
|
101
|
-
delete stored.client_secret;
|
|
102
|
-
// RFC 7592: the credential that later lets this client read, change or
|
|
103
|
-
// delete its own registration. Only its hash is kept, so this is the one
|
|
104
|
-
// moment the client can learn it.
|
|
105
|
-
const registrationToken = crypto.randomBytes(32).toString('base64url');
|
|
106
|
-
if (!store.addClient(stored, registrationToken)) {
|
|
107
|
-
console.warn(`mcp-hub: refused a registration: the hub is at its limit of ${store.clientLimits.maxClients} clients`);
|
|
108
|
-
throw new TooManyRequestsError('This hub is at its client registration limit; ask the operator to remove unused registrations');
|
|
109
|
-
}
|
|
110
|
-
console.log(`mcp-hub: registered OAuth client ${stored.client_id} (${logSafe(stored.client_name ?? 'unnamed')})`);
|
|
111
|
-
// The SDK's type describes RFC 7591 only, but it serializes whatever this
|
|
112
|
-
// returns — which is how the RFC 7592 fields reach the client. They are
|
|
113
|
-
// response-only and never stored: the record keeps a hash of the token.
|
|
114
|
-
return {
|
|
115
|
-
...stored,
|
|
116
|
-
registration_access_token: registrationToken,
|
|
117
|
-
registration_client_uri: registrationClientUri(this.externalUrl, stored.client_id),
|
|
118
|
-
...(isPublic ? { client_secret: crypto.randomBytes(32).toString('hex'), client_secret_expires_at: 0 } : {})
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
return {
|
|
122
|
-
getClient: clientId => (cimd && isClientIdMetadataUrl(clientId) ? cimd.resolve(clientId) : store.getClient(clientId)),
|
|
123
|
-
// Leaving this undefined is what removes registration_endpoint from the
|
|
124
|
-
// discovery document and unmounts /register: the SDK derives both from
|
|
125
|
-
// the presence of this method.
|
|
126
|
-
...(this.options.allowDynamicRegistration === false ? {} : { registerClient })
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
/** True for a client that proved itself with a metadata document rather than
|
|
130
|
-
* a registration. The pages use it to name the origin that vouched. */
|
|
131
|
-
isMetadataDocumentClient(clientId) {
|
|
132
|
-
return this.options.cimd !== undefined && isClientIdMetadataUrl(clientId);
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* What the login and consent pages need to say about who is asking: the
|
|
136
|
-
* metadata document URL when there is one (draft §6.4 asks for its hostname
|
|
137
|
-
* to be shown) and whether every redirect URI is local, which the MCP
|
|
138
|
-
* security considerations single out as impossible to attribute.
|
|
139
|
-
*/
|
|
140
|
-
clientIdentity(client) {
|
|
141
|
-
return {
|
|
142
|
-
clientId: this.isMetadataDocumentClient(client.client_id) ? client.client_id : undefined,
|
|
143
|
-
loopbackOnly: isLoopbackOnly(client.redirect_uris)
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
// --- Authorization endpoint ---------------------------------------------
|
|
147
|
-
/**
|
|
148
|
-
* A live session alone must not be enough to hand out a code: the cookie
|
|
149
|
-
* rides along on cross-site top-level navigations, so any page could walk a
|
|
150
|
-
* logged-in user through /authorize with a client it registered itself and
|
|
151
|
-
* collect the code. Only clients the user confirmed once are silent; for
|
|
152
|
-
* those the code can only ever land on their own registered redirect_uri.
|
|
153
|
-
*
|
|
154
|
-
* The SDK re-sends on `res` if this method throws, so nothing may throw
|
|
155
|
-
* after the response has gone out.
|
|
156
|
-
*/
|
|
157
|
-
async authorize(client, params, res) {
|
|
158
|
-
// Asking to authorize is use: it keeps a registration out of the sweep for
|
|
159
|
-
// never-confirmed clients, so a user who takes their time over the password
|
|
160
|
-
// does not have the registration deleted underneath them.
|
|
161
|
-
this.store.touchClient(client.client_id);
|
|
162
|
-
const resource = this.resolveResource(params.resource);
|
|
163
|
-
const session = this.readSessionCookie(res.req.headers.cookie);
|
|
164
|
-
if (session && this.isApproved(client, params.redirectUri)) {
|
|
165
|
-
this.redirectWithCode(client, { ...params, resource }, res);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
const request = this.signPending(client, { ...params, resource });
|
|
169
|
-
// A metadata-document client is identified by a URL nobody can forge, so
|
|
170
|
-
// the page can show where the self-declared name came from.
|
|
171
|
-
const identity = { clientName: client.client_name, resource: resource?.href, ...this.clientIdentity(client) };
|
|
172
|
-
const page = session
|
|
173
|
-
? renderConsentPage(request, this.csrfToken(session), params.redirectUri, identity)
|
|
174
|
-
: renderLoginPage(request, params.redirectUri, identity);
|
|
175
|
-
// Submitting either form ends in a redirect to this client's redirect_uri,
|
|
176
|
-
// which the SDK has already matched against its registration.
|
|
177
|
-
allowFormActionTo(res, params.redirectUri);
|
|
178
|
-
res.status(200).type('html').send(page);
|
|
179
|
-
}
|
|
180
|
-
/** Everything the login/consent form has to carry across, signed so it
|
|
181
|
-
* cannot be tampered with while it sits in the browser. */
|
|
182
|
-
signPending(client, params) {
|
|
183
|
-
const resource = this.resolveResource(params.resource);
|
|
184
|
-
const pending = {
|
|
185
|
-
clientId: client.client_id,
|
|
186
|
-
redirectUri: params.redirectUri,
|
|
187
|
-
codeChallenge: params.codeChallenge,
|
|
188
|
-
state: params.state,
|
|
189
|
-
scopes: params.scopes ?? [],
|
|
190
|
-
exp: Date.now() + CODE_TTL_MS,
|
|
191
|
-
resource: resource?.href
|
|
192
|
-
};
|
|
193
|
-
return signPayload(pending, this.store.cookieSecret);
|
|
194
|
-
}
|
|
195
|
-
isApproved(client, redirectUri) {
|
|
196
|
-
const approval = this.store.getApproval(client.client_id);
|
|
197
|
-
// Same matching the SDK applies at registration, so a loopback client that
|
|
198
|
-
// picks a fresh port every run stays approved.
|
|
199
|
-
return approval?.redirectUris.some(uri => redirectUriMatches(redirectUri, uri)) ?? false;
|
|
200
|
-
}
|
|
201
|
-
approve(client, redirectUri) {
|
|
202
|
-
this.store.saveApproval(client.client_id, redirectUri, client.client_name);
|
|
203
|
-
console.log(`mcp-hub: approved OAuth client ${logSafe(client.client_id)} (${logSafe(client.client_name ?? 'unnamed')}) for ${logSafe(redirectUri)}`);
|
|
204
|
-
}
|
|
205
|
-
decodePendingAuthorization(token) {
|
|
206
|
-
const pending = readSignedPayload(token, this.store.cookieSecret);
|
|
207
|
-
if (!pending || pending.exp < Date.now())
|
|
208
|
-
return undefined;
|
|
209
|
-
return pending;
|
|
210
|
-
}
|
|
211
|
-
redirectWithCode(client, params, res) {
|
|
212
|
-
const resource = typeof params.resource === 'string' ? params.resource : this.resolveResource(params.resource)?.href;
|
|
213
|
-
const code = crypto.randomBytes(32).toString('base64url');
|
|
214
|
-
this.sweepExpiredCodes();
|
|
215
|
-
this.codes.set(code, {
|
|
216
|
-
clientId: client.client_id,
|
|
217
|
-
codeChallenge: params.codeChallenge,
|
|
218
|
-
redirectUri: params.redirectUri,
|
|
219
|
-
scopes: params.scopes ?? [],
|
|
220
|
-
expiresAt: Date.now() + CODE_TTL_MS,
|
|
221
|
-
resource
|
|
222
|
-
});
|
|
223
|
-
const target = new URL(params.redirectUri);
|
|
224
|
-
target.searchParams.set('code', code);
|
|
225
|
-
if (params.state !== undefined)
|
|
226
|
-
target.searchParams.set('state', params.state);
|
|
227
|
-
res.redirect(target.toString());
|
|
228
|
-
}
|
|
229
|
-
/** RFC 6749 §4.1.2.1 — tell the client it was turned down instead of
|
|
230
|
-
* leaving it to time out. */
|
|
231
|
-
redirectWithError(redirectUri, state, error, res) {
|
|
232
|
-
const target = new URL(redirectUri);
|
|
233
|
-
target.searchParams.set('error', error);
|
|
234
|
-
if (state !== undefined)
|
|
235
|
-
target.searchParams.set('state', state);
|
|
236
|
-
res.redirect(target.toString());
|
|
237
|
-
}
|
|
238
|
-
/** Codes are otherwise only dropped on redemption, so unredeemed ones from
|
|
239
|
-
* anyone who can register a client would accumulate for the process' life. */
|
|
240
|
-
sweepExpiredCodes() {
|
|
241
|
-
const now = Date.now();
|
|
242
|
-
for (const [code, pending] of this.codes) {
|
|
243
|
-
if (pending.expiresAt < now)
|
|
244
|
-
this.codes.delete(code);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
// --- Session cookie ------------------------------------------------------
|
|
248
|
-
createSessionCookie() {
|
|
249
|
-
const expires = String(Date.now() + SESSION_TTL_MS);
|
|
250
|
-
return `${expires}.${sign(expires, this.store.cookieSecret)}`;
|
|
251
|
-
}
|
|
252
|
-
/** The verified cookie value, which doubles as the handle the consent
|
|
253
|
-
* form's CSRF token is bound to. */
|
|
254
|
-
readSessionCookie(cookieHeader) {
|
|
255
|
-
const match = cookieHeader?.match(new RegExp(`(?:^|;\\s*)${this.sessionCookieName}=([^;]+)`));
|
|
256
|
-
if (!match)
|
|
257
|
-
return undefined;
|
|
258
|
-
const value = decodeURIComponent(match[1]);
|
|
259
|
-
const [expires, signature] = value.split('.');
|
|
260
|
-
if (!expires || !signature)
|
|
261
|
-
return undefined;
|
|
262
|
-
if (!signatureMatches(expires, signature, this.store.cookieSecret))
|
|
263
|
-
return undefined;
|
|
264
|
-
return Number(expires) > Date.now() ? value : undefined;
|
|
265
|
-
}
|
|
266
|
-
hasValidSession(cookieHeader) {
|
|
267
|
-
return this.readSessionCookie(cookieHeader) !== undefined;
|
|
268
|
-
}
|
|
269
|
-
csrfToken(sessionValue) {
|
|
270
|
-
return sign(`csrf:${sessionValue}`, this.store.cookieSecret);
|
|
271
|
-
}
|
|
272
|
-
verifyCsrfToken(sessionValue, token) {
|
|
273
|
-
if (typeof token !== 'string')
|
|
274
|
-
return false;
|
|
275
|
-
return signatureMatches(`csrf:${sessionValue}`, token, this.store.cookieSecret);
|
|
276
|
-
}
|
|
277
|
-
// --- Token endpoint ------------------------------------------------------
|
|
278
|
-
async challengeForAuthorizationCode(_client, authorizationCode) {
|
|
279
|
-
const pending = this.codes.get(authorizationCode);
|
|
280
|
-
if (!pending || pending.expiresAt < Date.now())
|
|
281
|
-
throw new InvalidGrantError('Invalid or expired authorization code');
|
|
282
|
-
return pending.codeChallenge;
|
|
283
|
-
}
|
|
284
|
-
async exchangeAuthorizationCode(client, authorizationCode, _codeVerifier, redirectUri, resource) {
|
|
285
|
-
const pending = this.codes.get(authorizationCode);
|
|
286
|
-
if (!pending || pending.expiresAt < Date.now() || pending.clientId !== client.client_id) {
|
|
287
|
-
throw new InvalidGrantError('Invalid or expired authorization code');
|
|
288
|
-
}
|
|
289
|
-
if (redirectUri !== undefined && redirectUri !== pending.redirectUri) {
|
|
290
|
-
throw new InvalidGrantError('redirect_uri does not match the authorization request');
|
|
291
|
-
}
|
|
292
|
-
const requestedResource = resource ? this.resolveResource(resource) : undefined;
|
|
293
|
-
if (resource && requestedResource?.href !== pending.resource) {
|
|
294
|
-
throw new InvalidGrantError('resource does not match the authorization request');
|
|
295
|
-
}
|
|
296
|
-
this.codes.delete(authorizationCode); // single use
|
|
297
|
-
this.store.touchClient(client.client_id);
|
|
298
|
-
return this.issueTokens(client.client_id, pending.scopes, undefined, pending.resource);
|
|
299
|
-
}
|
|
300
|
-
async exchangeRefreshToken(client, refreshToken, scopes, resource) {
|
|
301
|
-
const record = this.store.getRefreshToken(refreshToken);
|
|
302
|
-
if (!record || record.clientId !== client.client_id) {
|
|
303
|
-
// Seeing a token that was already rotated away means the chain leaked:
|
|
304
|
-
// the legitimate holder and someone else both have one. Drop them all.
|
|
305
|
-
const consumed = this.store.wasConsumed(refreshToken);
|
|
306
|
-
if (consumed) {
|
|
307
|
-
const revoked = this.store.revokeFamily(consumed.familyId);
|
|
308
|
-
console.warn(`mcp-hub: refresh token replay from client ${logSafe(client.client_id)}, revoked ${revoked} token(s)`);
|
|
309
|
-
}
|
|
310
|
-
throw new InvalidGrantError('Invalid refresh token');
|
|
311
|
-
}
|
|
312
|
-
if (scopes && !scopes.every(scope => record.scopes.includes(scope))) {
|
|
313
|
-
throw new InvalidGrantError('Requested scopes exceed the original grant');
|
|
314
|
-
}
|
|
315
|
-
const requestedResource = resource ? this.resolveResource(resource) : undefined;
|
|
316
|
-
if (resource && !requestedResource)
|
|
317
|
-
throw new InvalidTargetError('Unknown or invalid resource');
|
|
318
|
-
if (record.resource && requestedResource && record.resource !== requestedResource.href) {
|
|
319
|
-
throw new InvalidGrantError('resource does not match the original grant');
|
|
320
|
-
}
|
|
321
|
-
const tokenResource = requestedResource?.href ?? record.resource;
|
|
322
|
-
if (this.options.requireResource && !tokenResource)
|
|
323
|
-
throw new InvalidTargetError('A resource indicator is required');
|
|
324
|
-
// Tokens issued before families existed adopt one on their first rotation.
|
|
325
|
-
const familyId = record.familyId ?? crypto.randomBytes(16).toString('base64url');
|
|
326
|
-
this.store.touchClient(client.client_id);
|
|
327
|
-
this.store.consumeRefreshToken(refreshToken, familyId, record.expiresAt);
|
|
328
|
-
return this.issueTokens(client.client_id, scopes ?? record.scopes, familyId, tokenResource);
|
|
329
|
-
}
|
|
330
|
-
async issueTokens(clientId, scopes, familyId = crypto.randomBytes(16).toString('base64url'), resource) {
|
|
331
|
-
const issuedMs = Date.now();
|
|
332
|
-
const accessToken = await new SignJWT({ client_id: clientId, scope: scopes.join(' '), issued_ms: issuedMs })
|
|
333
|
-
.setProtectedHeader({ alg: 'EdDSA' })
|
|
334
|
-
.setIssuer(this.externalUrl)
|
|
335
|
-
.setAudience(resource ?? this.externalUrl)
|
|
336
|
-
.setSubject('mcp-hub-user')
|
|
337
|
-
.setIssuedAt()
|
|
338
|
-
.setExpirationTime(`${ACCESS_TOKEN_TTL_S}s`)
|
|
339
|
-
.setJti(crypto.randomBytes(8).toString('base64url'))
|
|
340
|
-
.sign(this.store.privateKey);
|
|
341
|
-
const refreshToken = `rt_${crypto.randomBytes(32).toString('base64url')}`;
|
|
342
|
-
this.store.saveRefreshToken(refreshToken, {
|
|
343
|
-
clientId,
|
|
344
|
-
scopes,
|
|
345
|
-
expiresAt: Math.floor(Date.now() / 1000) + REFRESH_TOKEN_TTL_S,
|
|
346
|
-
familyId,
|
|
347
|
-
resource
|
|
348
|
-
});
|
|
349
|
-
return {
|
|
350
|
-
access_token: accessToken,
|
|
351
|
-
token_type: 'Bearer',
|
|
352
|
-
expires_in: ACCESS_TOKEN_TTL_S,
|
|
353
|
-
refresh_token: refreshToken,
|
|
354
|
-
scope: scopes.join(' ') || undefined
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
async verifyAccessToken(token) {
|
|
358
|
-
let payload;
|
|
359
|
-
try {
|
|
360
|
-
({ payload } = await jwtVerify(token, this.store.publicKey, {
|
|
361
|
-
issuer: this.externalUrl,
|
|
362
|
-
algorithms: ['EdDSA']
|
|
363
|
-
}));
|
|
364
|
-
}
|
|
365
|
-
catch {
|
|
366
|
-
throw new InvalidTokenError('Invalid or expired access token');
|
|
367
|
-
}
|
|
368
|
-
// Admin-minted API token: same signature and audience rules as an OAuth
|
|
369
|
-
// access token, but revocation lives in its state record — a jti whose
|
|
370
|
-
// record is gone (revoked or expired) is refused even though the JWT
|
|
371
|
-
// itself still verifies.
|
|
372
|
-
if (payload.sub === API_TOKEN_SUBJECT) {
|
|
373
|
-
if (typeof payload.jti !== 'string' || !this.store.getApiToken(payload.jti)) {
|
|
374
|
-
throw new InvalidTokenError('Access token has been revoked');
|
|
375
|
-
}
|
|
376
|
-
const audience = typeof payload.aud === 'string' ? payload.aud : undefined;
|
|
377
|
-
const resource = audience ? this.resolveResourceStrict(audience) : undefined;
|
|
378
|
-
if (!resource)
|
|
379
|
-
throw new InvalidTokenError('Invalid token audience');
|
|
380
|
-
return { token, clientId: `token:${payload.jti}`, scopes: [], expiresAt: payload.exp, resource };
|
|
381
|
-
}
|
|
382
|
-
if (payload.sub !== 'mcp-hub-user' || typeof payload.client_id !== 'string') {
|
|
383
|
-
throw new InvalidTokenError('Invalid access token claims');
|
|
384
|
-
}
|
|
385
|
-
const audience = typeof payload.aud === 'string' ? payload.aud : undefined;
|
|
386
|
-
const resource = audience && audience !== this.externalUrl ? this.resolveResourceStrict(audience) : undefined;
|
|
387
|
-
if (!audience || (audience !== this.externalUrl && !resource))
|
|
388
|
-
throw new InvalidTokenError('Invalid token audience');
|
|
389
|
-
const issuedMs = typeof payload.issued_ms === 'number' ? payload.issued_ms : (payload.iat ?? 0) * 1000;
|
|
390
|
-
const revokedBefore = this.store.getRevokedBefore(payload.client_id);
|
|
391
|
-
if (revokedBefore !== undefined && issuedMs <= revokedBefore)
|
|
392
|
-
throw new InvalidTokenError('Access token has been revoked');
|
|
393
|
-
return {
|
|
394
|
-
token,
|
|
395
|
-
clientId: payload.client_id,
|
|
396
|
-
scopes: typeof payload.scope === 'string' && payload.scope.length > 0 ? payload.scope.split(' ') : [],
|
|
397
|
-
expiresAt: payload.exp,
|
|
398
|
-
resource
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
/** Audience check for already-issued tokens: never falls back to a default. */
|
|
402
|
-
resolveResourceStrict(audience) {
|
|
403
|
-
let url;
|
|
404
|
-
try {
|
|
405
|
-
url = new URL(audience);
|
|
406
|
-
}
|
|
407
|
-
catch {
|
|
408
|
-
return undefined;
|
|
409
|
-
}
|
|
410
|
-
return this.options.resolveResource?.(url);
|
|
411
|
-
}
|
|
412
|
-
async revokeToken(client, request) {
|
|
413
|
-
const record = this.store.getRefreshToken(request.token);
|
|
414
|
-
if (record && record.clientId === client.client_id)
|
|
415
|
-
this.store.deleteRefreshToken(request.token);
|
|
416
|
-
// Individual JWTs are not stored; the offline admin command revokes all
|
|
417
|
-
// access and refresh tokens for a client through the revokedBefore marker.
|
|
418
|
-
}
|
|
419
|
-
resolveResource(resource) {
|
|
420
|
-
if (!resource) {
|
|
421
|
-
// Real clients omit the RFC 8707 parameter (older Codex logins, Google
|
|
422
|
-
// ADK, Gemini Enterprise). DEFAULT_RESOURCE lets the operator route
|
|
423
|
-
// those onto one chosen resource instead of turning them away — the
|
|
424
|
-
// token is still bound, never global.
|
|
425
|
-
if (this.options.defaultResource)
|
|
426
|
-
return this.options.defaultResource;
|
|
427
|
-
if (this.options.requireResource)
|
|
428
|
-
throw new InvalidTargetError('A resource indicator is required');
|
|
429
|
-
return undefined;
|
|
430
|
-
}
|
|
431
|
-
const resolved = this.options.resolveResource?.(resource);
|
|
432
|
-
if (!resolved)
|
|
433
|
-
throw new InvalidTargetError('Unknown or invalid resource');
|
|
434
|
-
return resolved;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
//# sourceMappingURL=provider.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/auth/provider.ts"],"names":[],"mappings":"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"}
|