@pymthouse/builder-sdk 0.3.1 → 0.4.1-rc.2

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