runbios-mcp 0.2.1-dev.62

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 (90) hide show
  1. package/README.md +363 -0
  2. package/dist/api-client.d.ts +147 -0
  3. package/dist/api-client.d.ts.map +1 -0
  4. package/dist/api-client.js +806 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/auth.d.ts +3 -0
  7. package/dist/auth.d.ts.map +1 -0
  8. package/dist/auth.js +11 -0
  9. package/dist/auth.js.map +1 -0
  10. package/dist/config.d.ts +30 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +16 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/deployment-contract.d.ts +78 -0
  15. package/dist/deployment-contract.d.ts.map +1 -0
  16. package/dist/deployment-contract.js +155 -0
  17. package/dist/deployment-contract.js.map +1 -0
  18. package/dist/gpu-priorities.d.ts +25 -0
  19. package/dist/gpu-priorities.d.ts.map +1 -0
  20. package/dist/gpu-priorities.js +61 -0
  21. package/dist/gpu-priorities.js.map +1 -0
  22. package/dist/http/app.d.ts +14 -0
  23. package/dist/http/app.d.ts.map +1 -0
  24. package/dist/http/app.js +140 -0
  25. package/dist/http/app.js.map +1 -0
  26. package/dist/http/audit.d.ts +23 -0
  27. package/dist/http/audit.d.ts.map +1 -0
  28. package/dist/http/audit.js +41 -0
  29. package/dist/http/audit.js.map +1 -0
  30. package/dist/http/config.d.ts +23 -0
  31. package/dist/http/config.d.ts.map +1 -0
  32. package/dist/http/config.js +70 -0
  33. package/dist/http/config.js.map +1 -0
  34. package/dist/http/consent.d.ts +18 -0
  35. package/dist/http/consent.d.ts.map +1 -0
  36. package/dist/http/consent.js +148 -0
  37. package/dist/http/consent.js.map +1 -0
  38. package/dist/http/internal-auth.d.ts +22 -0
  39. package/dist/http/internal-auth.d.ts.map +1 -0
  40. package/dist/http/internal-auth.js +73 -0
  41. package/dist/http/internal-auth.js.map +1 -0
  42. package/dist/http/main.d.ts +3 -0
  43. package/dist/http/main.d.ts.map +1 -0
  44. package/dist/http/main.js +65 -0
  45. package/dist/http/main.js.map +1 -0
  46. package/dist/http/mcp-handler.d.ts +12 -0
  47. package/dist/http/mcp-handler.d.ts.map +1 -0
  48. package/dist/http/mcp-handler.js +103 -0
  49. package/dist/http/mcp-handler.js.map +1 -0
  50. package/dist/http/metadata.d.ts +6 -0
  51. package/dist/http/metadata.d.ts.map +1 -0
  52. package/dist/http/metadata.js +32 -0
  53. package/dist/http/metadata.js.map +1 -0
  54. package/dist/http/oauth.d.ts +56 -0
  55. package/dist/http/oauth.d.ts.map +1 -0
  56. package/dist/http/oauth.js +486 -0
  57. package/dist/http/oauth.js.map +1 -0
  58. package/dist/http/serviceHostGuard.d.ts +31 -0
  59. package/dist/http/serviceHostGuard.d.ts.map +1 -0
  60. package/dist/http/serviceHostGuard.js +68 -0
  61. package/dist/http/serviceHostGuard.js.map +1 -0
  62. package/dist/http/store.d.ts +150 -0
  63. package/dist/http/store.d.ts.map +1 -0
  64. package/dist/http/store.js +366 -0
  65. package/dist/http/store.js.map +1 -0
  66. package/dist/index.d.ts +3 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +79 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/inference-contract.d.ts +29 -0
  71. package/dist/inference-contract.d.ts.map +1 -0
  72. package/dist/inference-contract.js +112 -0
  73. package/dist/inference-contract.js.map +1 -0
  74. package/dist/redaction.d.ts +74 -0
  75. package/dist/redaction.d.ts.map +1 -0
  76. package/dist/redaction.js +316 -0
  77. package/dist/redaction.js.map +1 -0
  78. package/dist/server.d.ts +63 -0
  79. package/dist/server.d.ts.map +1 -0
  80. package/dist/server.js +2282 -0
  81. package/dist/server.js.map +1 -0
  82. package/dist/training-contract.d.ts +107 -0
  83. package/dist/training-contract.d.ts.map +1 -0
  84. package/dist/training-contract.js +141 -0
  85. package/dist/training-contract.js.map +1 -0
  86. package/dist/version.d.ts +2 -0
  87. package/dist/version.d.ts.map +1 -0
  88. package/dist/version.js +2 -0
  89. package/dist/version.js.map +1 -0
  90. package/package.json +47 -0
