@query-farm/vgi-rpc 0.3.3 → 0.4.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.
Files changed (55) hide show
  1. package/dist/auth.d.ts +13 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/client/connect.d.ts.map +1 -1
  4. package/dist/client/index.d.ts +2 -0
  5. package/dist/client/index.d.ts.map +1 -1
  6. package/dist/client/introspect.d.ts +1 -0
  7. package/dist/client/introspect.d.ts.map +1 -1
  8. package/dist/client/oauth.d.ts +26 -0
  9. package/dist/client/oauth.d.ts.map +1 -0
  10. package/dist/client/stream.d.ts +2 -0
  11. package/dist/client/stream.d.ts.map +1 -1
  12. package/dist/client/types.d.ts +2 -0
  13. package/dist/client/types.d.ts.map +1 -1
  14. package/dist/dispatch/stream.d.ts.map +1 -1
  15. package/dist/http/auth.d.ts +21 -0
  16. package/dist/http/auth.d.ts.map +1 -0
  17. package/dist/http/common.d.ts.map +1 -1
  18. package/dist/http/dispatch.d.ts +2 -0
  19. package/dist/http/dispatch.d.ts.map +1 -1
  20. package/dist/http/handler.d.ts.map +1 -1
  21. package/dist/http/index.d.ts +4 -0
  22. package/dist/http/index.d.ts.map +1 -1
  23. package/dist/http/jwt.d.ts +21 -0
  24. package/dist/http/jwt.d.ts.map +1 -0
  25. package/dist/http/types.d.ts +5 -0
  26. package/dist/http/types.d.ts.map +1 -1
  27. package/dist/index.d.ts +3 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1479 -71
  30. package/dist/index.js.map +20 -15
  31. package/dist/types.d.ts +8 -2
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/util/conform.d.ts +5 -3
  34. package/dist/util/conform.d.ts.map +1 -1
  35. package/dist/wire/response.d.ts.map +1 -1
  36. package/package.json +3 -2
  37. package/src/auth.ts +31 -0
  38. package/src/client/connect.ts +15 -1
  39. package/src/client/index.ts +2 -0
  40. package/src/client/introspect.ts +14 -2
  41. package/src/client/oauth.ts +74 -0
  42. package/src/client/stream.ts +12 -0
  43. package/src/client/types.ts +2 -0
  44. package/src/dispatch/stream.ts +11 -5
  45. package/src/http/auth.ts +47 -0
  46. package/src/http/common.ts +1 -6
  47. package/src/http/dispatch.ts +6 -4
  48. package/src/http/handler.ts +41 -1
  49. package/src/http/index.ts +4 -0
  50. package/src/http/jwt.ts +66 -0
  51. package/src/http/types.ts +6 -0
  52. package/src/index.ts +7 -0
  53. package/src/types.ts +17 -3
  54. package/src/util/conform.ts +68 -5
  55. package/src/wire/response.ts +28 -14
package/dist/index.js CHANGED
@@ -50,6 +50,48 @@ var init_zstd = __esm(() => {
50
50
  isBun = typeof globalThis.Bun !== "undefined";
51
51
  });
52
52
 
53
+ // src/errors.ts
54
+ class RpcError extends Error {
55
+ errorType;
56
+ errorMessage;
57
+ remoteTraceback;
58
+ constructor(errorType, errorMessage, remoteTraceback) {
59
+ super(`${errorType}: ${errorMessage}`);
60
+ this.errorType = errorType;
61
+ this.errorMessage = errorMessage;
62
+ this.remoteTraceback = remoteTraceback;
63
+ this.name = "RpcError";
64
+ }
65
+ }
66
+
67
+ class VersionError extends Error {
68
+ constructor(message) {
69
+ super(message);
70
+ this.name = "VersionError";
71
+ }
72
+ }
73
+
74
+ // src/auth.ts
75
+ class AuthContext {
76
+ domain;
77
+ authenticated;
78
+ principal;
79
+ claims;
80
+ constructor(domain, authenticated, principal, claims = {}) {
81
+ this.domain = domain;
82
+ this.authenticated = authenticated;
83
+ this.principal = principal;
84
+ this.claims = claims;
85
+ }
86
+ static anonymous() {
87
+ return new AuthContext("", false, null);
88
+ }
89
+ requireAuthenticated() {
90
+ if (!this.authenticated) {
91
+ throw new RpcError("AuthenticationError", "Authentication required", "");
92
+ }
93
+ }
94
+ }
53
95
  // src/constants.ts
54
96
  var RPC_METHOD_KEY = "vgi_rpc.method";
55
97
  var LOG_LEVEL_KEY = "vgi_rpc.log_level";
@@ -66,17 +108,55 @@ var DESCRIBE_METHOD_NAME = "__describe__";
66
108
  var STATE_KEY = "vgi_rpc.stream_state#b64";
67
109
 
68
110
  // src/http/common.ts
69
- import {
70
- RecordBatchReader,
71
- RecordBatchStreamWriter
72
- } from "@query-farm/apache-arrow";
111
+ import { RecordBatchReader, RecordBatchStreamWriter } from "@query-farm/apache-arrow";
73
112
 
74
113
  // src/util/conform.ts
