@pymthouse/builder-sdk 0.3.0 → 0.4.1-rc.1

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 (103) hide show
  1. package/README.md +54 -28
  2. package/dist/{client-BHfjDvIe.d.ts → client-CauCfGa7.d.ts} +1 -1
  3. package/dist/{client-CvhJEhjV.d.cts → client-D1Xz-xlx.d.cts} +1 -1
  4. package/dist/config.cjs +0 -21
  5. package/dist/config.cjs.map +1 -1
  6. package/dist/config.d.cts +1 -5
  7. package/dist/config.d.ts +1 -5
  8. package/dist/config.js +1 -20
  9. package/dist/config.js.map +1 -1
  10. package/dist/device-initiate.cjs.map +1 -1
  11. package/dist/device-initiate.js.map +1 -1
  12. package/dist/device.cjs.map +1 -1
  13. package/dist/device.d.cts +1 -1
  14. package/dist/device.d.ts +1 -1
  15. package/dist/device.js.map +1 -1
  16. package/dist/env.cjs +13 -4
  17. package/dist/env.cjs.map +1 -1
  18. package/dist/env.d.cts +2 -2
  19. package/dist/env.d.ts +2 -2
  20. package/dist/env.js +13 -4
  21. package/dist/env.js.map +1 -1
  22. package/dist/index-BTDKEorK.d.ts +64 -0
  23. package/dist/index-BixH4VIG.d.cts +64 -0
  24. package/dist/index.cjs +13 -4
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +29 -5
  27. package/dist/index.d.ts +29 -5
  28. package/dist/index.js +13 -4
  29. package/dist/index.js.map +1 -1
  30. package/dist/{ingest-DoKJTWU9.d.ts → proxy-JrT6raU_.d.cts} +5 -42
  31. package/dist/{ingest-B3Yi8Tb1.d.cts → proxy-U32DFNuj.d.ts} +5 -42
  32. package/dist/signer/server.cjs +799 -895
  33. package/dist/signer/server.cjs.map +1 -1
  34. package/dist/signer/server.d.cts +9 -13
  35. package/dist/signer/server.d.ts +9 -13
  36. package/dist/signer/server.js +799 -893
  37. package/dist/signer/server.js.map +1 -1
  38. package/dist/signer/webhook/adapters/api-key.cjs +78 -0
  39. package/dist/signer/webhook/adapters/api-key.cjs.map +1 -0
  40. package/dist/signer/webhook/adapters/api-key.d.cts +18 -0
  41. package/dist/signer/webhook/adapters/api-key.d.ts +18 -0
  42. package/dist/signer/webhook/adapters/api-key.js +76 -0
  43. package/dist/signer/webhook/adapters/api-key.js.map +1 -0
  44. package/dist/signer/webhook/adapters/composite.cjs +60 -0
  45. package/dist/signer/webhook/adapters/composite.cjs.map +1 -0
  46. package/dist/signer/webhook/adapters/composite.d.cts +5 -0
  47. package/dist/signer/webhook/adapters/composite.d.ts +5 -0
  48. package/dist/signer/webhook/adapters/composite.js +58 -0
  49. package/dist/signer/webhook/adapters/composite.js.map +1 -0
  50. package/dist/signer/webhook/adapters/oauth1.cjs +18 -0
  51. package/dist/signer/webhook/adapters/oauth1.cjs.map +1 -0
  52. package/dist/signer/webhook/adapters/oauth1.d.cts +19 -0
  53. package/dist/signer/webhook/adapters/oauth1.d.ts +19 -0
  54. package/dist/signer/webhook/adapters/oauth1.js +16 -0
  55. package/dist/signer/webhook/adapters/oauth1.js.map +1 -0
  56. package/dist/signer/webhook/adapters/oidc.cjs +522 -0
  57. package/dist/signer/webhook/adapters/oidc.cjs.map +1 -0
  58. package/dist/signer/webhook/adapters/oidc.d.cts +4 -0
  59. package/dist/signer/webhook/adapters/oidc.d.ts +4 -0
  60. package/dist/signer/webhook/adapters/oidc.js +515 -0
  61. package/dist/signer/webhook/adapters/oidc.js.map +1 -0
  62. package/dist/signer/webhook/adapters/trusted-headers.cjs +103 -0
  63. package/dist/signer/webhook/adapters/trusted-headers.cjs.map +1 -0
  64. package/dist/signer/webhook/adapters/trusted-headers.d.cts +18 -0
  65. package/dist/signer/webhook/adapters/trusted-headers.d.ts +18 -0
  66. package/dist/signer/webhook/adapters/trusted-headers.js +99 -0
  67. package/dist/signer/webhook/adapters/trusted-headers.js.map +1 -0
  68. package/dist/signer/webhook.cjs +747 -0
  69. package/dist/signer/webhook.cjs.map +1 -0
  70. package/dist/signer/webhook.d.cts +26 -0
  71. package/dist/signer/webhook.d.ts +26 -0
  72. package/dist/signer/webhook.js +721 -0
  73. package/dist/signer/webhook.js.map +1 -0
  74. package/dist/tokens.d.cts +1 -1
  75. package/dist/tokens.d.ts +1 -1
  76. package/dist/{types-_R1AwEZp.d.cts → types-BORaHW_x.d.cts} +5 -5
  77. package/dist/{types-_R1AwEZp.d.ts → types-BORaHW_x.d.ts} +5 -5
  78. package/dist/verifier-B-WFDMz6.d.cts +48 -0
  79. package/dist/verifier-B-WFDMz6.d.ts +48 -0
  80. package/dist/verify.cjs.map +1 -1
  81. package/dist/verify.d.cts +1 -1
  82. package/dist/verify.d.ts +1 -1
  83. package/dist/verify.js.map +1 -1
  84. package/package.json +30 -30
  85. package/dist/gateway/client/index.cjs +0 -492
  86. package/dist/gateway/client/index.cjs.map +0 -1
  87. package/dist/gateway/client/index.d.cts +0 -63
  88. package/dist/gateway/client/index.d.ts +0 -63
  89. package/dist/gateway/client/index.js +0 -489
  90. package/dist/gateway/client/index.js.map +0 -1
  91. package/dist/gateway/index.cjs +0 -16
  92. package/dist/gateway/index.cjs.map +0 -1
  93. package/dist/gateway/index.d.cts +0 -52
  94. package/dist/gateway/index.d.ts +0 -52
  95. package/dist/gateway/index.js +0 -10
  96. package/dist/gateway/index.js.map +0 -1
  97. package/dist/gateway/server/index.cjs +0 -1248
  98. package/dist/gateway/server/index.cjs.map +0 -1
  99. package/dist/gateway/server/index.d.cts +0 -31
  100. package/dist/gateway/server/index.d.ts +0 -31
  101. package/dist/gateway/server/index.js +0 -1233
  102. package/dist/gateway/server/index.js.map +0 -1
  103. package/gateway/proto/lp_rpc.proto +0 -542