@@ -0,0 +1,486 @@
1
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { Router } from "express";
3
+ /* ─── Canonical scopes (mirror services/iam-service/internal/handler/apikey_scopes.go) ─── */
4
+ export const CANONICAL_SCOPES = [
5
+ "models:read",
6
+ "datasets:read",
7
+ "datasets:write",
8
+ "training:read",
9
+ "training:write",
10
+ "deployments:read",
11
+ "deployments:write",
12
+ "billing:read",
13
+ "analytics:read",
14
+ "integrations:read",
15
+ "integrations:write",
16
+ // Data-plane scope for the OpenAI-compatible serverless inference API. The
17
+ // gateway enforces it in the unified /v1 dispatcher, not the /api route
18
+ // allowlist, but it is a first-class canonical scope (part of the read-only
19
+ // and full-access presets in IAM), so a grant may request and hold it.
20
+ "serverless",
21
+ ];
22
+ const CANONICAL_SCOPE_SET = new Set(CANONICAL_SCOPES);
23
+ /**
24
+ * Validates a scope list: non-empty, every scope canonical, no duplicates.
25
+ * Returns the scopes in catalog order (deterministic, like the Go side), or
26
+ * null when invalid.
27
+ */
28
+ export function normalizeScopes(scopes) {
29
+ if (!Array.isArray(scopes) || scopes.length === 0)
30
+ return null;
31
+ const seen = new Set();
32
+ for (const raw of scopes) {
33
+ if (typeof raw !== "string")
34
+ return null;
35
+ const scope = raw.trim();
36
+ if (!scope || !CANONICAL_SCOPE_SET.has(scope) || seen.has(scope))
37
+ return null;
38
+ seen.add(scope);
39
+ }
40
+ return CANONICAL_SCOPES.filter((s) => seen.has(s));
41
+ }
42
+ /** Splits a space-separated scope parameter (RFC 6749 §3.3). */
43
+ export function parseScopeParam(scope) {
44
+ return scope.split(" ").map((s) => s.trim()).filter((s) => s.length > 0);
45
+ }
46
+ /* ─── Small crypto helpers ─── */
47
+ export function sha256Hex(value) {
48
+ return createHash("sha256").update(value).digest("hex");
49
+ }
50
+ /** Constant-time string equality (hash both sides to normalize length). */
51
+ export function timingSafeEqualStr(a, b) {
52
+ const da = createHash("sha256").update(a).digest();
53
+ const db = createHash("sha256").update(b).digest();
54
+ return timingSafeEqual(da, db);
55
+ }
56
+ export function newRawToken(prefix) {
57
+ return prefix + randomBytes(32).toString("base64url");
58
+ }
59
+ export function newId(prefix) {
60
+ return `${prefix}_${randomBytes(12).toString("hex")}`;
61
+ }
62
+ /* ─── PKCE (RFC 7636, S256 only) ─── */
63
+ const PKCE_VERIFIER_RE = /^[A-Za-z0-9\-._~]{43,128}$/;
64
+ export function verifyPkceS256(codeVerifier, codeChallenge) {
65
+ if (!PKCE_VERIFIER_RE.test(codeVerifier))
66
+ return false;
67
+ const computed = createHash("sha256").update(codeVerifier).digest("base64url");
68
+ return timingSafeEqualStr(computed, codeChallenge);
69
+ }
70
+ /* ─── Redirect URI validation ─── */
71
+ /**
72
+ * Registered redirect URIs must be https, or http with a loopback hostname
73
+ * (localhost / 127.0.0.1) for native clients, and must not carry a fragment.
74
+ */
75
+ export function isValidRedirectUri(uri) {
76
+ if (typeof uri !== "string" || uri.length === 0 || uri.length > 2000)
77
+ return false;
78
+ let parsed;
79
+ try {
80
+ parsed = new URL(uri);
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ if (parsed.hash)
86
+ return false;
87
+ if (parsed.protocol === "https:")
88
+ return true;
89
+ if (parsed.protocol === "http:") {
90
+ return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
91
+ }
92
+ return false;
93
+ }
94
+ /* ─── Token lifetimes ─── */
95
+ export const ACCESS_TOKEN_TTL_S = 3600;
96
+ export const REFRESH_TOKEN_TTL_MS = 30 * 24 * 3600_000;
97
+ export const AUTH_CODE_TTL_MS = 10 * 60_000;
98
+ export const AUTH_REQUEST_TTL_MS = 15 * 60_000;
99
+ export async function validateAccessToken(store, rawToken) {
100
+ if (!rawToken.startsWith("mcp_at_"))
101
+ return null;
102
+ const token = await store.getToken(sha256Hex(rawToken));
103
+ if (!token || token.kind !== "access" || token.revoked_at)
104
+ return null;
105
+ if (token.expires_at.getTime() <= Date.now())
106
+ return null;
107
+ const grant = await store.getGrant(token.grant_id);
108
+ if (!grant || grant.revoked_at)
109
+ return null;
110
+ // Throttled usage stamp: at most once a minute per grant, best-effort.
111
+ void store.touchGrantLastUsed(grant.id, 60_000).catch(() => { });
112
+ return { token, grant };
113
+ }
114
+ export async function issueTokenPair(store, grantId, familyId = newId("fam")) {
115
+ const accessToken = newRawToken("mcp_at_");
116
+ const refreshToken = newRawToken("mcp_rt_");
117
+ const refreshTokenHash = sha256Hex(refreshToken);
118
+ const now = Date.now();
119
+ await store.insertToken({
120
+ token_hash: sha256Hex(accessToken),
121
+ kind: "access",
122
+ grant_id: grantId,
123
+ family_id: familyId,
124
+ expires_at: new Date(now + ACCESS_TOKEN_TTL_S * 1000),
125
+ });
126
+ await store.insertToken({
127
+ token_hash: refreshTokenHash,
128
+ kind: "refresh",
129
+ grant_id: grantId,
130
+ family_id: familyId,
131
+ expires_at: new Date(now + REFRESH_TOKEN_TTL_MS),
132
+ });
133
+ return { accessToken, refreshToken, refreshTokenHash };
134
+ }
135
+ /* ─── In-memory sliding-window rate limiter (defense in depth) ─── */
136
+ export class SlidingWindowLimiter {
137
+ limit;
138
+ windowMs;
139
+ now;
140
+ hits = new Map();
141
+ constructor(limit, windowMs, now = Date.now) {
142
+ this.limit = limit;
143
+ this.windowMs = windowMs;
144
+ this.now = now;
145
+ }
146
+ allow(key) {
147
+ const t = this.now();
148
+ const cutoff = t - this.windowMs;
149
+ const alive = (this.hits.get(key) ?? []).filter((x) => x > cutoff);
150
+ if (alive.length >= this.limit) {
151
+ this.hits.set(key, alive);
152
+ return false;
153
+ }
154
+ alive.push(t);
155
+ this.hits.set(key, alive);
156
+ if (this.hits.size > 10_000) {
157
+ for (const [k, v] of this.hits) {
158
+ if (!v.some((x) => x > cutoff))
159
+ this.hits.delete(k);
160
+ }
161
+ }
162
+ return true;
163
+ }
164
+ }
165
+ const SUPPORTED_AUTH_METHODS = new Set(["none", "client_secret_basic", "client_secret_post"]);
166
+ const SUPPORTED_GRANT_TYPES = new Set(["authorization_code", "refresh_token"]);
167
+ const SUPPORTED_RESPONSE_TYPES = new Set(["code"]);
168
+ function str(value) {
169
+ return typeof value === "string" ? value : "";
170
+ }
171
+ function oauthError(res, status, error, description) {
172
+ res.status(status).set("Cache-Control", "no-store").json({ error, error_description: description });
173
+ }
174
+ function htmlBadRequest(res, message) {
175
+ res
176
+ .status(400)
177
+ .set("Content-Type", "text/html; charset=utf-8")
178
+ .send(`<!doctype html><html><head><meta charset="utf-8"><title>Connection error</title>` +
179
+ `<meta name="viewport" content="width=device-width, initial-scale=1"></head>` +
180
+ `<body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1rem;color:#111">` +
181
+ `<h1 style="font-size:1.25rem">We can't complete this connection</h1>` +
182
+ `<p>${message}</p>` +
183
+ `<p>Close this page and start the connection again from your MCP client.</p>` +
184
+ `</body></html>`);
185
+ }
186
+ function redirectWithError(res, redirectUri, error, description, state) {
187
+ const url = new URL(redirectUri);
188
+ url.searchParams.set("error", error);
189
+ if (description)
190
+ url.searchParams.set("error_description", description);
191
+ if (state)
192
+ url.searchParams.set("state", state);
193
+ res.redirect(302, url.toString());
194
+ }
195
+ function parseBasicAuth(header) {
196
+ if (!header || !header.startsWith("Basic "))
197
+ return null;
198
+ let decoded;
199
+ try {
200
+ decoded = Buffer.from(header.slice(6), "base64").toString("utf8");
201
+ }
202
+ catch {
203
+ return null;
204
+ }
205
+ const idx = decoded.indexOf(":");
206
+ if (idx < 0)
207
+ return null;
208
+ const decode = (v) => {
209
+ try {
210
+ return decodeURIComponent(v);
211
+ }
212
+ catch {
213
+ return v;
214
+ }
215
+ };
216
+ return { clientId: decode(decoded.slice(0, idx)), clientSecret: decode(decoded.slice(idx + 1)) };
217
+ }
218
+ async function authenticateClient(store, req) {
219
+ const body = (req.body ?? {});
220
+ const basic = parseBasicAuth(req.header("authorization"));
221
+ const clientId = basic?.clientId || str(body.client_id);
222
+ const usedBasic = Boolean(basic);
223
+ if (!clientId)
224
+ return { failed: true, usedBasic };
225
+ const client = await store.getClient(clientId);
226
+ if (!client || client.disabled_at)
227
+ return { failed: true, usedBasic };
228
+ if (client.token_endpoint_auth_method === "none") {
229
+ return { client, usedBasic };
230
+ }
231
+ const secret = basic?.clientSecret || str(body.client_secret);
232
+ if (!secret || !client.client_secret_hash || !timingSafeEqualStr(sha256Hex(secret), client.client_secret_hash)) {
233
+ return { failed: true, usedBasic };
234
+ }
235
+ return { client, usedBasic };
236
+ }
237
+ export function createOAuthRouter(deps) {
238
+ const { store, cfg, audit } = deps;
239
+ const registerLimiter = deps.registerLimiter ?? new SlidingWindowLimiter(10, 3600_000);
240
+ const tokenLimiter = deps.tokenLimiter ?? new SlidingWindowLimiter(60, 60_000);
241
+ const router = Router();
242
+ /* ─── RFC 7591 dynamic client registration ─── */
243
+ router.post("/mcp/oauth/register", async (req, res) => {
244
+ if (!registerLimiter.allow(req.ip ?? "unknown")) {
245
+ res.status(429).json({ error: "slow_down" });
246
+ return;
247
+ }
248
+ const body = (req.body ?? {});
249
+ const redirectUris = body.redirect_uris;
250
+ if (!Array.isArray(redirectUris) || redirectUris.length === 0) {
251
+ oauthError(res, 400, "invalid_redirect_uri", "redirect_uris must be a non-empty array");
252
+ return;
253
+ }
254
+ for (const uri of redirectUris) {
255
+ if (typeof uri !== "string" || !isValidRedirectUri(uri)) {
256
+ oauthError(res, 400, "invalid_redirect_uri", "each redirect_uri must be https, or http on localhost/127.0.0.1, with no fragment");
257
+ return;
258
+ }
259
+ }
260
+ let clientName = str(body.client_name).trim();
261
+ if (!clientName)
262
+ clientName = "MCP client";
263
+ if (clientName.length > 100)
264
+ clientName = clientName.slice(0, 100);
265
+ const authMethod = str(body.token_endpoint_auth_method) || "none";
266
+ if (!SUPPORTED_AUTH_METHODS.has(authMethod)) {
267
+ oauthError(res, 400, "invalid_client_metadata", "token_endpoint_auth_method must be one of: none, client_secret_basic, client_secret_post");
268
+ return;
269
+ }
270
+ if (body.grant_types !== undefined) {
271
+ const grantTypes = Array.isArray(body.grant_types) ? body.grant_types : null;
272
+ if (!grantTypes || grantTypes.some((g) => typeof g !== "string" || !SUPPORTED_GRANT_TYPES.has(g))) {
273
+ oauthError(res, 400, "invalid_client_metadata", "grant_types may only contain authorization_code and refresh_token");
274
+ return;
275
+ }
276
+ }
277
+ if (body.response_types !== undefined) {
278
+ const responseTypes = Array.isArray(body.response_types) ? body.response_types : null;
279
+ if (!responseTypes || responseTypes.some((r) => typeof r !== "string" || !SUPPORTED_RESPONSE_TYPES.has(r))) {
280
+ oauthError(res, 400, "invalid_client_metadata", "response_types may only contain code");
281
+ return;
282
+ }
283
+ }
284
+ const clientId = newId("client");
285
+ let clientSecret;
286
+ let clientSecretHash = null;
287
+ if (authMethod === "client_secret_basic" || authMethod === "client_secret_post") {
288
+ clientSecret = randomBytes(32).toString("base64url");
289
+ clientSecretHash = sha256Hex(clientSecret);
290
+ }
291
+ await store.insertClient({
292
+ client_id: clientId,
293
+ client_secret_hash: clientSecretHash,
294
+ client_name: clientName,
295
+ redirect_uris: redirectUris,
296
+ token_endpoint_auth_method: authMethod,
297
+ });
298
+ audit.emit("oauth_client_registered", { client_id: clientId, client_name: clientName });
299
+ res.status(201).set("Cache-Control", "no-store").json({
300
+ client_id: clientId,
301
+ ...(clientSecret ? { client_secret: clientSecret, client_secret_expires_at: 0 } : {}),
302
+ client_id_issued_at: Math.floor(Date.now() / 1000),
303
+ client_name: clientName,
304
+ redirect_uris: redirectUris,
305
+ grant_types: ["authorization_code", "refresh_token"],
306
+ response_types: ["code"],
307
+ token_endpoint_auth_method: authMethod,
308
+ });
309
+ });
310
+ /* ─── Authorization endpoint ─── */
311
+ router.get("/mcp/oauth/authorize", async (req, res) => {
312
+ const q = req.query;
313
+ const clientId = str(q.client_id);
314
+ const redirectUri = str(q.redirect_uri);
315
+ const state = str(q.state);
316
+ // Unknown client or unregistered redirect_uri: render an error page.
317
+ // Never redirect to an unverified target (open-redirect prevention).
318
+ const client = clientId ? await store.getClient(clientId) : null;
319
+ if (!client || client.disabled_at) {
320
+ htmlBadRequest(res, "The application that sent you here is not registered with Run BiOS.");
321
+ return;
322
+ }
323
+ if (!redirectUri || !client.redirect_uris.includes(redirectUri)) {
324
+ htmlBadRequest(res, "The application used a return address that does not match its registration.");
325
+ return;
326
+ }
327
+ // From here the redirect target is trusted: report errors per RFC 6749.
328
+ if (str(q.response_type) !== "code") {
329
+ redirectWithError(res, redirectUri, "unsupported_response_type", "response_type must be code", state);
330
+ return;
331
+ }
332
+ const codeChallenge = str(q.code_challenge);
333
+ const codeChallengeMethod = str(q.code_challenge_method);
334
+ if (!codeChallenge || codeChallengeMethod !== "S256") {
335
+ redirectWithError(res, redirectUri, "invalid_request", "PKCE with code_challenge_method=S256 is required", state);
336
+ return;
337
+ }
338
+ const scopeParam = str(q.scope);
339
+ if (scopeParam) {
340
+ const requested = parseScopeParam(scopeParam);
341
+ if (requested.length === 0 || requested.some((s) => !CANONICAL_SCOPE_SET.has(s))) {
342
+ redirectWithError(res, redirectUri, "invalid_scope", "unknown scope requested", state);
343
+ return;
344
+ }
345
+ }
346
+ const id = newId("req");
347
+ await store.insertRequest({
348
+ id,
349
+ client_id: client.client_id,
350
+ redirect_uri: redirectUri,
351
+ scope: scopeParam,
352
+ state,
353
+ code_challenge: codeChallenge,
354
+ code_challenge_method: "S256",
355
+ status: "pending",
356
+ expires_at: new Date(Date.now() + AUTH_REQUEST_TTL_MS),
357
+ });
358
+ res.redirect(302, `${cfg.consoleBaseUrl}/connect/mcp?request=${encodeURIComponent(id)}`);
359
+ });
360
+ /* ─── Token endpoint ─── */
361
+ router.post("/mcp/oauth/token", async (req, res) => {
362
+ if (!tokenLimiter.allow(req.ip ?? "unknown")) {
363
+ res.status(429).json({ error: "slow_down" });
364
+ return;
365
+ }
366
+ res.set("Cache-Control", "no-store");
367
+ res.set("Pragma", "no-cache");
368
+ const body = (req.body ?? {});
369
+ const grantType = str(body.grant_type);
370
+ const auth = await authenticateClient(store, req);
371
+ if (auth.failed || !auth.client) {
372
+ if (auth.usedBasic)
373
+ res.set("WWW-Authenticate", 'Basic realm="mcp"');
374
+ oauthError(res, 401, "invalid_client", "client authentication failed");
375
+ return;
376
+ }
377
+ const client = auth.client;
378
+ if (grantType === "authorization_code") {
379
+ await handleAuthorizationCodeGrant(req, res, client);
380
+ return;
381
+ }
382
+ if (grantType === "refresh_token") {
383
+ await handleRefreshTokenGrant(req, res, client);
384
+ return;
385
+ }
386
+ oauthError(res, 400, "unsupported_grant_type", "grant_type must be authorization_code or refresh_token");
387
+ });
388
+ async function revokeGrantEverything(grantId, reason) {
389
+ await store.revokeTokensForGrant(grantId);
390
+ await store.revokeGrant(grantId);
391
+ audit.emit("oauth_grant_revoked", { grant_id: grantId, reason });
392
+ }
393
+ async function handleAuthorizationCodeGrant(req, res, client) {
394
+ const body = (req.body ?? {});
395
+ const code = str(body.code);
396
+ const codeVerifier = str(body.code_verifier);
397
+ const redirectUri = str(body.redirect_uri);
398
+ if (!code || !codeVerifier || !redirectUri) {
399
+ oauthError(res, 400, "invalid_request", "code, code_verifier, and redirect_uri are required");
400
+ return;
401
+ }
402
+ const codeHash = sha256Hex(code);
403
+ const prior = await store.getCode(codeHash);
404
+ if (!prior) {
405
+ oauthError(res, 400, "invalid_grant", "authorization code is invalid or expired");
406
+ return;
407
+ }
408
+ if (prior.used_at) {
409
+ // OAuth 2.1: a replayed code revokes everything issued from it.
410
+ await revokeGrantEverything(prior.grant_id, "authorization_code_replay");
411
+ oauthError(res, 400, "invalid_grant", "authorization code is invalid or expired");
412
+ return;
413
+ }
414
+ const consumed = await store.consumeCode(codeHash);
415
+ if (!consumed) {
416
+ // Lost a race with a concurrent redemption: also a replay.
417
+ await revokeGrantEverything(prior.grant_id, "authorization_code_replay");
418
+ oauthError(res, 400, "invalid_grant", "authorization code is invalid or expired");
419
+ return;
420
+ }
421
+ const valid = consumed.expires_at.getTime() > Date.now() &&
422
+ consumed.client_id === client.client_id &&
423
+ consumed.redirect_uri === redirectUri &&
424
+ consumed.code_challenge_method === "S256" &&
425
+ verifyPkceS256(codeVerifier, consumed.code_challenge);
426
+ if (!valid) {
427
+ oauthError(res, 400, "invalid_grant", "authorization code is invalid or expired");
428
+ return;
429
+ }
430
+ const grant = await store.getGrant(consumed.grant_id);
431
+ if (!grant || grant.revoked_at) {
432
+ oauthError(res, 400, "invalid_grant", "authorization grant is no longer valid");
433
+ return;
434
+ }
435
+ const pair = await issueTokenPair(store, grant.id);
436
+ res.status(200).json({
437
+ access_token: pair.accessToken,
438
+ token_type: "Bearer",
439
+ expires_in: ACCESS_TOKEN_TTL_S,
440
+ refresh_token: pair.refreshToken,
441
+ scope: grant.scopes.join(" "),
442
+ });
443
+ }
444
+ async function handleRefreshTokenGrant(req, res, client) {
445
+ const body = (req.body ?? {});
446
+ const rawRefresh = str(body.refresh_token);
447
+ if (!rawRefresh) {
448
+ oauthError(res, 400, "invalid_request", "refresh_token is required");
449
+ return;
450
+ }
451
+ const row = await store.getToken(sha256Hex(rawRefresh));
452
+ if (!row || row.kind !== "refresh") {
453
+ oauthError(res, 400, "invalid_grant", "refresh token is invalid or expired");
454
+ return;
455
+ }
456
+ const grant = await store.getGrant(row.grant_id);
457
+ if (!grant || grant.client_id !== client.client_id) {
458
+ oauthError(res, 400, "invalid_grant", "refresh token is invalid or expired");
459
+ return;
460
+ }
461
+ if (row.revoked_at) {
462
+ // Rotation reuse: revoke the whole family and the grant.
463
+ await store.revokeFamily(row.family_id);
464
+ await revokeGrantEverything(grant.id, "refresh_token_reuse");
465
+ audit.emit("oauth_refresh_reuse_detected", { grant_id: grant.id, family_id: row.family_id });
466
+ oauthError(res, 400, "invalid_grant", "refresh token is invalid or expired");
467
+ return;
468
+ }
469
+ if (grant.revoked_at || row.expires_at.getTime() <= Date.now()) {
470
+ oauthError(res, 400, "invalid_grant", "refresh token is invalid or expired");
471
+ return;
472
+ }
473
+ const pair = await issueTokenPair(store, grant.id, row.family_id);
474
+ await store.revokeToken(row.token_hash, pair.refreshTokenHash);
475
+ audit.emit("oauth_token_refreshed", { grant_id: grant.id });
476
+ res.status(200).json({
477
+ access_token: pair.accessToken,
478
+ token_type: "Bearer",
479
+ expires_in: ACCESS_TOKEN_TTL_S,
480
+ refresh_token: pair.refreshToken,
481
+ scope: grant.scopes.join(" "),
482
+ });
483
+ }
484
+ return router;
485
+ }
486
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/http/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * ECS boot guard for internal service peers.
3
+ *
4
+ * Mirrors the Go services' MissingServiceHostsInECS contract exactly:
5
+ * 1. Fires ONLY under ECS (detected via ECS_CONTAINER_METADATA_URI_V4 or
6
+ * ECS_CONTAINER_METADATA_URI). Under local dev / unit tests it is a
7
+ * complete no-op — localhost is the correct answer locally.
8
+ * 2. Non-fatal: callers log a loud ERROR per problem and keep booting. A
9
+ * listed peer may be unused at runtime, so crashing is unsafe.
10
+ * 3. For each internal peer it checks the ACTUAL env var the service reads:
11
+ * - UNSET/empty -> ERROR (falls back to localhost)
12
+ * - value contains localhost/127.0.0.1 -> ERROR
13
+ *
14
+ * Dependency-free (stdlib only) so it is trivially unit-testable in isolation.
15
+ */
16
+ /** True when running inside an ECS task (task metadata endpoint is injected). */
17
+ export declare function runningInEcs(env?: NodeJS.ProcessEnv): boolean;
18
+ /**
19
+ * Returns one entry per misconfigured internal peer. Always empty when not
20
+ * running under ECS.
21
+ */
22
+ export declare function checkServiceHosts(env?: NodeJS.ProcessEnv): Array<{
23
+ envVar: string;
24
+ reason: string;
25
+ }>;
26
+ /**
27
+ * Logs one ERROR line per problem via console.error. No-op when there are no
28
+ * problems (including when not under ECS). Never throws.
29
+ */
30
+ export declare function logServiceHostProblems(env?: NodeJS.ProcessEnv): void;
31
+ //# sourceMappingURL=serviceHostGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceHostGuard.d.ts","sourceRoot":"","sources":["../../src/http/serviceHostGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAI1E;AAOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA+B3C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAIjF"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * ECS boot guard for internal service peers.
3
+ *
4
+ * Mirrors the Go services' MissingServiceHostsInECS contract exactly:
5
+ * 1. Fires ONLY under ECS (detected via ECS_CONTAINER_METADATA_URI_V4 or
6
+ * ECS_CONTAINER_METADATA_URI). Under local dev / unit tests it is a
7
+ * complete no-op — localhost is the correct answer locally.
8
+ * 2. Non-fatal: callers log a loud ERROR per problem and keep booting. A
9
+ * listed peer may be unused at runtime, so crashing is unsafe.
10
+ * 3. For each internal peer it checks the ACTUAL env var the service reads:
11
+ * - UNSET/empty -> ERROR (falls back to localhost)
12
+ * - value contains localhost/127.0.0.1 -> ERROR
13
+ *
14
+ * Dependency-free (stdlib only) so it is trivially unit-testable in isolation.
15
+ */
16
+ /** True when running inside an ECS task (task metadata endpoint is injected). */
17
+ export function runningInEcs(env = process.env) {
18
+ const v4 = (env.ECS_CONTAINER_METADATA_URI_V4 ?? "").trim();
19
+ const v3 = (env.ECS_CONTAINER_METADATA_URI ?? "").trim();
20
+ return v4.length > 0 || v3.length > 0;
21
+ }
22
+ function pointsAtLocalhost(value) {
23
+ const v = value.toLowerCase();
24
+ return v.includes("localhost") || v.includes("127.0.0.1");
25
+ }
26
+ /**
27
+ * Returns one entry per misconfigured internal peer. Always empty when not
28
+ * running under ECS.
29
+ */
30
+ export function checkServiceHosts(env = process.env) {
31
+ if (!runningInEcs(env))
32
+ return [];
33
+ const problems = [];
34
+ // Required peer: the api-gateway. UNSET/empty falls back to "localhost".
35
+ const apiGateway = (env.API_GATEWAY_HOST ?? "").trim();
36
+ if (apiGateway === "") {
37
+ problems.push({
38
+ envVar: "API_GATEWAY_HOST",
39
+ reason: "API_GATEWAY_HOST is UNSET under ECS - internal calls fall back to localhost and will fail; set it to the peer's Service Connect DNS name",
40
+ });
41
+ }
42
+ else if (pointsAtLocalhost(apiGateway)) {
43
+ problems.push({
44
+ envVar: "API_GATEWAY_HOST",
45
+ reason: "API_GATEWAY_HOST points at localhost/127.0.0.1 under ECS",
46
+ });
47
+ }
48
+ // Optional peer: audit-service. UNSET means audit is intentionally disabled,
49
+ // so do NOT warn. Only warn if SET but pointing at localhost/127.0.0.1.
50
+ const auditHost = (env.AUDIT_SERVICE_HOST ?? "").trim();
51
+ if (auditHost !== "" && pointsAtLocalhost(auditHost)) {
52
+ problems.push({
53
+ envVar: "AUDIT_SERVICE_HOST",
54
+ reason: "AUDIT_SERVICE_HOST points at localhost/127.0.0.1 under ECS",
55
+ });
56
+ }
57
+ return problems;
58
+ }
59
+ /**
60
+ * Logs one ERROR line per problem via console.error. No-op when there are no
61
+ * problems (including when not under ECS). Never throws.
62
+ */
63
+ export function logServiceHostProblems(env = process.env) {
64
+ for (const problem of checkServiceHosts(env)) {
65
+ console.error(`[service-host-guard] ${problem.reason}`);
66
+ }
67
+ }
68
+ //# sourceMappingURL=serviceHostGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceHostGuard.js","sourceRoot":"","sources":["../../src/http/serviceHostGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC/D,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACzD,OAAO,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC9B,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC5D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,QAAQ,GAA8C,EAAE,CAAC;IAE/D,yEAAyE;IACzE,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EACJ,0IAA0I;SAC7I,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,iBAAiB,CAAC,UAAU,CAAC,EAAE,CAAC;QACzC,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EAAE,0DAA0D;SACnE,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,wEAAwE;IACxE,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,IAAI,SAAS,KAAK,EAAE,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE,CAAC;QACrD,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,oBAAoB;YAC5B,MAAM,EAAE,4DAA4D;SACrE,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACzE,KAAK,MAAM,OAAO,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,OAAO,CAAC,KAAK,CAAC,wBAAwB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC"}