75
- import { RecordBatch, Struct, makeData } from "@query-farm/apache-arrow";
114
+ import {
115
+ makeData,
116
+ RecordBatch,
117
+ Struct,
118
+ Type,
119
+ vectorFromArray
120
+ } from "@query-farm/apache-arrow";
121
+ function needsValueCast(src, dst) {
122
+ if (src.typeId === dst.typeId)
123
+ return false;
124
+ if (src.constructor === dst.constructor)
125
+ return false;
126
+ return true;
127
+ }
128
+ function isNumeric(t) {
129
+ return t.typeId === Type.Int || t.typeId === Type.Float;
130
+ }
76
131
  function conformBatchToSchema(batch, schema) {
77
132
  if (batch.numRows === 0)
78
133
  return batch;
79
- const children = schema.fields.map((f, i) => batch.data.children[i].clone(f.type));
134
+ if (batch.schema.fields.length !== schema.fields.length) {
135
+ throw new TypeError(`Field count mismatch: expected ${schema.fields.length}, got ${batch.schema.fields.length}`);
136
+ }
137
+ for (let i = 0;i < schema.fields.length; i++) {
138
+ if (batch.schema.fields[i].name !== schema.fields[i].name) {
139
+ throw new TypeError(`Field name mismatch at index ${i}: expected '${schema.fields[i].name}', got '${batch.schema.fields[i].name}'`);
140
+ }
141
+ }
142
+ const children = schema.fields.map((f, i) => {
143
+ const srcChild = batch.data.children[i];
144
+ const srcType = srcChild.type;
145
+ const dstType = f.type;
146
+ if (!needsValueCast(srcType, dstType)) {
147
+ return srcChild.clone(dstType);
148
+ }
149
+ if (isNumeric(srcType) && isNumeric(dstType)) {
150
+ const col = batch.getChildAt(i);
151
+ const values = [];
152
+ for (let r = 0;r < batch.numRows; r++) {
153
+ const v = col.get(r);
154
+ values.push(typeof v === "bigint" ? Number(v) : v);
155
+ }
156
+ return vectorFromArray(values, dstType).data[0];
157
+ }
158
+ return srcChild.clone(dstType);
159
+ });
80
160
  const structType = new Struct(schema.fields);
81
161
  const data = makeData({
82
162
  type: structType,
@@ -142,30 +222,9 @@ import {
142
222
  RecordBatchReader as RecordBatchReader3,
143
223
  Struct as Struct2,
144
224
  Utf8,
145
- vectorFromArray
225
+ vectorFromArray as vectorFromArray2
146
226
  } from "@query-farm/apache-arrow";
147
227
 
148
- // src/errors.ts
149
- class RpcError extends Error {
150
- errorType;
151
- errorMessage;
152
- remoteTraceback;
153
- constructor(errorType, errorMessage, remoteTraceback) {
154
- super(`${errorType}: ${errorMessage}`);
155
- this.errorType = errorType;
156
- this.errorMessage = errorMessage;
157
- this.remoteTraceback = remoteTraceback;
158
- this.name = "RpcError";
159
- }
160
- }
161
-
162
- class VersionError extends Error {
163
- constructor(message) {
164
- super(message);
165
- this.name = "VersionError";
166
- }
167
- }
168
-
169
228
  // src/wire/reader.ts
170
229
  import { RecordBatchReader as RecordBatchReader2 } from "@query-farm/apache-arrow";
171
230
 
@@ -297,7 +356,7 @@ function buildRequestIpc(schema, params, method) {
297
356
  }
298
357
  const children = schema.fields.map((f) => {
299
358
  const val = coerceForArrow(f.type, params[f.name]);
300
- return vectorFromArray([val], f.type).data[0];
359
+ return vectorFromArray2([val], f.type).data[0];
301
360
  });
302
361
  const structType = new Struct2(schema.fields);
303
362
  const data = makeData2({
@@ -451,18 +510,25 @@ async function httpIntrospect(baseUrl, options) {
451
510
  const prefix = options?.prefix ?? "/vgi";
452
511
  const emptySchema = new ArrowSchema([]);
453
512
  const body = buildRequestIpc(emptySchema, {}, DESCRIBE_METHOD_NAME);
513
+ const headers = { "Content-Type": ARROW_CONTENT_TYPE };
514
+ if (options?.authorization) {
515
+ headers.Authorization = options.authorization;
516
+ }
454
517
  const response = await fetch(`${baseUrl}${prefix}/${DESCRIBE_METHOD_NAME}`, {
455
518
  method: "POST",
456
- headers: { "Content-Type": ARROW_CONTENT_TYPE },
519
+ headers,
457
520
  body
458
521
  });
522
+ if (response.status === 401) {
523
+ throw new RpcError("AuthenticationError", "Authentication required", "");
524
+ }
459
525
  const responseBody = new Uint8Array(await response.arrayBuffer());
460
526
  const { batches } = await readResponseBatches(responseBody);
461
527
  return parseDescribeResponse(batches);
462
528
  }
463
529
 
464
530
  // src/client/stream.ts
465
- import { Field, makeData as makeData3, RecordBatch as RecordBatch3, Schema, Struct as Struct3, vectorFromArray as vectorFromArray2 } from "@query-farm/apache-arrow";
531
+ import { Field, makeData as makeData3, RecordBatch as RecordBatch3, Schema, Struct as Struct3, vectorFromArray as vectorFromArray3 } from "@query-farm/apache-arrow";
466
532
  class HttpStreamSession {
467
533
  _baseUrl;
468
534
  _prefix;
@@ -477,6 +543,7 @@ class HttpStreamSession {
477
543
  _compressionLevel;
478
544
  _compressFn;
479
545
  _decompressFn;
546
+ _authorization;
480
547
  constructor(opts) {
481
548
  this._baseUrl = opts.baseUrl;
482
549
  this._prefix = opts.prefix;
@@ -491,6 +558,7 @@ class HttpStreamSession {
491
558
  this._compressionLevel = opts.compressionLevel;
492
559
  this._compressFn = opts.compressFn;
493
560
  this._decompressFn = opts.decompressFn;
561
+ this._authorization = opts.authorization;
494
562
  }
495
563
  get header() {
496
564
  return this._header;
@@ -503,6 +571,9 @@ class HttpStreamSession {
503
571
  headers["Content-Encoding"] = "zstd";
504
572
  headers["Accept-Encoding"] = "zstd";
505
573
  }
574
+ if (this._authorization) {
575
+ headers.Authorization = this._authorization;
576
+ }
506
577
  return headers;
507
578
  }
508
579
  _prepareBody(content) {
@@ -546,7 +617,7 @@ class HttpStreamSession {
546
617
  const inputSchema = new Schema(fields);
547
618
  const children = inputSchema.fields.map((f) => {
548
619
  const values = input.map((row) => row[f.name]);
549
- return vectorFromArray2(values, f.type).data[0];
620
+ return vectorFromArray3(values, f.type).data[0];
550
621
  });
551
622
  const structType = new Struct3(inputSchema.fields);
552
623
  const data = makeData3({
@@ -567,6 +638,9 @@ class HttpStreamSession {
567
638
  headers: this._buildHeaders(),
568
639
  body: this._prepareBody(body)
569
640
  });
641
+ if (resp.status === 401) {
642
+ throw new RpcError("AuthenticationError", "Authentication required", "");
643
+ }
570
644
  const responseBody = await this._readResponse(resp);
571
645
  const { batches: responseBatches } = await readResponseBatches(responseBody);
572
646
  let resultRows = [];
@@ -652,6 +726,9 @@ class HttpStreamSession {
652
726
  headers: this._buildHeaders(),
653
727
  body: this._prepareBody(body)
654
728
  });
729
+ if (resp.status === 401) {
730
+ throw new RpcError("AuthenticationError", "Authentication required", "");
731
+ }
655
732
  return this._readResponse(resp);
656
733
  }
657
734
  close() {}
@@ -662,6 +739,7 @@ function httpConnect(baseUrl, options) {
662
739
  const prefix = (options?.prefix ?? "/vgi").replace(/\/+$/, "");
663
740
  const onLog = options?.onLog;
664
741
  const compressionLevel = options?.compressionLevel;
742
+ const authorization = options?.authorization;
665
743
  let methodCache = null;
666
744
  let compressFn;
667
745
  let decompressFn;
@@ -684,6 +762,9 @@ function httpConnect(baseUrl, options) {
684
762
  headers["Content-Encoding"] = "zstd";
685
763
  headers["Accept-Encoding"] = "zstd";
686
764
  }
765
+ if (authorization) {
766
+ headers.Authorization = authorization;
767
+ }
687
768
  return headers;
688
769
  }
689
770
  function prepareBody(content) {
@@ -692,6 +773,11 @@ function httpConnect(baseUrl, options) {
692
773
  }
693
774
  return content;
694
775
  }
776
+ function checkAuth(resp) {
777
+ if (resp.status === 401) {
778
+ throw new RpcError("AuthenticationError", "Authentication required", "");
779
+ }
780
+ }
695
781
  async function readResponse(resp) {
696
782
  let body = new Uint8Array(await resp.arrayBuffer());
697
783
  if (resp.headers.get("Content-Encoding") === "zstd" && decompressFn) {
@@ -702,7 +788,7 @@ function httpConnect(baseUrl, options) {
702
788
  async function ensureMethodCache() {
703
789
  if (methodCache)
704
790
  return methodCache;
705
- const desc = await httpIntrospect(baseUrl, { prefix });
791
+ const desc = await httpIntrospect(baseUrl, { prefix, authorization });
706
792
  methodCache = new Map(desc.methods.map((m) => [m.name, m]));
707
793
  return methodCache;
708
794
  }
@@ -721,6 +807,7 @@ function httpConnect(baseUrl, options) {
721
807
  headers: buildHeaders(),
722
808
  body: prepareBody(body)
723
809
  });
810
+ checkAuth(resp);
724
811
  const responseBody = await readResponse(resp);
725
812
  const { batches } = await readResponseBatches(responseBody);
726
813
  let resultBatch = null;
@@ -756,6 +843,7 @@ function httpConnect(baseUrl, options) {
756
843
  headers: buildHeaders(),
757
844
  body: prepareBody(body)
758
845
  });
846
+ checkAuth(resp);
759
847
  const responseBody = await readResponse(resp);
760
848
  let header = null;
761
849
  let stateToken = null;
@@ -861,7 +949,8 @@ function httpConnect(baseUrl, options) {
861
949
  header,
862
950
  compressionLevel,
863
951
  compressFn,
864
- decompressFn
952
+ decompressFn,
953
+ authorization
865
954
  });
866
955
  },
867
956
  async describe() {
@@ -870,6 +959,53 @@ function httpConnect(baseUrl, options) {
870
959
  close() {}
871
960
  };
872
961
  }
962
+ // src/client/oauth.ts
963
+ function parseMetadataJson(json) {
964
+ const result = {
965
+ resource: json.resource,
966
+ authorizationServers: json.authorization_servers
967
+ };
968
+ if (json.scopes_supported)
969
+ result.scopesSupported = json.scopes_supported;
970
+ if (json.bearer_methods_supported)
971
+ result.bearerMethodsSupported = json.bearer_methods_supported;
972
+ if (json.resource_name)
973
+ result.resourceName = json.resource_name;
974
+ if (json.resource_documentation)
975
+ result.resourceDocumentation = json.resource_documentation;
976
+ if (json.resource_policy_uri)
977
+ result.resourcePolicyUri = json.resource_policy_uri;
978
+ if (json.resource_tos_uri)
979
+ result.resourceTosUri = json.resource_tos_uri;
980
+ return result;
981
+ }
982
+ async function httpOAuthMetadata(baseUrl, prefix) {
983
+ const effectivePrefix = (prefix ?? "/vgi").replace(/\/+$/, "");
984
+ const metadataUrl = `${baseUrl.replace(/\/+$/, "")}/.well-known/oauth-protected-resource${effectivePrefix}`;
985
+ try {
986
+ return await fetchOAuthMetadata(metadataUrl);
987
+ } catch {
988
+ return null;
989
+ }
990
+ }
991
+ async function fetchOAuthMetadata(metadataUrl) {
992
+ const response = await fetch(metadataUrl);
993
+ if (!response.ok) {
994
+ throw new Error(`Failed to fetch OAuth metadata from ${metadataUrl}: ${response.status}`);
995
+ }
996
+ const json = await response.json();
997
+ return parseMetadataJson(json);
998
+ }
999
+ function parseResourceMetadataUrl(wwwAuthenticate) {
1000
+ const bearerMatch = wwwAuthenticate.match(/^Bearer\s+(.*)/i);
1001
+ if (!bearerMatch)
1002
+ return null;
1003
+ const params = bearerMatch[1];
1004
+ const metadataMatch = params.match(/resource_metadata="([^"]+)"/);
1005
+ if (!metadataMatch)
1006
+ return null;
1007
+ return metadataMatch[1];
1008
+ }
873
1009
  // src/client/pipe.ts
874
1010
  import {
875
1011
  Field as Field2,
@@ -878,7 +1014,7 @@ import {
878
1014
  RecordBatchStreamWriter as RecordBatchStreamWriter2,
879
1015
  Schema as Schema2,
880
1016
  Struct as Struct4,
881
- vectorFromArray as vectorFromArray3
1017
+ vectorFromArray as vectorFromArray4
882
1018
  } from "@query-farm/apache-arrow";
883
1019
  class PipeIncrementalWriter {
884
1020
  writer;
@@ -1001,7 +1137,7 @@ class PipeStreamSession {
1001
1137
  }
1002
1138
  const children = inputSchema.fields.map((f) => {
1003
1139
  const values = input.map((row) => row[f.name]);
1004
- return vectorFromArray3(values, f.type).data[0];
1140
+ return vectorFromArray4(values, f.type).data[0];
1005
1141
  });
1006
1142
  const structType = new Struct4(inputSchema.fields);
1007
1143
  const data = makeData4({
@@ -1303,6 +1439,35 @@ function subprocessConnect(cmd, options) {
1303
1439
  };
1304
1440
  return client;
1305
1441
  }
1442
+ // src/http/auth.ts
1443
+ function oauthResourceMetadataToJson(metadata) {
1444
+ const json = {
1445
+ resource: metadata.resource,
1446
+ authorization_servers: metadata.authorizationServers
1447
+ };
1448
+ if (metadata.scopesSupported)
1449
+ json.scopes_supported = metadata.scopesSupported;
1450
+ if (metadata.bearerMethodsSupported)
1451
+ json.bearer_methods_supported = metadata.bearerMethodsSupported;
1452
+ if (metadata.resourceName)
1453
+ json.resource_name = metadata.resourceName;
1454
+ if (metadata.resourceDocumentation)
1455
+ json.resource_documentation = metadata.resourceDocumentation;
1456
+ if (metadata.resourcePolicyUri)
1457
+ json.resource_policy_uri = metadata.resourcePolicyUri;
1458
+ if (metadata.resourceTosUri)
1459
+ json.resource_tos_uri = metadata.resourceTosUri;
1460
+ return json;
1461
+ }
1462
+ function wellKnownPath(prefix) {
1463
+ return `/.well-known/oauth-protected-resource${prefix}`;
1464
+ }
1465
+ function buildWwwAuthenticateHeader(metadataUrl) {
1466
+ if (metadataUrl) {
1467
+ return `Bearer resource_metadata="${metadataUrl}"`;
1468
+ }
1469
+ return "Bearer";
1470
+ }
1306
1471
  // src/http/handler.ts
1307
1472
  import { randomBytes } from "node:crypto";
1308
1473
  import { Schema as Schema5 } from "@query-farm/apache-arrow";
@@ -1317,7 +1482,7 @@ import {
1317
1482
  makeData as makeData5,
1318
1483
  RecordBatch as RecordBatch5,
1319
1484
  Struct as Struct5,
1320
- vectorFromArray as vectorFromArray4
1485
+ vectorFromArray as vectorFromArray5
1321
1486
  } from "@query-farm/apache-arrow";
1322
1487
  function coerceInt64(schema, values) {
1323
1488
  const result = { ...values };
@@ -1356,7 +1521,7 @@ function buildResultBatch(schema, values, serverId, requestId) {
1356
1521
  if (val instanceof Data) {
1357
1522
  return val;
1358
1523
  }
1359
- const arr = vectorFromArray4([val], f.type);
1524
+ const arr = vectorFromArray5([val], f.type);
1360
1525
  return arr.data[0];
1361
1526
  });
1362
1527
  const structType = new Struct5(schema.fields);
@@ -1399,20 +1564,28 @@ function buildLogBatch(schema, level, message, extra, serverId, requestId) {
1399
1564
  }
1400
1565
  return buildEmptyBatch(schema, metadata);
1401
1566
  }
1402
- function buildEmptyBatch(schema, metadata) {
1403
- const children = schema.fields.map((f) => {
1404
- return makeData5({ type: f.type, length: 0, nullCount: 0 });
1405
- });
1406
- if (schema.fields.length === 0) {
1407
- const structType2 = new Struct5(schema.fields);
1408
- const data2 = makeData5({
1409
- type: structType2,
1410
- length: 0,
1411
- children: [],
1412
- nullCount: 0
1413
- });
1414
- return new RecordBatch5(schema, data2, metadata);
1567
+ function makeEmptyData(type) {
1568
+ if (DataType2.isStruct(type)) {
1569
+ const children = type.children.map((f) => makeEmptyData(f.type));
1570
+ return makeData5({ type, length: 0, children, nullCount: 0 });
1571
+ }
1572
+ if (DataType2.isList(type)) {
1573
+ const childData = makeEmptyData(type.children[0].type);
1574
+ return makeData5({ type, length: 0, children: [childData], nullCount: 0, valueOffsets: new Int32Array([0]) });
1575
+ }
1576
+ if (DataType2.isFixedSizeList(type)) {
1577
+ const childData = makeEmptyData(type.children[0].type);
1578
+ return makeData5({ type, length: 0, child: childData, nullCount: 0 });
1415
1579
  }
1580
+ if (DataType2.isMap(type)) {
1581
+ const entryType = type.children[0]?.type;
1582
+ const entryData = entryType ? makeEmptyData(entryType) : makeData5({ type: new Struct5([]), length: 0, children: [], nullCount: 0 });
1583
+ return makeData5({ type, length: 0, children: [entryData], nullCount: 0, valueOffsets: new Int32Array([0]) });
1584
+ }
1585
+ return makeData5({ type, length: 0, nullCount: 0 });
1586
+ }
1587
+ function buildEmptyBatch(schema, metadata) {
1588
+ const children = schema.fields.map((f) => makeEmptyData(f.type));
1416
1589
  const structType = new Struct5(schema.fields);
1417
1590
  const data = makeData5({
1418
1591
  type: structType,
@@ -1438,11 +1611,13 @@ class OutputCollector {
1438
1611
  _outputSchema;
1439
1612
  _serverId;
1440
1613
  _requestId;
1441
- constructor(outputSchema, producerMode = true, serverId = "", requestId = null) {
1614
+ auth;
1615
+ constructor(outputSchema, producerMode = true, serverId = "", requestId = null, authContext) {
1442
1616
  this._outputSchema = outputSchema;
1443
1617
  this._producerMode = producerMode;
1444
1618
  this._serverId = serverId;
1445
1619
  this._requestId = requestId;
1620
+ this.auth = authContext ?? AuthContext.anonymous();
1446
1621
  }
1447
1622
  get outputSchema() {
1448
1623
  return this._outputSchema;
@@ -1502,7 +1677,7 @@ import {
1502
1677
  Schema as Schema3,
1503
1678
  Struct as Struct6,
1504
1679
  Utf8 as Utf82,
1505
- vectorFromArray as vectorFromArray5
1680
+ vectorFromArray as vectorFromArray6
1506
1681
  } from "@query-farm/apache-arrow";
1507
1682
 
1508
1683
  // src/util/schema.ts
@@ -1566,16 +1741,16 @@ function buildDescribeBatch(protocolName, methods, serverId) {
1566
1741
  hasHeaders.push(!!method.headerSchema);
1567
1742
  headerSchemas.push(method.headerSchema ? serializeSchema(method.headerSchema) : null);
1568
1743
  }
1569
- const nameArr = vectorFromArray5(names, new Utf82);
1570
- const methodTypeArr = vectorFromArray5(methodTypes, new Utf82);
1571
- const docArr = vectorFromArray5(docs, new Utf82);
1572
- const hasReturnArr = vectorFromArray5(hasReturns, new Bool2);
1573
- const paramsSchemaArr = vectorFromArray5(paramsSchemas, new Binary2);
1574
- const resultSchemaArr = vectorFromArray5(resultSchemas, new Binary2);
1575
- const paramTypesArr = vectorFromArray5(paramTypesJsons, new Utf82);
1576
- const paramDefaultsArr = vectorFromArray5(paramDefaultsJsons, new Utf82);
1577
- const hasHeaderArr = vectorFromArray5(hasHeaders, new Bool2);
1578
- const headerSchemaArr = vectorFromArray5(headerSchemas, new Binary2);
1744
+ const nameArr = vectorFromArray6(names, new Utf82);
1745
+ const methodTypeArr = vectorFromArray6(methodTypes, new Utf82);
1746
+ const docArr = vectorFromArray6(docs, new Utf82);
1747
+ const hasReturnArr = vectorFromArray6(hasReturns, new Bool2);
1748
+ const paramsSchemaArr = vectorFromArray6(paramsSchemas, new Binary2);
1749
+ const resultSchemaArr = vectorFromArray6(resultSchemas, new Binary2);
1750
+ const paramTypesArr = vectorFromArray6(paramTypesJsons, new Utf82);
1751
+ const paramDefaultsArr = vectorFromArray6(paramDefaultsJsons, new Utf82);
1752
+ const hasHeaderArr = vectorFromArray6(hasHeaders, new Bool2);
1753
+ const headerSchemaArr = vectorFromArray6(headerSchemas, new Binary2);
1579
1754
  const children = [
1580
1755
  nameArr.data[0],
1581
1756
  methodTypeArr.data[0],
@@ -1745,7 +1920,7 @@ async function httpDispatchUnary(method, body, ctx) {
1745
1920
  if (parsed.methodName !== method.name) {
1746
1921
  throw new HttpRpcError(`Method name in request '${parsed.methodName}' does not match URL '${method.name}'`, 400);
1747
1922
  }
1748
- const out = new OutputCollector(schema, true, ctx.serverId, parsed.requestId);
1923
+ const out = new OutputCollector(schema, true, ctx.serverId, parsed.requestId, ctx.authContext);
1749
1924
  try {
1750
1925
  const result = await method.handler(parsed.params, out);
1751
1926
  const resultBatch = buildResultBatch(schema, result, ctx.serverId, parsed.requestId);
@@ -1782,7 +1957,7 @@ async function httpDispatchStreamInit(method, body, ctx) {
1782
1957
  let headerBytes = null;
1783
1958
  if (method.headerSchema && method.headerInit) {
1784
1959
  try {
1785
- const headerOut = new OutputCollector(method.headerSchema, true, ctx.serverId, parsed.requestId);
1960
+ const headerOut = new OutputCollector(method.headerSchema, true, ctx.serverId, parsed.requestId, ctx.authContext);
1786
1961
  const headerValues = method.headerInit(parsed.params, state, headerOut);
1787
1962
  const headerBatch = buildResultBatch(method.headerSchema, headerValues, ctx.serverId, parsed.requestId);
1788
1963
  const headerBatches = [...headerOut.batches.map((b) => b.batch), headerBatch];
@@ -1850,7 +2025,7 @@ async function httpDispatchStreamExchange(method, body, ctx) {
1850
2025
  if (effectiveProducer) {
1851
2026
  return produceStreamResponse(method, state, outputSchema, inputSchema, ctx, null, null);
1852
2027
  } else {
1853
- const out = new OutputCollector(outputSchema, effectiveProducer, ctx.serverId, null);
2028
+ const out = new OutputCollector(outputSchema, effectiveProducer, ctx.serverId, null, ctx.authContext);
1854
2029
  const conformedBatch = conformBatchToSchema(reqBatch, inputSchema);
1855
2030
  try {
1856
2031
  if (method.exchangeFn) {
@@ -1900,7 +2075,7 @@ async function produceStreamResponse(method, state, outputSchema, inputSchema, c
1900
2075
  const maxBytes = ctx.maxStreamResponseBytes;
1901
2076
  let estimatedBytes = 0;
1902
2077
  while (true) {
1903
- const out = new OutputCollector(outputSchema, true, ctx.serverId, requestId);
2078
+ const out = new OutputCollector(outputSchema, true, ctx.serverId, requestId, ctx.authContext);
1904
2079
  try {
1905
2080
  if (method.producerFn) {
1906
2081
  await method.producerFn(state, out);
@@ -1976,10 +2151,12 @@ function createHttpHandler(protocol, options) {
1976
2151
  const maxRequestBytes = options?.maxRequestBytes;
1977
2152
  const maxStreamResponseBytes = options?.maxStreamResponseBytes;
1978
2153
  const serverId = options?.serverId ?? crypto.randomUUID().replace(/-/g, "").slice(0, 12);
2154
+ const authenticate = options?.authenticate;
2155
+ const oauthMetadata = options?.oauthResourceMetadata;
1979
2156
  const methods = protocol.getMethods();
1980
2157
  const compressionLevel = options?.compressionLevel;
1981
2158
  const stateSerializer = options?.stateSerializer ?? jsonStateSerializer;
1982
- const ctx = {
2159
+ const baseCtx = {
1983
2160
  signingKey,
1984
2161
  tokenTtl,
1985
2162
  serverId,
@@ -2015,6 +2192,18 @@ function createHttpHandler(protocol, options) {
2015
2192
  return async function handler(request) {
2016
2193
  const url = new URL(request.url);
2017
2194
  const path = url.pathname;
2195
+ if (oauthMetadata && path === wellKnownPath(prefix)) {
2196
+ if (request.method !== "GET") {
2197
+ return new Response("Method Not Allowed", { status: 405 });
2198
+ }
2199
+ const body2 = JSON.stringify(oauthResourceMetadataToJson(oauthMetadata));
2200
+ const headers = new Headers({
2201
+ "Content-Type": "application/json",
2202
+ "Cache-Control": "public, max-age=3600"
2203
+ });
2204
+ addCorsHeaders(headers);
2205
+ return new Response(body2, { status: 200, headers });
2206
+ }
2018
2207
  if (request.method === "OPTIONS") {
2019
2208
  if (path === `${prefix}/__capabilities__`) {
2020
2209
  const headers = new Headers;
@@ -2050,6 +2239,22 @@ function createHttpHandler(protocol, options) {
2050
2239
  if (contentEncoding === "zstd") {
2051
2240
  body = zstdDecompress(body);
2052
2241
  }
2242
+ const ctx = { ...baseCtx };
2243
+ if (authenticate) {
2244
+ try {
2245
+ ctx.authContext = await authenticate(request);
2246
+ } catch (error) {
2247
+ const headers = new Headers({ "Content-Type": "text/plain" });
2248
+ addCorsHeaders(headers);
2249
+ if (oauthMetadata) {
2250
+ const metadataUrl = new URL(request.url);
2251
+ metadataUrl.pathname = wellKnownPath(prefix);
2252
+ metadataUrl.search = "";
2253
+ headers.set("WWW-Authenticate", buildWwwAuthenticateHeader(metadataUrl.toString()));
2254
+ }
2255
+ return new Response(error.message || "Unauthorized", { status: 401, headers });
2256
+ }
2257
+ }
2053
2258
  if (path === `${prefix}/${DESCRIBE_METHOD_NAME}`) {
2054
2259
  try {
2055
2260
  const response = httpDispatchDescribe(protocol.name, methods, serverId);
@@ -2109,6 +2314,1200 @@ function createHttpHandler(protocol, options) {
2109
2314
  }
2110
2315
  };
2111
2316
  }
2317
+ // node_modules/oauth4webapi/build/index.js
2318
+ var USER_AGENT;
2319
+ if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
2320
+ const NAME = "oauth4webapi";
2321
+ const VERSION = "v3.8.5";
2322
+ USER_AGENT = `${NAME}/${VERSION}`;
2323
+ }
2324
+ function looseInstanceOf(input, expected) {
2325
+ if (input == null) {
2326
+ return false;
2327
+ }
2328
+ try {
2329
+ return input instanceof expected || Object.getPrototypeOf(input)[Symbol.toStringTag] === expected.prototype[Symbol.toStringTag];
2330
+ } catch {
2331
+ return false;
2332
+ }
2333
+ }
2334
+ var ERR_INVALID_ARG_VALUE = "ERR_INVALID_ARG_VALUE";
2335
+ var ERR_INVALID_ARG_TYPE = "ERR_INVALID_ARG_TYPE";
2336
+ function CodedTypeError(message, code, cause) {
2337
+ const err = new TypeError(message, { cause });
2338
+ Object.assign(err, { code });
2339
+ return err;
2340
+ }
2341
+ var allowInsecureRequests = Symbol();
2342
+ var clockSkew = Symbol();
2343
+ var clockTolerance = Symbol();
2344
+ var customFetch = Symbol();
2345
+ var modifyAssertion = Symbol();
2346
+ var jweDecrypt = Symbol();
2347
+ var jwksCache = Symbol();
2348
+ var encoder = new TextEncoder;
2349
+ var decoder = new TextDecoder;
2350
+ function buf(input) {
2351
+ if (typeof input === "string") {
2352
+ return encoder.encode(input);
2353
+ }
2354
+ return decoder.decode(input);
2355
+ }
2356
+ var encodeBase64Url;
2357
+ if (Uint8Array.prototype.toBase64) {
2358
+ encodeBase64Url = (input) => {
2359
+ if (input instanceof ArrayBuffer) {
2360
+ input = new Uint8Array(input);
2361
+ }
2362
+ return input.toBase64({ alphabet: "base64url", omitPadding: true });
2363
+ };
2364
+ } else {
2365
+ const CHUNK_SIZE = 32768;
2366
+ encodeBase64Url = (input) => {
2367
+ if (input instanceof ArrayBuffer) {
2368
+ input = new Uint8Array(input);
2369
+ }
2370
+ const arr = [];
2371
+ for (let i = 0;i < input.byteLength; i += CHUNK_SIZE) {
2372
+ arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
2373
+ }
2374
+ return btoa(arr.join("")).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
2375
+ };
2376
+ }
2377
+ var decodeBase64Url;
2378
+ if (Uint8Array.fromBase64) {
2379
+ decodeBase64Url = (input) => {
2380
+ try {
2381
+ return Uint8Array.fromBase64(input, { alphabet: "base64url" });
2382
+ } catch (cause) {
2383
+ throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
2384
+ }
2385
+ };
2386
+ } else {
2387
+ decodeBase64Url = (input) => {
2388
+ try {
2389
+ const binary = atob(input.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""));
2390
+ const bytes = new Uint8Array(binary.length);
2391
+ for (let i = 0;i < binary.length; i++) {
2392
+ bytes[i] = binary.charCodeAt(i);
2393
+ }
2394
+ return bytes;
2395
+ } catch (cause) {
2396
+ throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
2397
+ }
2398
+ };
2399
+ }
2400
+ function b64u(input) {
2401
+ if (typeof input === "string") {
2402
+ return decodeBase64Url(input);
2403
+ }
2404
+ return encodeBase64Url(input);
2405
+ }
2406
+
2407
+ class UnsupportedOperationError extends Error {
2408
+ code;
2409
+ constructor(message, options) {
2410
+ super(message, options);
2411
+ this.name = this.constructor.name;
2412
+ this.code = UNSUPPORTED_OPERATION;
2413
+ Error.captureStackTrace?.(this, this.constructor);
2414
+ }
2415
+ }
2416
+
2417
+ class OperationProcessingError extends Error {
2418
+ code;
2419
+ constructor(message, options) {
2420
+ super(message, options);
2421
+ this.name = this.constructor.name;
2422
+ if (options?.code) {
2423
+ this.code = options?.code;
2424
+ }
2425
+ Error.captureStackTrace?.(this, this.constructor);
2426
+ }
2427
+ }
2428
+ function OPE(message, code, cause) {
2429
+ return new OperationProcessingError(message, { code, cause });
2430
+ }
2431
+ async function calculateJwkThumbprint(jwk) {
2432
+ let components;
2433
+ switch (jwk.kty) {
2434
+ case "EC":
2435
+ components = {
2436
+ crv: jwk.crv,
2437
+ kty: jwk.kty,
2438
+ x: jwk.x,
2439
+ y: jwk.y
2440
+ };
2441
+ break;
2442
+ case "OKP":
2443
+ components = {
2444
+ crv: jwk.crv,
2445
+ kty: jwk.kty,
2446
+ x: jwk.x
2447
+ };
2448
+ break;
2449
+ case "AKP":
2450
+ components = {
2451
+ alg: jwk.alg,
2452
+ kty: jwk.kty,
2453
+ pub: jwk.pub
2454
+ };
2455
+ break;
2456
+ case "RSA":
2457
+ components = {
2458
+ e: jwk.e,
2459
+ kty: jwk.kty,
2460
+ n: jwk.n
2461
+ };
2462
+ break;
2463
+ default:
2464
+ throw new UnsupportedOperationError("unsupported JWK key type", { cause: jwk });
2465
+ }
2466
+ return b64u(await crypto.subtle.digest("SHA-256", buf(JSON.stringify(components))));
2467
+ }
2468
+ function assertCryptoKey(key, it) {
2469
+ if (!(key instanceof CryptoKey)) {
2470
+ throw CodedTypeError(`${it} must be a CryptoKey`, ERR_INVALID_ARG_TYPE);
2471
+ }
2472
+ }
2473
+ function assertPrivateKey(key, it) {
2474
+ assertCryptoKey(key, it);
2475
+ if (key.type !== "private") {
2476
+ throw CodedTypeError(`${it} must be a private CryptoKey`, ERR_INVALID_ARG_VALUE);
2477
+ }
2478
+ }
2479
+ function assertPublicKey(key, it) {
2480
+ assertCryptoKey(key, it);
2481
+ if (key.type !== "public") {
2482
+ throw CodedTypeError(`${it} must be a public CryptoKey`, ERR_INVALID_ARG_VALUE);
2483
+ }
2484
+ }
2485
+ function normalizeTyp(value) {
2486
+ return value.toLowerCase().replace(/^application\//, "");
2487
+ }
2488
+ function isJsonObject(input) {
2489
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
2490
+ return false;
2491
+ }
2492
+ return true;
2493
+ }
2494
+ function prepareHeaders(input) {
2495
+ if (looseInstanceOf(input, Headers)) {
2496
+ input = Object.fromEntries(input.entries());
2497
+ }
2498
+ const headers = new Headers(input ?? {});
2499
+ if (USER_AGENT && !headers.has("user-agent")) {
2500
+ headers.set("user-agent", USER_AGENT);
2501
+ }
2502
+ if (headers.has("authorization")) {
2503
+ throw CodedTypeError('"options.headers" must not include the "authorization" header name', ERR_INVALID_ARG_VALUE);
2504
+ }
2505
+ return headers;
2506
+ }
2507
+ function signal(url, value) {
2508
+ if (value !== undefined) {
2509
+ if (typeof value === "function") {
2510
+ value = value(url.href);
2511
+ }
2512
+ if (!(value instanceof AbortSignal)) {
2513
+ throw CodedTypeError('"options.signal" must return or be an instance of AbortSignal', ERR_INVALID_ARG_TYPE);
2514
+ }
2515
+ return value;
2516
+ }
2517
+ return;
2518
+ }
2519
+ function replaceDoubleSlash(pathname) {
2520
+ if (pathname.includes("//")) {
2521
+ return pathname.replace("//", "/");
2522
+ }
2523
+ return pathname;
2524
+ }
2525
+ function prependWellKnown(url, wellKnown, allowTerminatingSlash = false) {
2526
+ if (url.pathname === "/") {
2527
+ url.pathname = wellKnown;
2528
+ } else {
2529
+ url.pathname = replaceDoubleSlash(`${wellKnown}/${allowTerminatingSlash ? url.pathname : url.pathname.replace(/(\/)$/, "")}`);
2530
+ }
2531
+ return url;
2532
+ }
2533
+ function appendWellKnown(url, wellKnown) {
2534
+ url.pathname = replaceDoubleSlash(`${url.pathname}/${wellKnown}`);
2535
+ return url;
2536
+ }
2537
+ async function performDiscovery(input, urlName, transform, options) {
2538
+ if (!(input instanceof URL)) {
2539
+ throw CodedTypeError(`"${urlName}" must be an instance of URL`, ERR_INVALID_ARG_TYPE);
2540
+ }
2541
+ checkProtocol(input, options?.[allowInsecureRequests] !== true);
2542
+ const url = transform(new URL(input.href));
2543
+ const headers = prepareHeaders(options?.headers);
2544
+ headers.set("accept", "application/json");
2545
+ return (options?.[customFetch] || fetch)(url.href, {
2546
+ body: undefined,
2547
+ headers: Object.fromEntries(headers.entries()),
2548
+ method: "GET",
2549
+ redirect: "manual",
2550
+ signal: signal(url, options?.signal)
2551
+ });
2552
+ }
2553
+ async function discoveryRequest(issuerIdentifier, options) {
2554
+ return performDiscovery(issuerIdentifier, "issuerIdentifier", (url) => {
2555
+ switch (options?.algorithm) {
2556
+ case undefined:
2557
+ case "oidc":
2558
+ appendWellKnown(url, ".well-known/openid-configuration");
2559
+ break;
2560
+ case "oauth2":
2561
+ prependWellKnown(url, ".well-known/oauth-authorization-server");
2562
+ break;
2563
+ default:
2564
+ throw CodedTypeError('"options.algorithm" must be "oidc" (default), or "oauth2"', ERR_INVALID_ARG_VALUE);
2565
+ }
2566
+ return url;
2567
+ }, options);
2568
+ }
2569
+ function assertString(input, it, code, cause) {
2570
+ try {
2571
+ if (typeof input !== "string") {
2572
+ throw CodedTypeError(`${it} must be a string`, ERR_INVALID_ARG_TYPE, cause);
2573
+ }
2574
+ if (input.length === 0) {
2575
+ throw CodedTypeError(`${it} must not be empty`, ERR_INVALID_ARG_VALUE, cause);
2576
+ }
2577
+ } catch (err) {
2578
+ if (code) {
2579
+ throw OPE(err.message, code, cause);
2580
+ }
2581
+ throw err;
2582
+ }
2583
+ }
2584
+ async function processDiscoveryResponse(expectedIssuerIdentifier, response) {
2585
+ const expected = expectedIssuerIdentifier;
2586
+ if (!(expected instanceof URL) && expected !== _nodiscoverycheck) {
2587
+ throw CodedTypeError('"expectedIssuerIdentifier" must be an instance of URL', ERR_INVALID_ARG_TYPE);
2588
+ }
2589
+ if (!looseInstanceOf(response, Response)) {
2590
+ throw CodedTypeError('"response" must be an instance of Response', ERR_INVALID_ARG_TYPE);
2591
+ }
2592
+ if (response.status !== 200) {
2593
+ throw OPE('"response" is not a conform Authorization Server Metadata response (unexpected HTTP status code)', RESPONSE_IS_NOT_CONFORM, response);
2594
+ }
2595
+ assertReadableResponse(response);
2596
+ const json = await getResponseJsonBody(response);
2597
+ assertString(json.issuer, '"response" body "issuer" property', INVALID_RESPONSE, { body: json });
2598
+ if (expected !== _nodiscoverycheck && new URL(json.issuer).href !== expected.href) {
2599
+ throw OPE('"response" body "issuer" property does not match the expected value', JSON_ATTRIBUTE_COMPARISON, { expected: expected.href, body: json, attribute: "issuer" });
2600
+ }
2601
+ return json;
2602
+ }
2603
+ function assertApplicationJson(response) {
2604
+ assertContentType(response, "application/json");
2605
+ }
2606
+ function notJson(response, ...types) {
2607
+ let msg = '"response" content-type must be ';
2608
+ if (types.length > 2) {
2609
+ const last = types.pop();
2610
+ msg += `${types.join(", ")}, or ${last}`;
2611
+ } else if (types.length === 2) {
2612
+ msg += `${types[0]} or ${types[1]}`;
2613
+ } else {
2614
+ msg += types[0];
2615
+ }
2616
+ return OPE(msg, RESPONSE_IS_NOT_JSON, response);
2617
+ }
2618
+ function assertContentTypes(response, ...types) {
2619
+ if (!types.includes(getContentType(response))) {
2620
+ throw notJson(response, ...types);
2621
+ }
2622
+ }
2623
+ function assertContentType(response, contentType) {
2624
+ if (getContentType(response) !== contentType) {
2625
+ throw notJson(response, contentType);
2626
+ }
2627
+ }
2628
+ function randomBytes2() {
2629
+ return b64u(crypto.getRandomValues(new Uint8Array(32)));
2630
+ }
2631
+ function psAlg(key) {
2632
+ switch (key.algorithm.hash.name) {
2633
+ case "SHA-256":
2634
+ return "PS256";
2635
+ case "SHA-384":
2636
+ return "PS384";
2637
+ case "SHA-512":
2638
+ return "PS512";
2639
+ default:
2640
+ throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
2641
+ cause: key
2642
+ });
2643
+ }
2644
+ }
2645
+ function rsAlg(key) {
2646
+ switch (key.algorithm.hash.name) {
2647
+ case "SHA-256":
2648
+ return "RS256";
2649
+ case "SHA-384":
2650
+ return "RS384";
2651
+ case "SHA-512":
2652
+ return "RS512";
2653
+ default:
2654
+ throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
2655
+ cause: key
2656
+ });
2657
+ }
2658
+ }
2659
+ function esAlg(key) {
2660
+ switch (key.algorithm.namedCurve) {
2661
+ case "P-256":
2662
+ return "ES256";
2663
+ case "P-384":
2664
+ return "ES384";
2665
+ case "P-521":
2666
+ return "ES512";
2667
+ default:
2668
+ throw new UnsupportedOperationError("unsupported EcKeyAlgorithm namedCurve", { cause: key });
2669
+ }
2670
+ }
2671
+ function keyToJws(key) {
2672
+ switch (key.algorithm.name) {
2673
+ case "RSA-PSS":
2674
+ return psAlg(key);
2675
+ case "RSASSA-PKCS1-v1_5":
2676
+ return rsAlg(key);
2677
+ case "ECDSA":
2678
+ return esAlg(key);
2679
+ case "Ed25519":
2680
+ case "ML-DSA-44":
2681
+ case "ML-DSA-65":
2682
+ case "ML-DSA-87":
2683
+ return key.algorithm.name;
2684
+ case "EdDSA":
2685
+ return "Ed25519";
2686
+ default:
2687
+ throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
2688
+ }
2689
+ }
2690
+ function getClockSkew(client) {
2691
+ const skew = client?.[clockSkew];
2692
+ return typeof skew === "number" && Number.isFinite(skew) ? skew : 0;
2693
+ }
2694
+ function getClockTolerance(client) {
2695
+ const tolerance = client?.[clockTolerance];
2696
+ return typeof tolerance === "number" && Number.isFinite(tolerance) && Math.sign(tolerance) !== -1 ? tolerance : 30;
2697
+ }
2698
+ function epochTime() {
2699
+ return Math.floor(Date.now() / 1000);
2700
+ }
2701
+ function assertAs(as) {
2702
+ if (typeof as !== "object" || as === null) {
2703
+ throw CodedTypeError('"as" must be an object', ERR_INVALID_ARG_TYPE);
2704
+ }
2705
+ assertString(as.issuer, '"as.issuer"');
2706
+ }
2707
+ async function signJwt(header, payload, key) {
2708
+ if (!key.usages.includes("sign")) {
2709
+ throw CodedTypeError('CryptoKey instances used for signing assertions must include "sign" in their "usages"', ERR_INVALID_ARG_VALUE);
2710
+ }
2711
+ const input = `${b64u(buf(JSON.stringify(header)))}.${b64u(buf(JSON.stringify(payload)))}`;
2712
+ const signature = b64u(await crypto.subtle.sign(keyToSubtle(key), key, buf(input)));
2713
+ return `${input}.${signature}`;
2714
+ }
2715
+ var jwkCache;
2716
+ async function getSetPublicJwkCache(key, alg) {
2717
+ const { kty, e, n, x, y, crv, pub } = await crypto.subtle.exportKey("jwk", key);
2718
+ const jwk = { kty, e, n, x, y, crv, pub };
2719
+ if (kty === "AKP")
2720
+ jwk.alg = alg;
2721
+ jwkCache.set(key, jwk);
2722
+ return jwk;
2723
+ }
2724
+ async function publicJwk(key, alg) {
2725
+ jwkCache ||= new WeakMap;
2726
+ return jwkCache.get(key) || getSetPublicJwkCache(key, alg);
2727
+ }
2728
+ var URLParse = URL.parse ? (url, base) => URL.parse(url, base) : (url, base) => {
2729
+ try {
2730
+ return new URL(url, base);
2731
+ } catch {
2732
+ return null;
2733
+ }
2734
+ };
2735
+ function checkProtocol(url, enforceHttps) {
2736
+ if (enforceHttps && url.protocol !== "https:") {
2737
+ throw OPE("only requests to HTTPS are allowed", HTTP_REQUEST_FORBIDDEN, url);
2738
+ }
2739
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
2740
+ throw OPE("only HTTP and HTTPS requests are allowed", REQUEST_PROTOCOL_FORBIDDEN, url);
2741
+ }
2742
+ }
2743
+ function validateEndpoint(value, endpoint, useMtlsAlias, enforceHttps) {
2744
+ let url;
2745
+ if (typeof value !== "string" || !(url = URLParse(value))) {
2746
+ throw OPE(`authorization server metadata does not contain a valid ${useMtlsAlias ? `"as.mtls_endpoint_aliases.${endpoint}"` : `"as.${endpoint}"`}`, value === undefined ? MISSING_SERVER_METADATA : INVALID_SERVER_METADATA, { attribute: useMtlsAlias ? `mtls_endpoint_aliases.${endpoint}` : endpoint });
2747
+ }
2748
+ checkProtocol(url, enforceHttps);
2749
+ return url;
2750
+ }
2751
+ function resolveEndpoint(as, endpoint, useMtlsAlias, enforceHttps) {
2752
+ if (useMtlsAlias && as.mtls_endpoint_aliases && endpoint in as.mtls_endpoint_aliases) {
2753
+ return validateEndpoint(as.mtls_endpoint_aliases[endpoint], endpoint, useMtlsAlias, enforceHttps);
2754
+ }
2755
+ return validateEndpoint(as[endpoint], endpoint, useMtlsAlias, enforceHttps);
2756
+ }
2757
+ class DPoPHandler {
2758
+ #header;
2759
+ #privateKey;
2760
+ #publicKey;
2761
+ #clockSkew;
2762
+ #modifyAssertion;
2763
+ #map;
2764
+ #jkt;
2765
+ constructor(client, keyPair, options) {
2766
+ assertPrivateKey(keyPair?.privateKey, '"DPoP.privateKey"');
2767
+ assertPublicKey(keyPair?.publicKey, '"DPoP.publicKey"');
2768
+ if (!keyPair.publicKey.extractable) {
2769
+ throw CodedTypeError('"DPoP.publicKey.extractable" must be true', ERR_INVALID_ARG_VALUE);
2770
+ }
2771
+ this.#modifyAssertion = options?.[modifyAssertion];
2772
+ this.#clockSkew = getClockSkew(client);
2773
+ this.#privateKey = keyPair.privateKey;
2774
+ this.#publicKey = keyPair.publicKey;
2775
+ branded.add(this);
2776
+ }
2777
+ #get(key) {
2778
+ this.#map ||= new Map;
2779
+ let item = this.#map.get(key);
2780
+ if (item) {
2781
+ this.#map.delete(key);
2782
+ this.#map.set(key, item);
2783
+ }
2784
+ return item;
2785
+ }
2786
+ #set(key, val) {
2787
+ this.#map ||= new Map;
2788
+ this.#map.delete(key);
2789
+ if (this.#map.size === 100) {
2790
+ this.#map.delete(this.#map.keys().next().value);
2791
+ }
2792
+ this.#map.set(key, val);
2793
+ }
2794
+ async calculateThumbprint() {
2795
+ if (!this.#jkt) {
2796
+ const jwk = await crypto.subtle.exportKey("jwk", this.#publicKey);
2797
+ this.#jkt ||= await calculateJwkThumbprint(jwk);
2798
+ }
2799
+ return this.#jkt;
2800
+ }
2801
+ async addProof(url, headers, htm, accessToken) {
2802
+ const alg = keyToJws(this.#privateKey);
2803
+ this.#header ||= {
2804
+ alg,
2805
+ typ: "dpop+jwt",
2806
+ jwk: await publicJwk(this.#publicKey, alg)
2807
+ };
2808
+ const nonce = this.#get(url.origin);
2809
+ const now = epochTime() + this.#clockSkew;
2810
+ const payload = {
2811
+ iat: now,
2812
+ jti: randomBytes2(),
2813
+ htm,
2814
+ nonce,
2815
+ htu: `${url.origin}${url.pathname}`,
2816
+ ath: accessToken ? b64u(await crypto.subtle.digest("SHA-256", buf(accessToken))) : undefined
2817
+ };
2818
+ this.#modifyAssertion?.(this.#header, payload);
2819
+ headers.set("dpop", await signJwt(this.#header, payload, this.#privateKey));
2820
+ }
2821
+ cacheNonce(response, url) {
2822
+ try {
2823
+ const nonce = response.headers.get("dpop-nonce");
2824
+ if (nonce) {
2825
+ this.#set(url.origin, nonce);
2826
+ }
2827
+ } catch {}
2828
+ }
2829
+ }
2830
+ var tokenMatch = "[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+";
2831
+ var token68Match = "[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}";
2832
+ var quotedMatch = '"((?:[^"\\\\]|\\\\[\\s\\S])*)"';
2833
+ var quotedParamMatcher = "(" + tokenMatch + ")\\s*=\\s*" + quotedMatch;
2834
+ var paramMatcher = "(" + tokenMatch + ")\\s*=\\s*(" + tokenMatch + ")";
2835
+ var schemeRE = new RegExp("^[,\\s]*(" + tokenMatch + ")");
2836
+ var quotedParamRE = new RegExp("^[,\\s]*" + quotedParamMatcher + "[,\\s]*(.*)");
2837
+ var unquotedParamRE = new RegExp("^[,\\s]*" + paramMatcher + "[,\\s]*(.*)");
2838
+ var token68ParamRE = new RegExp("^(" + token68Match + ")(?:$|[,\\s])(.*)");
2839
+ var jwksMap;
2840
+ function setJwksCache(as, jwks, uat, cache) {
2841
+ jwksMap ||= new WeakMap;
2842
+ jwksMap.set(as, {
2843
+ jwks,
2844
+ uat,
2845
+ get age() {
2846
+ return epochTime() - this.uat;
2847
+ }
2848
+ });
2849
+ if (cache) {
2850
+ Object.assign(cache, { jwks: structuredClone(jwks), uat });
2851
+ }
2852
+ }
2853
+ function isFreshJwksCache(input) {
2854
+ if (typeof input !== "object" || input === null) {
2855
+ return false;
2856
+ }
2857
+ if (!("uat" in input) || typeof input.uat !== "number" || epochTime() - input.uat >= 300) {
2858
+ return false;
2859
+ }
2860
+ if (!("jwks" in input) || !isJsonObject(input.jwks) || !Array.isArray(input.jwks.keys) || !Array.prototype.every.call(input.jwks.keys, isJsonObject)) {
2861
+ return false;
2862
+ }
2863
+ return true;
2864
+ }
2865
+ function clearJwksCache(as, cache) {
2866
+ jwksMap?.delete(as);
2867
+ delete cache?.jwks;
2868
+ delete cache?.uat;
2869
+ }
2870
+ async function getPublicSigKeyFromIssuerJwksUri(as, options, header) {
2871
+ const { alg, kid } = header;
2872
+ checkSupportedJwsAlg(header);
2873
+ if (!jwksMap?.has(as) && isFreshJwksCache(options?.[jwksCache])) {
2874
+ setJwksCache(as, options?.[jwksCache].jwks, options?.[jwksCache].uat);
2875
+ }
2876
+ let jwks;
2877
+ let age;
2878
+ if (jwksMap?.has(as)) {
2879
+ ({ jwks, age } = jwksMap.get(as));
2880
+ if (age >= 300) {
2881
+ clearJwksCache(as, options?.[jwksCache]);
2882
+ return getPublicSigKeyFromIssuerJwksUri(as, options, header);
2883
+ }
2884
+ } else {
2885
+ jwks = await jwksRequest(as, options).then(processJwksResponse);
2886
+ age = 0;
2887
+ setJwksCache(as, jwks, epochTime(), options?.[jwksCache]);
2888
+ }
2889
+ let kty;
2890
+ switch (alg.slice(0, 2)) {
2891
+ case "RS":
2892
+ case "PS":
2893
+ kty = "RSA";
2894
+ break;
2895
+ case "ES":
2896
+ kty = "EC";
2897
+ break;
2898
+ case "Ed":
2899
+ kty = "OKP";
2900
+ break;
2901
+ case "ML":
2902
+ kty = "AKP";
2903
+ break;
2904
+ default:
2905
+ throw new UnsupportedOperationError("unsupported JWS algorithm", { cause: { alg } });
2906
+ }
2907
+ const candidates = jwks.keys.filter((jwk2) => {
2908
+ if (jwk2.kty !== kty) {
2909
+ return false;
2910
+ }
2911
+ if (kid !== undefined && kid !== jwk2.kid) {
2912
+ return false;
2913
+ }
2914
+ if (jwk2.alg !== undefined && alg !== jwk2.alg) {
2915
+ return false;
2916
+ }
2917
+ if (jwk2.use !== undefined && jwk2.use !== "sig") {
2918
+ return false;
2919
+ }
2920
+ if (jwk2.key_ops?.includes("verify") === false) {
2921
+ return false;
2922
+ }
2923
+ switch (true) {
2924
+ case (alg === "ES256" && jwk2.crv !== "P-256"):
2925
+ case (alg === "ES384" && jwk2.crv !== "P-384"):
2926
+ case (alg === "ES512" && jwk2.crv !== "P-521"):
2927
+ case (alg === "Ed25519" && jwk2.crv !== "Ed25519"):
2928
+ case (alg === "EdDSA" && jwk2.crv !== "Ed25519"):
2929
+ return false;
2930
+ }
2931
+ return true;
2932
+ });
2933
+ const { 0: jwk, length } = candidates;
2934
+ if (!length) {
2935
+ if (age >= 60) {
2936
+ clearJwksCache(as, options?.[jwksCache]);
2937
+ return getPublicSigKeyFromIssuerJwksUri(as, options, header);
2938
+ }
2939
+ throw OPE("error when selecting a JWT verification key, no applicable keys found", KEY_SELECTION, { header, candidates, jwks_uri: new URL(as.jwks_uri) });
2940
+ }
2941
+ if (length !== 1) {
2942
+ throw OPE('error when selecting a JWT verification key, multiple applicable keys found, a "kid" JWT Header Parameter is required', KEY_SELECTION, { header, candidates, jwks_uri: new URL(as.jwks_uri) });
2943
+ }
2944
+ return importJwk(alg, jwk);
2945
+ }
2946
+ var skipSubjectCheck = Symbol();
2947
+ function getContentType(input) {
2948
+ return input.headers.get("content-type")?.split(";")[0];
2949
+ }
2950
+ var idTokenClaims = new WeakMap;
2951
+ var jwtRefs = new WeakMap;
2952
+ function validateAudience(expected, result) {
2953
+ if (Array.isArray(result.claims.aud)) {
2954
+ if (!result.claims.aud.includes(expected)) {
2955
+ throw OPE('unexpected JWT "aud" (audience) claim value', JWT_CLAIM_COMPARISON, {
2956
+ expected,
2957
+ claims: result.claims,
2958
+ claim: "aud"
2959
+ });
2960
+ }
2961
+ } else if (result.claims.aud !== expected) {
2962
+ throw OPE('unexpected JWT "aud" (audience) claim value', JWT_CLAIM_COMPARISON, {
2963
+ expected,
2964
+ claims: result.claims,
2965
+ claim: "aud"
2966
+ });
2967
+ }
2968
+ return result;
2969
+ }
2970
+ function validateIssuer(as, result) {
2971
+ const expected = as[_expectedIssuer]?.(result) ?? as.issuer;
2972
+ if (result.claims.iss !== expected) {
2973
+ throw OPE('unexpected JWT "iss" (issuer) claim value', JWT_CLAIM_COMPARISON, {
2974
+ expected,
2975
+ claims: result.claims,
2976
+ claim: "iss"
2977
+ });
2978
+ }
2979
+ return result;
2980
+ }
2981
+ var branded = new WeakSet;
2982
+ var nopkce = Symbol();
2983
+ var jwtClaimNames = {
2984
+ aud: "audience",
2985
+ c_hash: "code hash",
2986
+ client_id: "client id",
2987
+ exp: "expiration time",
2988
+ iat: "issued at",
2989
+ iss: "issuer",
2990
+ jti: "jwt id",
2991
+ nonce: "nonce",
2992
+ s_hash: "state hash",
2993
+ sub: "subject",
2994
+ ath: "access token hash",
2995
+ htm: "http method",
2996
+ htu: "http uri",
2997
+ cnf: "confirmation",
2998
+ auth_time: "authentication time"
2999
+ };
3000
+ function validatePresence(required, result) {
3001
+ for (const claim of required) {
3002
+ if (result.claims[claim] === undefined) {
3003
+ throw OPE(`JWT "${claim}" (${jwtClaimNames[claim]}) claim missing`, INVALID_RESPONSE, {
3004
+ claims: result.claims
3005
+ });
3006
+ }
3007
+ }
3008
+ return result;
3009
+ }
3010
+ var expectNoNonce = Symbol();
3011
+ var skipAuthTimeCheck = Symbol();
3012
+ var UNSUPPORTED_OPERATION = "OAUTH_UNSUPPORTED_OPERATION";
3013
+ var PARSE_ERROR = "OAUTH_PARSE_ERROR";
3014
+ var INVALID_RESPONSE = "OAUTH_INVALID_RESPONSE";
3015
+ var INVALID_REQUEST = "OAUTH_INVALID_REQUEST";
3016
+ var RESPONSE_IS_NOT_JSON = "OAUTH_RESPONSE_IS_NOT_JSON";
3017
+ var RESPONSE_IS_NOT_CONFORM = "OAUTH_RESPONSE_IS_NOT_CONFORM";
3018
+ var HTTP_REQUEST_FORBIDDEN = "OAUTH_HTTP_REQUEST_FORBIDDEN";
3019
+ var REQUEST_PROTOCOL_FORBIDDEN = "OAUTH_REQUEST_PROTOCOL_FORBIDDEN";
3020
+ var JWT_TIMESTAMP_CHECK = "OAUTH_JWT_TIMESTAMP_CHECK_FAILED";
3021
+ var JWT_CLAIM_COMPARISON = "OAUTH_JWT_CLAIM_COMPARISON_FAILED";
3022
+ var JSON_ATTRIBUTE_COMPARISON = "OAUTH_JSON_ATTRIBUTE_COMPARISON_FAILED";
3023
+ var KEY_SELECTION = "OAUTH_KEY_SELECTION_FAILED";
3024
+ var MISSING_SERVER_METADATA = "OAUTH_MISSING_SERVER_METADATA";
3025
+ var INVALID_SERVER_METADATA = "OAUTH_INVALID_SERVER_METADATA";
3026
+ function checkJwtType(expected, result) {
3027
+ if (typeof result.header.typ !== "string" || normalizeTyp(result.header.typ) !== expected) {
3028
+ throw OPE('unexpected JWT "typ" header parameter value', INVALID_RESPONSE, {
3029
+ header: result.header
3030
+ });
3031
+ }
3032
+ return result;
3033
+ }
3034
+ function assertReadableResponse(response) {
3035
+ if (response.bodyUsed) {
3036
+ throw CodedTypeError('"response" body has been used already', ERR_INVALID_ARG_VALUE);
3037
+ }
3038
+ }
3039
+ async function jwksRequest(as, options) {
3040
+ assertAs(as);
3041
+ const url = resolveEndpoint(as, "jwks_uri", false, options?.[allowInsecureRequests] !== true);
3042
+ const headers = prepareHeaders(options?.headers);
3043
+ headers.set("accept", "application/json");
3044
+ headers.append("accept", "application/jwk-set+json");
3045
+ return (options?.[customFetch] || fetch)(url.href, {
3046
+ body: undefined,
3047
+ headers: Object.fromEntries(headers.entries()),
3048
+ method: "GET",
3049
+ redirect: "manual",
3050
+ signal: signal(url, options?.signal)
3051
+ });
3052
+ }
3053
+ async function processJwksResponse(response) {
3054
+ if (!looseInstanceOf(response, Response)) {
3055
+ throw CodedTypeError('"response" must be an instance of Response', ERR_INVALID_ARG_TYPE);
3056
+ }
3057
+ if (response.status !== 200) {
3058
+ throw OPE('"response" is not a conform JSON Web Key Set response (unexpected HTTP status code)', RESPONSE_IS_NOT_CONFORM, response);
3059
+ }
3060
+ assertReadableResponse(response);
3061
+ const json = await getResponseJsonBody(response, (response2) => assertContentTypes(response2, "application/json", "application/jwk-set+json"));
3062
+ if (!Array.isArray(json.keys)) {
3063
+ throw OPE('"response" body "keys" property must be an array', INVALID_RESPONSE, { body: json });
3064
+ }
3065
+ if (!Array.prototype.every.call(json.keys, isJsonObject)) {
3066
+ throw OPE('"response" body "keys" property members must be JWK formatted objects', INVALID_RESPONSE, { body: json });
3067
+ }
3068
+ return json;
3069
+ }
3070
+ function supported(alg) {
3071
+ switch (alg) {
3072
+ case "PS256":
3073
+ case "ES256":
3074
+ case "RS256":
3075
+ case "PS384":
3076
+ case "ES384":
3077
+ case "RS384":
3078
+ case "PS512":
3079
+ case "ES512":
3080
+ case "RS512":
3081
+ case "Ed25519":
3082
+ case "EdDSA":
3083
+ case "ML-DSA-44":
3084
+ case "ML-DSA-65":
3085
+ case "ML-DSA-87":
3086
+ return true;
3087
+ default:
3088
+ return false;
3089
+ }
3090
+ }
3091
+ function checkSupportedJwsAlg(header) {
3092
+ if (!supported(header.alg)) {
3093
+ throw new UnsupportedOperationError('unsupported JWS "alg" identifier', {
3094
+ cause: { alg: header.alg }
3095
+ });
3096
+ }
3097
+ }
3098
+ function checkRsaKeyAlgorithm(key) {
3099
+ const { algorithm } = key;
3100
+ if (typeof algorithm.modulusLength !== "number" || algorithm.modulusLength < 2048) {
3101
+ throw new UnsupportedOperationError(`unsupported ${algorithm.name} modulusLength`, {
3102
+ cause: key
3103
+ });
3104
+ }
3105
+ }
3106
+ function ecdsaHashName(key) {
3107
+ const { algorithm } = key;
3108
+ switch (algorithm.namedCurve) {
3109
+ case "P-256":
3110
+ return "SHA-256";
3111
+ case "P-384":
3112
+ return "SHA-384";
3113
+ case "P-521":
3114
+ return "SHA-512";
3115
+ default:
3116
+ throw new UnsupportedOperationError("unsupported ECDSA namedCurve", { cause: key });
3117
+ }
3118
+ }
3119
+ function keyToSubtle(key) {
3120
+ switch (key.algorithm.name) {
3121
+ case "ECDSA":
3122
+ return {
3123
+ name: key.algorithm.name,
3124
+ hash: ecdsaHashName(key)
3125
+ };
3126
+ case "RSA-PSS": {
3127
+ checkRsaKeyAlgorithm(key);
3128
+ switch (key.algorithm.hash.name) {
3129
+ case "SHA-256":
3130
+ case "SHA-384":
3131
+ case "SHA-512":
3132
+ return {
3133
+ name: key.algorithm.name,
3134
+ saltLength: parseInt(key.algorithm.hash.name.slice(-3), 10) >> 3
3135
+ };
3136
+ default:
3137
+ throw new UnsupportedOperationError("unsupported RSA-PSS hash name", { cause: key });
3138
+ }
3139
+ }
3140
+ case "RSASSA-PKCS1-v1_5":
3141
+ checkRsaKeyAlgorithm(key);
3142
+ return key.algorithm.name;
3143
+ case "ML-DSA-44":
3144
+ case "ML-DSA-65":
3145
+ case "ML-DSA-87":
3146
+ case "Ed25519":
3147
+ return key.algorithm.name;
3148
+ }
3149
+ throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
3150
+ }
3151
+ async function validateJwsSignature(protectedHeader, payload, key, signature) {
3152
+ const data = buf(`${protectedHeader}.${payload}`);
3153
+ const algorithm = keyToSubtle(key);
3154
+ const verified = await crypto.subtle.verify(algorithm, key, signature, data);
3155
+ if (!verified) {
3156
+ throw OPE("JWT signature verification failed", INVALID_RESPONSE, {
3157
+ key,
3158
+ data,
3159
+ signature,
3160
+ algorithm
3161
+ });
3162
+ }
3163
+ }
3164
+ async function validateJwt(jws, checkAlg, clockSkew2, clockTolerance2, decryptJwt) {
3165
+ let { 0: protectedHeader, 1: payload, length } = jws.split(".");
3166
+ if (length === 5) {
3167
+ if (decryptJwt !== undefined) {
3168
+ jws = await decryptJwt(jws);
3169
+ ({ 0: protectedHeader, 1: payload, length } = jws.split("."));
3170
+ } else {
3171
+ throw new UnsupportedOperationError("JWE decryption is not configured", { cause: jws });
3172
+ }
3173
+ }
3174
+ if (length !== 3) {
3175
+ throw OPE("Invalid JWT", INVALID_RESPONSE, jws);
3176
+ }
3177
+ let header;
3178
+ try {
3179
+ header = JSON.parse(buf(b64u(protectedHeader)));
3180
+ } catch (cause) {
3181
+ throw OPE("failed to parse JWT Header body as base64url encoded JSON", PARSE_ERROR, cause);
3182
+ }
3183
+ if (!isJsonObject(header)) {
3184
+ throw OPE("JWT Header must be a top level object", INVALID_RESPONSE, jws);
3185
+ }
3186
+ checkAlg(header);
3187
+ if (header.crit !== undefined) {
3188
+ throw new UnsupportedOperationError('no JWT "crit" header parameter extensions are supported', {
3189
+ cause: { header }
3190
+ });
3191
+ }
3192
+ let claims;
3193
+ try {
3194
+ claims = JSON.parse(buf(b64u(payload)));
3195
+ } catch (cause) {
3196
+ throw OPE("failed to parse JWT Payload body as base64url encoded JSON", PARSE_ERROR, cause);
3197
+ }
3198
+ if (!isJsonObject(claims)) {
3199
+ throw OPE("JWT Payload must be a top level object", INVALID_RESPONSE, jws);
3200
+ }
3201
+ const now = epochTime() + clockSkew2;
3202
+ if (claims.exp !== undefined) {
3203
+ if (typeof claims.exp !== "number") {
3204
+ throw OPE('unexpected JWT "exp" (expiration time) claim type', INVALID_RESPONSE, { claims });
3205
+ }
3206
+ if (claims.exp <= now - clockTolerance2) {
3207
+ throw OPE('unexpected JWT "exp" (expiration time) claim value, expiration is past current timestamp', JWT_TIMESTAMP_CHECK, { claims, now, tolerance: clockTolerance2, claim: "exp" });
3208
+ }
3209
+ }
3210
+ if (claims.iat !== undefined) {
3211
+ if (typeof claims.iat !== "number") {
3212
+ throw OPE('unexpected JWT "iat" (issued at) claim type', INVALID_RESPONSE, { claims });
3213
+ }
3214
+ }
3215
+ if (claims.iss !== undefined) {
3216
+ if (typeof claims.iss !== "string") {
3217
+ throw OPE('unexpected JWT "iss" (issuer) claim type', INVALID_RESPONSE, { claims });
3218
+ }
3219
+ }
3220
+ if (claims.nbf !== undefined) {
3221
+ if (typeof claims.nbf !== "number") {
3222
+ throw OPE('unexpected JWT "nbf" (not before) claim type', INVALID_RESPONSE, { claims });
3223
+ }
3224
+ if (claims.nbf > now + clockTolerance2) {
3225
+ throw OPE('unexpected JWT "nbf" (not before) claim value', JWT_TIMESTAMP_CHECK, {
3226
+ claims,
3227
+ now,
3228
+ tolerance: clockTolerance2,
3229
+ claim: "nbf"
3230
+ });
3231
+ }
3232
+ }
3233
+ if (claims.aud !== undefined) {
3234
+ if (typeof claims.aud !== "string" && !Array.isArray(claims.aud)) {
3235
+ throw OPE('unexpected JWT "aud" (audience) claim type', INVALID_RESPONSE, { claims });
3236
+ }
3237
+ }
3238
+ return { header, claims, jwt: jws };
3239
+ }
3240
+ function checkSigningAlgorithm(client, issuer, fallback, header) {
3241
+ if (client !== undefined) {
3242
+ if (typeof client === "string" ? header.alg !== client : !client.includes(header.alg)) {
3243
+ throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
3244
+ header,
3245
+ expected: client,
3246
+ reason: "client configuration"
3247
+ });
3248
+ }
3249
+ return;
3250
+ }
3251
+ if (Array.isArray(issuer)) {
3252
+ if (!issuer.includes(header.alg)) {
3253
+ throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
3254
+ header,
3255
+ expected: issuer,
3256
+ reason: "authorization server metadata"
3257
+ });
3258
+ }
3259
+ return;
3260
+ }
3261
+ if (fallback !== undefined) {
3262
+ if (typeof fallback === "string" ? header.alg !== fallback : typeof fallback === "function" ? !fallback(header.alg) : !fallback.includes(header.alg)) {
3263
+ throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
3264
+ header,
3265
+ expected: fallback,
3266
+ reason: "default value"
3267
+ });
3268
+ }
3269
+ return;
3270
+ }
3271
+ throw OPE('missing client or server configuration to verify used JWT "alg" header parameter', undefined, { client, issuer, fallback });
3272
+ }
3273
+ var skipStateCheck = Symbol();
3274
+ var expectNoState = Symbol();
3275
+ function algToSubtle(alg) {
3276
+ switch (alg) {
3277
+ case "PS256":
3278
+ case "PS384":
3279
+ case "PS512":
3280
+ return { name: "RSA-PSS", hash: `SHA-${alg.slice(-3)}` };
3281
+ case "RS256":
3282
+ case "RS384":
3283
+ case "RS512":
3284
+ return { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${alg.slice(-3)}` };
3285
+ case "ES256":
3286
+ case "ES384":
3287
+ return { name: "ECDSA", namedCurve: `P-${alg.slice(-3)}` };
3288
+ case "ES512":
3289
+ return { name: "ECDSA", namedCurve: "P-521" };
3290
+ case "EdDSA":
3291
+ return "Ed25519";
3292
+ case "Ed25519":
3293
+ case "ML-DSA-44":
3294
+ case "ML-DSA-65":
3295
+ case "ML-DSA-87":
3296
+ return alg;
3297
+ default:
3298
+ throw new UnsupportedOperationError("unsupported JWS algorithm", { cause: { alg } });
3299
+ }
3300
+ }
3301
+ async function importJwk(alg, jwk) {
3302
+ const { ext, key_ops, use, ...key } = jwk;
3303
+ return crypto.subtle.importKey("jwk", key, algToSubtle(alg), true, ["verify"]);
3304
+ }
3305
+ function normalizeHtu(htu) {
3306
+ const url = new URL(htu);
3307
+ url.search = "";
3308
+ url.hash = "";
3309
+ return url.href;
3310
+ }
3311
+ async function validateDPoP(request, accessToken, accessTokenClaims, options) {
3312
+ const headerValue = request.headers.get("dpop");
3313
+ if (headerValue === null) {
3314
+ throw OPE("operation indicated DPoP use but the request has no DPoP HTTP Header", INVALID_REQUEST, { headers: request.headers });
3315
+ }
3316
+ if (request.headers.get("authorization")?.toLowerCase().startsWith("dpop ") === false) {
3317
+ throw OPE(`operation indicated DPoP use but the request's Authorization HTTP Header scheme is not DPoP`, INVALID_REQUEST, { headers: request.headers });
3318
+ }
3319
+ if (typeof accessTokenClaims.cnf?.jkt !== "string") {
3320
+ throw OPE("operation indicated DPoP use but the JWT Access Token has no jkt confirmation claim", INVALID_REQUEST, { claims: accessTokenClaims });
3321
+ }
3322
+ const clockSkew2 = getClockSkew(options);
3323
+ const proof = await validateJwt(headerValue, checkSigningAlgorithm.bind(undefined, options?.signingAlgorithms, undefined, supported), clockSkew2, getClockTolerance(options), undefined).then(checkJwtType.bind(undefined, "dpop+jwt")).then(validatePresence.bind(undefined, ["iat", "jti", "ath", "htm", "htu"]));
3324
+ const now = epochTime() + clockSkew2;
3325
+ const diff = Math.abs(now - proof.claims.iat);
3326
+ if (diff > 300) {
3327
+ throw OPE("DPoP Proof iat is not recent enough", JWT_TIMESTAMP_CHECK, {
3328
+ now,
3329
+ claims: proof.claims,
3330
+ claim: "iat"
3331
+ });
3332
+ }
3333
+ if (proof.claims.htm !== request.method) {
3334
+ throw OPE("DPoP Proof htm mismatch", JWT_CLAIM_COMPARISON, {
3335
+ expected: request.method,
3336
+ claims: proof.claims,
3337
+ claim: "htm"
3338
+ });
3339
+ }
3340
+ if (typeof proof.claims.htu !== "string" || normalizeHtu(proof.claims.htu) !== normalizeHtu(request.url)) {
3341
+ throw OPE("DPoP Proof htu mismatch", JWT_CLAIM_COMPARISON, {
3342
+ expected: normalizeHtu(request.url),
3343
+ claims: proof.claims,
3344
+ claim: "htu"
3345
+ });
3346
+ }
3347
+ {
3348
+ const expected = b64u(await crypto.subtle.digest("SHA-256", buf(accessToken)));
3349
+ if (proof.claims.ath !== expected) {
3350
+ throw OPE("DPoP Proof ath mismatch", JWT_CLAIM_COMPARISON, {
3351
+ expected,
3352
+ claims: proof.claims,
3353
+ claim: "ath"
3354
+ });
3355
+ }
3356
+ }
3357
+ {
3358
+ const expected = await calculateJwkThumbprint(proof.header.jwk);
3359
+ if (accessTokenClaims.cnf.jkt !== expected) {
3360
+ throw OPE("JWT Access Token confirmation mismatch", JWT_CLAIM_COMPARISON, {
3361
+ expected,
3362
+ claims: accessTokenClaims,
3363
+ claim: "cnf.jkt"
3364
+ });
3365
+ }
3366
+ }
3367
+ const { 0: protectedHeader, 1: payload, 2: encodedSignature } = headerValue.split(".");
3368
+ const signature = b64u(encodedSignature);
3369
+ const { jwk, alg } = proof.header;
3370
+ if (!jwk) {
3371
+ throw OPE("DPoP Proof is missing the jwk header parameter", INVALID_REQUEST, {
3372
+ header: proof.header
3373
+ });
3374
+ }
3375
+ const key = await importJwk(alg, jwk);
3376
+ if (key.type !== "public") {
3377
+ throw OPE("DPoP Proof jwk header parameter must contain a public key", INVALID_REQUEST, {
3378
+ header: proof.header
3379
+ });
3380
+ }
3381
+ await validateJwsSignature(protectedHeader, payload, key, signature);
3382
+ }
3383
+ async function validateJwtAccessToken(as, request, expectedAudience, options) {
3384
+ assertAs(as);
3385
+ if (!looseInstanceOf(request, Request)) {
3386
+ throw CodedTypeError('"request" must be an instance of Request', ERR_INVALID_ARG_TYPE);
3387
+ }
3388
+ assertString(expectedAudience, '"expectedAudience"');
3389
+ const authorization = request.headers.get("authorization");
3390
+ if (authorization === null) {
3391
+ throw OPE('"request" is missing an Authorization HTTP Header', INVALID_REQUEST, {
3392
+ headers: request.headers
3393
+ });
3394
+ }
3395
+ let { 0: scheme, 1: accessToken, length } = authorization.split(" ");
3396
+ scheme = scheme.toLowerCase();
3397
+ switch (scheme) {
3398
+ case "dpop":
3399
+ case "bearer":
3400
+ break;
3401
+ default:
3402
+ throw new UnsupportedOperationError("unsupported Authorization HTTP Header scheme", {
3403
+ cause: { headers: request.headers }
3404
+ });
3405
+ }
3406
+ if (length !== 2) {
3407
+ throw OPE("invalid Authorization HTTP Header format", INVALID_REQUEST, {
3408
+ headers: request.headers
3409
+ });
3410
+ }
3411
+ const requiredClaims = [
3412
+ "iss",
3413
+ "exp",
3414
+ "aud",
3415
+ "sub",
3416
+ "iat",
3417
+ "jti",
3418
+ "client_id"
3419
+ ];
3420
+ if (options?.requireDPoP || scheme === "dpop" || request.headers.has("dpop")) {
3421
+ requiredClaims.push("cnf");
3422
+ }
3423
+ const { claims, header } = await validateJwt(accessToken, checkSigningAlgorithm.bind(undefined, options?.signingAlgorithms, undefined, supported), getClockSkew(options), getClockTolerance(options), undefined).then(checkJwtType.bind(undefined, "at+jwt")).then(validatePresence.bind(undefined, requiredClaims)).then(validateIssuer.bind(undefined, as)).then(validateAudience.bind(undefined, expectedAudience)).catch(reassignRSCode);
3424
+ for (const claim of ["client_id", "jti", "sub"]) {
3425
+ if (typeof claims[claim] !== "string") {
3426
+ throw OPE(`unexpected JWT "${claim}" claim type`, INVALID_REQUEST, { claims });
3427
+ }
3428
+ }
3429
+ if ("cnf" in claims) {
3430
+ if (!isJsonObject(claims.cnf)) {
3431
+ throw OPE('unexpected JWT "cnf" (confirmation) claim value', INVALID_REQUEST, { claims });
3432
+ }
3433
+ const { 0: cnf, length: length2 } = Object.keys(claims.cnf);
3434
+ if (length2) {
3435
+ if (length2 !== 1) {
3436
+ throw new UnsupportedOperationError("multiple confirmation claims are not supported", {
3437
+ cause: { claims }
3438
+ });
3439
+ }
3440
+ if (cnf !== "jkt") {
3441
+ throw new UnsupportedOperationError("unsupported JWT Confirmation method", {
3442
+ cause: { claims }
3443
+ });
3444
+ }
3445
+ }
3446
+ }
3447
+ const { 0: protectedHeader, 1: payload, 2: encodedSignature } = accessToken.split(".");
3448
+ const signature = b64u(encodedSignature);
3449
+ const key = await getPublicSigKeyFromIssuerJwksUri(as, options, header);
3450
+ await validateJwsSignature(protectedHeader, payload, key, signature);
3451
+ if (options?.requireDPoP || scheme === "dpop" || claims.cnf?.jkt !== undefined || request.headers.has("dpop")) {
3452
+ await validateDPoP(request, accessToken, claims, options).catch(reassignRSCode);
3453
+ }
3454
+ return claims;
3455
+ }
3456
+ function reassignRSCode(err) {
3457
+ if (err instanceof OperationProcessingError && err?.code === INVALID_REQUEST) {
3458
+ err.code = INVALID_RESPONSE;
3459
+ }
3460
+ throw err;
3461
+ }
3462
+ async function getResponseJsonBody(response, check = assertApplicationJson) {
3463
+ let json;
3464
+ try {
3465
+ json = await response.json();
3466
+ } catch (cause) {
3467
+ check(response);
3468
+ throw OPE('failed to parse "response" body as JSON', PARSE_ERROR, cause);
3469
+ }
3470
+ if (!isJsonObject(json)) {
3471
+ throw OPE('"response" body must be a top level object', INVALID_RESPONSE, { body: json });
3472
+ }
3473
+ return json;
3474
+ }
3475
+ var _nodiscoverycheck = Symbol();
3476
+ var _expectedIssuer = Symbol();
3477
+
3478
+ // src/http/jwt.ts
3479
+ function jwtAuthenticate(options) {
3480
+ const principalClaim = options.principalClaim ?? "sub";
3481
+ const domain = options.domain ?? "jwt";
3482
+ const audience = options.audience;
3483
+ let asPromise = null;
3484
+ async function getAuthorizationServer() {
3485
+ if (options.jwksUri) {
3486
+ return {
3487
+ issuer: options.issuer,
3488
+ jwks_uri: options.jwksUri
3489
+ };
3490
+ }
3491
+ const issuerUrl = new URL(options.issuer);
3492
+ const response = await discoveryRequest(issuerUrl);
3493
+ return processDiscoveryResponse(issuerUrl, response);
3494
+ }
3495
+ return async function authenticate(request) {
3496
+ if (!asPromise) {
3497
+ asPromise = getAuthorizationServer();
3498
+ }
3499
+ let as;
3500
+ try {
3501
+ as = await asPromise;
3502
+ } catch (error) {
3503
+ asPromise = null;
3504
+ throw error;
3505
+ }
3506
+ const claims = await validateJwtAccessToken(as, request, audience);
3507
+ const principal = claims[principalClaim] ?? null;
3508
+ return new AuthContext(domain, true, principal, claims);
3509
+ };
3510
+ }
2112
3511
  // src/protocol.ts
2113
3512
  import { Schema as Schema7 } from "@query-farm/apache-arrow";
2114
3513
 
@@ -2303,8 +3702,11 @@ async function dispatchStream(method, params, writer, reader, serverId, requestI
2303
3702
  if (expectedInputSchema && !isProducer && inputBatch.schema !== expectedInputSchema) {
2304
3703
  try {
2305
3704
  inputBatch = conformBatchToSchema(inputBatch, expectedInputSchema);
2306
- } catch {
2307
- throw new TypeError(`Input schema mismatch: expected ${expectedInputSchema}, got ${inputBatch.schema}`);
3705
+ } catch (e) {
3706
+ if (e instanceof TypeError) {
3707
+ throw e;
3708
+ }
3709
+ console.debug?.(`Schema conformance skipped: ${e instanceof Error ? e.message : e}`);
2308
3710
  }
2309
3711
  }
2310
3712
  const out = new OutputCollector(outputSchema, effectiveProducer, serverId, requestId);
@@ -2512,15 +3914,20 @@ export {
2512
3914
  subprocessConnect,
2513
3915
  str,
2514
3916
  pipeConnect,
3917
+ parseResourceMetadataUrl,
2515
3918
  parseDescribeResponse,
3919
+ oauthResourceMetadataToJson,
3920
+ jwtAuthenticate,
2516
3921
  jsonStateSerializer,
2517
3922
  int32,
2518
3923
  int,
2519
3924
  inferParamTypes,
3925
+ httpOAuthMetadata,
2520
3926
  httpIntrospect,
2521
3927
  httpConnect,
2522
3928
  float32,
2523
3929
  float,
3930
+ fetchOAuthMetadata,
2524
3931
  createHttpHandler,
2525
3932
  bytes,
2526
3933
  bool,
@@ -2545,7 +3952,8 @@ export {
2545
3952
  DESCRIBE_VERSION_KEY,
2546
3953
  DESCRIBE_VERSION,
2547
3954
  DESCRIBE_METHOD_NAME,
3955
+ AuthContext,
2548
3956
  ARROW_CONTENT_TYPE
2549
3957
  };
2550
3958
 
2551
- //# debugId=5E36C5DD1C64E65A64756E2164756E21
3959
+ //# debugId=FCA96ECA4C5D644864756E2164756E21