@@ -0,0 +1,721 @@
1
+ import { timingSafeEqual } from 'crypto';
2
+ import { discoveryRequest, processDiscoveryResponse, validateJwtAccessToken, customFetch, ResponseBodyError, OperationProcessingError, allowInsecureRequests } from 'oauth4webapi';
3
+ import { createServer } from 'http';
4
+
5
+ // src/signer/webhook/types.ts
6
+ function isValidUsageIdentity(identity) {
7
+ return Boolean(
8
+ identity.issuer.trim() && identity.client_id.trim() && identity.usage_subject.trim() && identity.usage_subject_type.trim()
9
+ );
10
+ }
11
+
12
+ // src/errors.ts
13
+ var PmtHouseError = class extends Error {
14
+ status;
15
+ code;
16
+ details;
17
+ constructor(message, {
18
+ status = 500,
19
+ code = "pymthouse_error",
20
+ details
21
+ } = {}) {
22
+ super(message);
23
+ this.name = "PmtHouseError";
24
+ this.status = status;
25
+ this.code = code;
26
+ this.details = details;
27
+ }
28
+ };
29
+
30
+ // src/signer/webhook/identity.ts
31
+ var DEFAULT_WEBHOOK_IDENTITY_CLAIMS = {
32
+ claimClientId: "client_id",
33
+ claimUsageSubject: "sub",
34
+ usageSubjectType: "external_user_id"
35
+ };
36
+ function readClaim(payload, key) {
37
+ const value = payload[key];
38
+ if (typeof value === "string" && value.trim()) {
39
+ return value.trim();
40
+ }
41
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
42
+ return value.toString().trim();
43
+ }
44
+ return "";
45
+ }
46
+ function identityFromWebhookClaims(claims, mapping = {}) {
47
+ const claimClientId = mapping.claimClientId ?? DEFAULT_WEBHOOK_IDENTITY_CLAIMS.claimClientId;
48
+ const claimUsageSubject = mapping.claimUsageSubject ?? DEFAULT_WEBHOOK_IDENTITY_CLAIMS.claimUsageSubject;
49
+ const defaultUsageSubjectType = mapping.usageSubjectType ?? DEFAULT_WEBHOOK_IDENTITY_CLAIMS.usageSubjectType;
50
+ let clientId = readClaim(claims, claimClientId);
51
+ if (!clientId) {
52
+ clientId = readClaim(claims, "azp");
53
+ }
54
+ const usageSubject = readClaim(claims, claimUsageSubject);
55
+ let usageSubjectType = defaultUsageSubjectType;
56
+ const claimUsageSubjectType = readClaim(claims, "usage_subject_type");
57
+ if (claimUsageSubjectType) {
58
+ usageSubjectType = claimUsageSubjectType;
59
+ }
60
+ const identity = {
61
+ issuer: readClaim(claims, "iss"),
62
+ client_id: clientId,
63
+ usage_subject: usageSubject,
64
+ usage_subject_type: usageSubjectType
65
+ };
66
+ if (!identity.issuer || !identity.client_id || !identity.usage_subject) {
67
+ throw new PmtHouseError("JWT missing required identity claims", {
68
+ status: 403,
69
+ code: "invalid_identity"
70
+ });
71
+ }
72
+ return identity;
73
+ }
74
+ function claimExpirySeconds(claims, fallbackTtlSeconds = 300) {
75
+ const exp = claims.exp;
76
+ if (typeof exp === "number" && Number.isFinite(exp)) {
77
+ return Math.trunc(exp);
78
+ }
79
+ if (typeof exp === "string" && exp.trim()) {
80
+ const parsed = Number(exp);
81
+ if (Number.isFinite(parsed)) {
82
+ return Math.trunc(parsed);
83
+ }
84
+ }
85
+ return Math.trunc(Date.now() / 1e3) + fallbackTtlSeconds;
86
+ }
87
+
88
+ // src/signer/webhook/payload.ts
89
+ function headerValueFromWebhookPayload(headers, name) {
90
+ if (!headers) {
91
+ return "";
92
+ }
93
+ const target = name.toLowerCase();
94
+ for (const [key, values] of Object.entries(headers)) {
95
+ if (key.toLowerCase() !== target) {
96
+ continue;
97
+ }
98
+ if (!Array.isArray(values)) {
99
+ continue;
100
+ }
101
+ for (const value of values) {
102
+ if (typeof value === "string" && value.trim()) {
103
+ return value.trim();
104
+ }
105
+ }
106
+ }
107
+ return "";
108
+ }
109
+ function authorizationFromWebhookPayload(payload) {
110
+ const fromHeaders = headerValueFromWebhookPayload(
111
+ payload.headers,
112
+ "Authorization"
113
+ );
114
+ if (fromHeaders) {
115
+ return fromHeaders;
116
+ }
117
+ return payload.authorization?.trim() ?? "";
118
+ }
119
+
120
+ // src/signer/webhook/authorize.ts
121
+ function authIdFromIdentity(identity) {
122
+ return `${identity.client_id}:${identity.usage_subject}`;
123
+ }
124
+ function timingSafeEqualStrings(a, b) {
125
+ const aBuffer = Buffer.from(a);
126
+ const bBuffer = Buffer.from(b);
127
+ if (aBuffer.length !== bBuffer.length) {
128
+ return false;
129
+ }
130
+ return timingSafeEqual(aBuffer, bBuffer);
131
+ }
132
+ function authenticateWebhookCaller(request, secret) {
133
+ if (!secret.trim()) {
134
+ return false;
135
+ }
136
+ const trimmed = secret.trim();
137
+ const auth = request.headers.get("authorization")?.trim() ?? "";
138
+ if (auth === `Bearer ${trimmed}`) {
139
+ return true;
140
+ }
141
+ const apiKey = request.headers.get("x-api-key")?.trim() ?? "";
142
+ if (apiKey && timingSafeEqualStrings(apiKey, trimmed)) {
143
+ return true;
144
+ }
145
+ const legacySecret = request.headers.get("x-webhook-secret")?.trim() ?? "";
146
+ if (legacySecret && timingSafeEqualStrings(legacySecret, trimmed)) {
147
+ return true;
148
+ }
149
+ return false;
150
+ }
151
+ function paymentWebhookJson(httpStatus, body) {
152
+ return new Response(JSON.stringify(body), {
153
+ status: httpStatus,
154
+ headers: { "Content-Type": "application/json" }
155
+ });
156
+ }
157
+ function rejectStatusFromError(err) {
158
+ if (err instanceof PmtHouseError) {
159
+ return {
160
+ status: err.status >= 400 && err.status < 600 ? err.status : 403,
161
+ reason: err.message
162
+ };
163
+ }
164
+ const reason = err instanceof Error ? err.message : "authorization rejected";
165
+ return { status: 403, reason };
166
+ }
167
+ async function handleRemoteSignerAuthorize(request, config) {
168
+ if (!authenticateWebhookCaller(request, config.webhookSecret)) {
169
+ return paymentWebhookJson(401, {
170
+ status: 401,
171
+ reason: "unauthorized webhook caller"
172
+ });
173
+ }
174
+ let payload;
175
+ try {
176
+ payload = await request.json();
177
+ } catch {
178
+ return paymentWebhookJson(400, {
179
+ status: 400,
180
+ reason: "invalid request json"
181
+ });
182
+ }
183
+ const authorization = authorizationFromWebhookPayload(payload) ?? "";
184
+ try {
185
+ const verified = await config.endUserAuth.verify({
186
+ authorization,
187
+ payload,
188
+ request
189
+ });
190
+ if (config.afterVerify) {
191
+ await config.afterVerify({
192
+ authorization,
193
+ payload,
194
+ request,
195
+ verified,
196
+ identity: verified.identity
197
+ });
198
+ }
199
+ return paymentWebhookJson(200, {
200
+ status: 200,
201
+ expiry: verified.expiry,
202
+ auth_id: authIdFromIdentity(verified.identity),
203
+ identity: verified.identity
204
+ });
205
+ } catch (err) {
206
+ const { status, reason } = rejectStatusFromError(err);
207
+ return paymentWebhookJson(200, {
208
+ status,
209
+ reason
210
+ });
211
+ }
212
+ }
213
+ function createRemoteSignerAuthorizeHandler(config) {
214
+ return (request) => handleRemoteSignerAuthorize(request, config);
215
+ }
216
+ async function routeRemoteSignerWebhookRequest(request, config) {
217
+ const url = new URL(request.url);
218
+ if (request.method === "POST" && url.pathname === "/authorize") {
219
+ return handleRemoteSignerAuthorize(request, config);
220
+ }
221
+ const adminRoutes = config.endUserAuth.adminRoutes ?? [];
222
+ for (const route of adminRoutes) {
223
+ if (request.method === route.method && url.pathname === route.pathname) {
224
+ return route.handler(request);
225
+ }
226
+ }
227
+ return null;
228
+ }
229
+
230
+ // src/signer/webhook/bearer.ts
231
+ function bearerTokenFromAuthorization(authorization) {
232
+ const trimmed = authorization.trim();
233
+ if (!trimmed) {
234
+ throw new Error("missing authorization");
235
+ }
236
+ const prefix = "Bearer ";
237
+ if (!trimmed.startsWith(prefix)) {
238
+ throw new Error("authorization must be Bearer token");
239
+ }
240
+ const token = trimmed.slice(prefix.length).trim();
241
+ if (!token) {
242
+ throw new Error("empty bearer token");
243
+ }
244
+ return token;
245
+ }
246
+
247
+ // src/signer/webhook/adapters/api-key/verifier.ts
248
+ function createApiKeyEndUserVerifier(config) {
249
+ const prefix = config.apiKeyPrefix ?? "sk_";
250
+ const defaultClientId = config.defaultClientId ?? "daydream-scope";
251
+ const defaultUsageSubjectType = config.defaultUsageSubjectType ?? "clerk_user_id";
252
+ const ttl = config.expiryTtlSeconds ?? 60;
253
+ return {
254
+ kind: "custom",
255
+ verify: async ({ authorization }) => {
256
+ const token = bearerTokenFromAuthorization(authorization);
257
+ if (prefix && !token.startsWith(prefix)) {
258
+ throw new PmtHouseError("invalid api key", {
259
+ status: 401,
260
+ code: "invalid_api_key"
261
+ });
262
+ }
263
+ const resolved = await config.resolveApiKey(token);
264
+ if (!resolved?.userId) {
265
+ throw new PmtHouseError("invalid api key", {
266
+ status: 401,
267
+ code: "invalid_api_key"
268
+ });
269
+ }
270
+ const identity = {
271
+ issuer: config.issuer,
272
+ client_id: resolved.clientId ?? defaultClientId,
273
+ usage_subject: resolved.userId,
274
+ usage_subject_type: resolved.usageSubjectType ?? defaultUsageSubjectType
275
+ };
276
+ return {
277
+ identity,
278
+ expiry: Math.trunc(Date.now() / 1e3) + ttl,
279
+ raw: resolved
280
+ };
281
+ }
282
+ };
283
+ }
284
+
285
+ // src/signer/webhook/adapters/composite/verifier.ts
286
+ function createFirstMatchEndUserVerifier(verifiers) {
287
+ if (verifiers.length === 0) {
288
+ throw new PmtHouseError("at least one verifier is required", {
289
+ status: 500,
290
+ code: "invalid_verifier_config"
291
+ });
292
+ }
293
+ return {
294
+ kind: "custom",
295
+ verify: async (context) => {
296
+ let lastError;
297
+ for (const verifier of verifiers) {
298
+ try {
299
+ return await verifier.verify(context);
300
+ } catch (err) {
301
+ lastError = err;
302
+ }
303
+ }
304
+ if (lastError instanceof PmtHouseError) {
305
+ throw lastError;
306
+ }
307
+ if (lastError instanceof Error) {
308
+ throw new PmtHouseError(lastError.message, {
309
+ status: 401,
310
+ code: "invalid_credentials"
311
+ });
312
+ }
313
+ throw new PmtHouseError("invalid credentials", {
314
+ status: 401,
315
+ code: "invalid_credentials"
316
+ });
317
+ },
318
+ adminRoutes: verifiers.flatMap((verifier) => verifier.adminRoutes ?? [])
319
+ };
320
+ }
321
+
322
+ // src/string-utils.ts
323
+ function stripTrailingSlashes(value) {
324
+ let end = value.length;
325
+ while (end > 0 && (value.codePointAt(end - 1) ?? 0) === 47) {
326
+ end--;
327
+ }
328
+ return value.slice(0, end);
329
+ }
330
+
331
+ // src/discovery.ts
332
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
333
+ var discoveryCache = /* @__PURE__ */ new Map();
334
+ function normalizedIssuerKey(issuerUrl) {
335
+ return stripTrailingSlashes(issuerUrl);
336
+ }
337
+ async function loadAuthorizationServer(issuerUrl, fetchImpl, options = {}) {
338
+ const key = normalizedIssuerKey(issuerUrl);
339
+ const now = Date.now();
340
+ const cached = discoveryCache.get(key);
341
+ if (!options.force && cached && now - cached.fetchedAt < CACHE_TTL_MS) {
342
+ return cached.as;
343
+ }
344
+ const issuerIdentifier = new URL(key);
345
+ const discoveryOpts = {
346
+ algorithm: "oidc",
347
+ [customFetch]: fetchImpl
348
+ };
349
+ if (options.allowInsecureHttp) {
350
+ discoveryOpts[allowInsecureRequests] = true;
351
+ }
352
+ let response;
353
+ try {
354
+ response = await discoveryRequest(issuerIdentifier, discoveryOpts);
355
+ } catch (e) {
356
+ throw mapDiscoveryNetworkError(e);
357
+ }
358
+ let as;
359
+ try {
360
+ as = await processDiscoveryResponse(issuerIdentifier, response);
361
+ } catch (e) {
362
+ throw mapOAuthDiscoveryError(e);
363
+ }
364
+ discoveryCache.set(key, { as, fetchedAt: now });
365
+ return as;
366
+ }
367
+ function mapOAuthDiscoveryError(error) {
368
+ if (error instanceof PmtHouseError) {
369
+ return error;
370
+ }
371
+ if (error instanceof Error) {
372
+ return new PmtHouseError(error.message, {
373
+ status: 500,
374
+ code: "oidc_discovery_invalid",
375
+ details: { cause: error.cause }
376
+ });
377
+ }
378
+ return new PmtHouseError("OIDC discovery failed", {
379
+ status: 500,
380
+ code: "oidc_discovery_invalid"
381
+ });
382
+ }
383
+ function mapDiscoveryNetworkError(error) {
384
+ if (error instanceof PmtHouseError) {
385
+ return error;
386
+ }
387
+ if (error instanceof Error) {
388
+ return new PmtHouseError(`Failed to load OIDC discovery: ${error.message}`, {
389
+ status: 502,
390
+ code: "oidc_discovery_failed"
391
+ });
392
+ }
393
+ return new PmtHouseError("Failed to load OIDC discovery", {
394
+ status: 502,
395
+ code: "oidc_discovery_failed"
396
+ });
397
+ }
398
+ function mapOAuthError(error) {
399
+ if (error instanceof PmtHouseError) {
400
+ return error;
401
+ }
402
+ if (error instanceof ResponseBodyError) {
403
+ const cause = error.cause;
404
+ const description = typeof error.error_description === "string" ? error.error_description : error.message;
405
+ const details = { ...cause };
406
+ if (typeof cause.error_uri === "string") {
407
+ details.error_uri = cause.error_uri;
408
+ }
409
+ return new PmtHouseError(description, {
410
+ status: error.status,
411
+ code: error.error,
412
+ details
413
+ });
414
+ }
415
+ if (error instanceof OperationProcessingError) {
416
+ return new PmtHouseError(error.message, {
417
+ status: 502,
418
+ code: error.code ?? "oauth_processing_error",
419
+ details: { cause: error.cause }
420
+ });
421
+ }
422
+ if (error instanceof Error) {
423
+ return new PmtHouseError(error.message, {
424
+ status: 500,
425
+ code: "unexpected_error"
426
+ });
427
+ }
428
+ return new PmtHouseError("Unexpected error", {
429
+ status: 500,
430
+ code: "unexpected_error"
431
+ });
432
+ }
433
+
434
+ // src/verify.ts
435
+ async function verifyJwt(token, options) {
436
+ const fetchImpl = options.fetch ?? fetch;
437
+ const as = await loadAuthorizationServer(options.issuerUrl, fetchImpl, {
438
+ allowInsecureHttp: options.allowInsecureHttp
439
+ });
440
+ const request = new Request("https://resource.invalid/", {
441
+ headers: {
442
+ Authorization: `Bearer ${token}`
443
+ }
444
+ });
445
+ const httpOpts = {
446
+ [customFetch]: fetchImpl
447
+ };
448
+ if (options.allowInsecureHttp) {
449
+ httpOpts[allowInsecureRequests] = true;
450
+ }
451
+ try {
452
+ const claims = await validateJwtAccessToken(
453
+ as,
454
+ request,
455
+ options.audience,
456
+ httpOpts
457
+ );
458
+ if (options.requiredScopes?.length) {
459
+ const scopeStr = typeof claims.scope === "string" ? claims.scope : "";
460
+ const have = new Set(scopeStr.split(/\s+/).filter(Boolean));
461
+ for (const s of options.requiredScopes) {
462
+ if (!have.has(s)) {
463
+ throw new PmtHouseError(`Missing required scope: ${s}`, {
464
+ status: 403,
465
+ code: "insufficient_scope"
466
+ });
467
+ }
468
+ }
469
+ }
470
+ return claims;
471
+ } catch (e) {
472
+ throw mapOAuthError(e);
473
+ }
474
+ }
475
+
476
+ // src/signer/webhook/adapters/oidc/verifier.ts
477
+ async function handleRemoteSignerRefreshJwks(request, config) {
478
+ if (!authenticateWebhookCaller(request, config.webhookSecret)) {
479
+ return new Response("unauthorized", { status: 401 });
480
+ }
481
+ try {
482
+ await loadAuthorizationServer(config.jwtIssuer, config.fetch ?? fetch, {
483
+ force: true,
484
+ allowInsecureHttp: config.allowInsecureHttp
485
+ });
486
+ return new Response(JSON.stringify({ status: "ok" }), {
487
+ status: 200,
488
+ headers: { "Content-Type": "application/json" }
489
+ });
490
+ } catch (err) {
491
+ const message = err instanceof Error ? err.message : "jwks refresh failed";
492
+ return new Response(message, { status: 500 });
493
+ }
494
+ }
495
+ function createOidcEndUserVerifier(config) {
496
+ const claimMapping = {
497
+ ...DEFAULT_WEBHOOK_IDENTITY_CLAIMS,
498
+ ...config.claimMapping
499
+ };
500
+ return {
501
+ kind: "oidc",
502
+ verify: async ({ authorization }) => {
503
+ const token = bearerTokenFromAuthorization(authorization);
504
+ const audience = config.jwtAudience.trim();
505
+ if (!audience) {
506
+ throw new Error("jwt audience is required for webhook verification");
507
+ }
508
+ const claims = await verifyJwt(token, {
509
+ issuerUrl: config.jwtIssuer,
510
+ audience,
511
+ fetch: config.fetch,
512
+ allowInsecureHttp: config.allowInsecureHttp,
513
+ requiredScopes: config.requiredScopes
514
+ });
515
+ const claimsRecord = claims;
516
+ const identity = identityFromWebhookClaims(claimsRecord, claimMapping);
517
+ return {
518
+ identity,
519
+ expiry: claimExpirySeconds(claimsRecord),
520
+ raw: claimsRecord
521
+ };
522
+ },
523
+ adminRoutes: [
524
+ {
525
+ method: "POST",
526
+ pathname: "/admin/refresh-jwks",
527
+ handler: (request) => handleRemoteSignerRefreshJwks(request, config)
528
+ }
529
+ ]
530
+ };
531
+ }
532
+
533
+ // src/signer/webhook/adapters/oauth1/verifier.ts
534
+ function createOAuth1EndUserVerifier(config) {
535
+ return {
536
+ kind: "oauth1",
537
+ verify: async () => {
538
+ if (!config.consumerKey.trim() || !config.consumerSecret.trim()) {
539
+ throw new Error("OAuth 1.0 consumer credentials are required");
540
+ }
541
+ throw new Error("OAuth 1.0 webhook verification is not implemented yet");
542
+ }
543
+ };
544
+ }
545
+
546
+ // src/signer/webhook/adapters/trusted-headers/verifier.ts
547
+ var DEFAULT_DMZ_TRUSTED_HEADERS = {
548
+ issuer: "X-Livepeer-Usage-Issuer",
549
+ clientId: "X-Livepeer-Client-ID",
550
+ usageSubject: "X-Livepeer-Usage-Subject",
551
+ usageSubjectType: "X-Livepeer-Usage-Subject-Type"
552
+ };
553
+ function normalizeIssuer(value) {
554
+ let end = value.length;
555
+ while (end > 0 && value[end - 1] === "/") {
556
+ end -= 1;
557
+ }
558
+ return value.slice(0, end);
559
+ }
560
+ function identityFromTrustedHeaders(headers, config) {
561
+ const names = {
562
+ ...DEFAULT_DMZ_TRUSTED_HEADERS,
563
+ ...config.headerNames
564
+ };
565
+ const issuer = headerValueFromWebhookPayload(headers, names.issuer);
566
+ const clientId = headerValueFromWebhookPayload(headers, names.clientId);
567
+ const usageSubject = headerValueFromWebhookPayload(headers, names.usageSubject);
568
+ const usageSubjectType = headerValueFromWebhookPayload(headers, names.usageSubjectType) || "external_user_id";
569
+ if (!issuer || !clientId || !usageSubject) {
570
+ throw new PmtHouseError("missing trusted usage identity headers", {
571
+ status: 403,
572
+ code: "invalid_identity"
573
+ });
574
+ }
575
+ if (normalizeIssuer(issuer) !== normalizeIssuer(config.expectedIssuer.trim())) {
576
+ throw new PmtHouseError("trusted usage issuer mismatch", {
577
+ status: 403,
578
+ code: "invalid_identity"
579
+ });
580
+ }
581
+ return {
582
+ issuer,
583
+ client_id: clientId,
584
+ usage_subject: usageSubject,
585
+ usage_subject_type: usageSubjectType
586
+ };
587
+ }
588
+ function createTrustedHeadersEndUserVerifier(config) {
589
+ const expiryTtlSeconds = config.expiryTtlSeconds ?? 300;
590
+ return {
591
+ kind: "trusted_headers",
592
+ verify: async ({ payload }) => {
593
+ const identity = identityFromTrustedHeaders(payload.headers, config);
594
+ return {
595
+ identity,
596
+ expiry: Math.trunc(Date.now() / 1e3) + expiryTtlSeconds
597
+ };
598
+ }
599
+ };
600
+ }
601
+
602
+ // src/signer/webhook/adapters/oidc/config.ts
603
+ function envTrim(env, key) {
604
+ const value = env[key]?.trim();
605
+ return value || void 0;
606
+ }
607
+ function createOidcRemoteSignerWebhookConfig(input) {
608
+ const { afterVerify, ...oidcConfig } = input;
609
+ return {
610
+ webhookSecret: oidcConfig.webhookSecret,
611
+ endUserAuth: createOidcEndUserVerifier(oidcConfig),
612
+ afterVerify
613
+ };
614
+ }
615
+ function createSignerDmzRemoteSignerWebhookConfig(input) {
616
+ const {
617
+ afterVerify,
618
+ dmzTrustedHeaders = true,
619
+ trustedHeaders,
620
+ ...oidcConfig
621
+ } = input;
622
+ const oidcVerifier = createOidcEndUserVerifier(oidcConfig);
623
+ const endUserAuth = dmzTrustedHeaders === false ? oidcVerifier : createFirstMatchEndUserVerifier([
624
+ createTrustedHeadersEndUserVerifier({
625
+ expectedIssuer: oidcConfig.jwtIssuer,
626
+ ...trustedHeaders
627
+ }),
628
+ oidcVerifier
629
+ ]);
630
+ return {
631
+ webhookSecret: oidcConfig.webhookSecret,
632
+ endUserAuth,
633
+ afterVerify
634
+ };
635
+ }
636
+ function readOidcRemoteSignerWebhookConfigFromEnv(env = process.env) {
637
+ const webhookSecret = envTrim(env, "WEBHOOK_SECRET");
638
+ const jwtIssuer = envTrim(env, "JWT_ISSUER");
639
+ const jwtAudience = envTrim(env, "JWT_AUDIENCE") ?? jwtIssuer;
640
+ if (!webhookSecret) {
641
+ throw new Error("WEBHOOK_SECRET is required");
642
+ }
643
+ if (!jwtIssuer) {
644
+ throw new Error("JWT_ISSUER is required");
645
+ }
646
+ if (!jwtAudience) {
647
+ throw new Error("JWT_AUDIENCE is required");
648
+ }
649
+ return createSignerDmzRemoteSignerWebhookConfig({
650
+ webhookSecret,
651
+ jwtIssuer,
652
+ jwtAudience,
653
+ claimMapping: {
654
+ claimClientId: envTrim(env, "CLAIM_CLIENT_ID") ?? "client_id",
655
+ claimUsageSubject: envTrim(env, "CLAIM_USAGE_SUBJECT") ?? "sub",
656
+ usageSubjectType: envTrim(env, "USAGE_SUBJECT_TYPE") ?? "external_user_id"
657
+ },
658
+ allowInsecureHttp: envTrim(env, "ALLOW_INSECURE_HTTP") === "1"
659
+ });
660
+ }
661
+ var readRemoteSignerWebhookConfigFromEnv = readOidcRemoteSignerWebhookConfigFromEnv;
662
+ function startRemoteSignerWebhookServer(options = {}) {
663
+ const config = options.config ?? readOidcRemoteSignerWebhookConfigFromEnv();
664
+ const port = options.port ?? Number(process.env.PORT ?? 8090);
665
+ const addr = options.addr ?? process.env.ADDR ?? "0.0.0.0";
666
+ const server = createServer(async (req, res) => {
667
+ try {
668
+ const host = req.headers.host ?? "localhost";
669
+ const url = `http://${host}${req.url ?? "/"}`;
670
+ const headers = new Headers();
671
+ for (const [name, value] of Object.entries(req.headers)) {
672
+ if (typeof value === "string") {
673
+ headers.set(name, value);
674
+ } else if (Array.isArray(value)) {
675
+ for (const item of value) {
676
+ headers.append(name, item);
677
+ }
678
+ }
679
+ }
680
+ const chunks = [];
681
+ for await (const chunk of req) {
682
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
683
+ }
684
+ const body = Buffer.concat(chunks);
685
+ const request = new Request(url, {
686
+ method: req.method,
687
+ headers,
688
+ body: req.method === "GET" || req.method === "HEAD" ? void 0 : body
689
+ });
690
+ const response = await routeRemoteSignerWebhookRequest(request, config) ?? new Response("not found", { status: 404 });
691
+ res.statusCode = response.status;
692
+ response.headers.forEach((value, name) => {
693
+ res.setHeader(name, value);
694
+ });
695
+ const responseBody = Buffer.from(await response.arrayBuffer());
696
+ res.end(responseBody);
697
+ } catch (err) {
698
+ const message = err instanceof Error ? err.message : "internal error";
699
+ res.statusCode = 500;
700
+ res.end(message);
701
+ }
702
+ });
703
+ server.listen(port, addr, () => {
704
+ console.log(
705
+ `[builder-sdk] remote signer identity webhook listening on http://${addr}:${port}`
706
+ );
707
+ console.log(
708
+ `[builder-sdk] end-user auth strategy=${config.endUserAuth.kind}`
709
+ );
710
+ });
711
+ return server;
712
+ }
713
+
714
+ // src/signer/webhook/index.ts
715
+ function createRemoteSignerRefreshJwksHandler(config) {
716
+ return (request) => handleRemoteSignerRefreshJwks(request, config);
717
+ }
718
+
719
+ export { DEFAULT_DMZ_TRUSTED_HEADERS, DEFAULT_WEBHOOK_IDENTITY_CLAIMS, authenticateWebhookCaller, authorizationFromWebhookPayload, bearerTokenFromAuthorization, claimExpirySeconds, createApiKeyEndUserVerifier, createFirstMatchEndUserVerifier, createOAuth1EndUserVerifier, createOidcEndUserVerifier, createOidcRemoteSignerWebhookConfig, createRemoteSignerAuthorizeHandler, createRemoteSignerRefreshJwksHandler, createSignerDmzRemoteSignerWebhookConfig, createTrustedHeadersEndUserVerifier, handleRemoteSignerAuthorize, handleRemoteSignerRefreshJwks, headerValueFromWebhookPayload, identityFromTrustedHeaders, identityFromWebhookClaims, isValidUsageIdentity, readOidcRemoteSignerWebhookConfigFromEnv, readRemoteSignerWebhookConfigFromEnv, routeRemoteSignerWebhookRequest, startRemoteSignerWebhookServer };
720
+ //# sourceMappingURL=webhook.js.map
721
+ //# sourceMappingURL=webhook.js.map