@datalayer/personal-agent-protocol 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +141 -14
  3. package/lib/auth.d.ts +35 -1
  4. package/lib/auth.d.ts.map +1 -1
  5. package/lib/auth.js +230 -2
  6. package/lib/auth.js.map +1 -1
  7. package/lib/crypto.d.ts +1 -1
  8. package/lib/crypto.d.ts.map +1 -1
  9. package/lib/crypto.js +1 -2
  10. package/lib/crypto.js.map +1 -1
  11. package/lib/discovery.d.ts +1 -0
  12. package/lib/discovery.d.ts.map +1 -1
  13. package/lib/discovery.js +4 -3
  14. package/lib/discovery.js.map +1 -1
  15. package/lib/errors.d.ts +7 -0
  16. package/lib/errors.d.ts.map +1 -1
  17. package/lib/errors.js +6 -0
  18. package/lib/errors.js.map +1 -1
  19. package/lib/identity.d.ts +19 -0
  20. package/lib/identity.d.ts.map +1 -0
  21. package/lib/identity.js +111 -0
  22. package/lib/identity.js.map +1 -0
  23. package/lib/index.d.ts +8 -6
  24. package/lib/index.d.ts.map +1 -1
  25. package/lib/index.js +667 -656
  26. package/lib/index.js.map +4 -4
  27. package/lib/models.d.ts +15 -0
  28. package/lib/models.d.ts.map +1 -1
  29. package/lib/models.js +53 -0
  30. package/lib/models.js.map +1 -1
  31. package/lib/reactor.d.ts +16 -1
  32. package/lib/reactor.d.ts.map +1 -1
  33. package/lib/reactor.js +5 -1
  34. package/lib/reactor.js.map +1 -1
  35. package/lib/secret.d.ts +9 -0
  36. package/lib/secret.d.ts.map +1 -0
  37. package/lib/secret.js +21 -0
  38. package/lib/secret.js.map +1 -0
  39. package/lib/server.d.ts +15 -0
  40. package/lib/server.d.ts.map +1 -0
  41. package/lib/server.js +74 -0
  42. package/lib/server.js.map +1 -0
  43. package/lib/sessions.d.ts +6 -7
  44. package/lib/sessions.d.ts.map +1 -1
  45. package/lib/sessions.js +6 -19
  46. package/lib/sessions.js.map +1 -1
  47. package/package.json +14 -1
package/lib/index.js CHANGED
@@ -26,6 +26,12 @@ var DiscoveryError = class extends PersonalAgentProtocolError {
26
26
  this.name = "DiscoveryError";
27
27
  }
28
28
  };
29
+ var IdentityError = class extends PersonalAgentProtocolError {
30
+ constructor(code, message2, options = {}) {
31
+ super(code, message2, options);
32
+ this.name = "IdentityError";
33
+ }
34
+ };
29
35
  var AuthorizationError = class extends PersonalAgentProtocolError {
30
36
  constructor(code, message2, options = {}) {
31
37
  super(code, message2, options);
@@ -45,7 +51,7 @@ var ProofError = class extends PersonalAgentProtocolError {
45
51
  }
46
52
  };
47
53
 
48
- // ../../node_modules/jose/dist/webapi/lib/buffer_utils.js
54
+ // node_modules/jose/dist/webapi/lib/buffer_utils.js
49
55
  var encoder = new TextEncoder();
50
56
  var decoder = new TextDecoder();
51
57
  var strictDecoder = new TextDecoder("utf-8", { fatal: true });
@@ -95,12 +101,8 @@ function decodeBase64(encoded, url = false) {
95
101
  bytes[i] = binary.charCodeAt(i);
96
102
  return bytes;
97
103
  }
98
- async function digest(algorithm2, data) {
99
- const subtleDigest = `SHA-${algorithm2.slice(-3)}`;
100
- return new Uint8Array(await crypto.subtle.digest(subtleDigest, data));
101
- }
102
104
 
103
- // ../../node_modules/jose/dist/webapi/util/errors.js
105
+ // node_modules/jose/dist/webapi/util/errors.js
104
106
  var JOSEError = class extends Error {
105
107
  static code = "ERR_JOSE_GENERIC";
106
108
  code = "ERR_JOSE_GENERIC";
@@ -108,30 +110,6 @@ var JOSEError = class extends Error {
108
110
  super(message2, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);
109
111
  }
110
112
  };
111
- var JWTClaimValidationFailed = class extends JOSEError {
112
- static code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
113
- code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
114
- claim;
115
- reason;
116
- payload;
117
- constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
118
- super(message2, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;
119
- }
120
- };
121
- var JWTExpired = class extends JOSEError {
122
- static code = "ERR_JWT_EXPIRED";
123
- code = "ERR_JWT_EXPIRED";
124
- claim;
125
- reason;
126
- payload;
127
- constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
128
- super(message2, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;
129
- }
130
- };
131
- var JOSEAlgNotAllowed = class extends JOSEError {
132
- static code = "ERR_JOSE_ALG_NOT_ALLOWED";
133
- code = "ERR_JOSE_ALG_NOT_ALLOWED";
134
- };
135
113
  var JOSENotSupported = class extends JOSEError {
136
114
  static code = "ERR_JOSE_NOT_SUPPORTED";
137
115
  code = "ERR_JOSE_NOT_SUPPORTED";
@@ -144,19 +122,8 @@ var JWTInvalid = class extends JOSEError {
144
122
  static code = "ERR_JWT_INVALID";
145
123
  code = "ERR_JWT_INVALID";
146
124
  };
147
- var JWKInvalid = class extends JOSEError {
148
- static code = "ERR_JWK_INVALID";
149
- code = "ERR_JWK_INVALID";
150
- };
151
- var JWSSignatureVerificationFailed = class extends JOSEError {
152
- static code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
153
- code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
154
- constructor(message2 = "signature verification failed", options) {
155
- super(message2, options);
156
- }
157
- };
158
125
 
159
- // ../../node_modules/jose/dist/webapi/util/base64url.js
126
+ // node_modules/jose/dist/webapi/util/base64url.js
160
127
  var invalid = "The input to be decoded is not correctly encoded.";
161
128
  function decode(input) {
162
129
  try {
@@ -169,7 +136,7 @@ function encode2(input) {
169
136
  return encodeBase64(typeof input == "string" ? encoder.encode(input) : input, true);
170
137
  }
171
138
 
172
- // ../../node_modules/jose/dist/webapi/lib/validate.js
139
+ // node_modules/jose/dist/webapi/lib/validate.js
173
140
  function isObject(input) {
174
141
  if (typeof input != "object" || input === null || Object.prototype.toString.call(input) !== "[object Object]")
175
142
  return false;
@@ -191,37 +158,7 @@ function assertNotSet(value, name) {
191
158
  if (value !== void 0)
192
159
  throw new TypeError(`${name} can only be called once`);
193
160
  }
194
- function decodeBase64url(value, label, ErrorClass) {
195
- try {
196
- return decode(value);
197
- } catch {
198
- throw new ErrorClass(`Failed to base64url decode the ${label}`);
199
- }
200
- }
201
- function encodeBase64url(value, label, ErrorClass) {
202
- try {
203
- return encode(value);
204
- } catch {
205
- throw new ErrorClass(`The ${label} is not a valid base64url string`);
206
- }
207
- }
208
- function parseJoseHeader(b64, ErrorClass, message2) {
209
- let parsed;
210
- try {
211
- parsed = JSON.parse(strictDecoder.decode(decode(b64)));
212
- } catch {
213
- throw new ErrorClass(message2);
214
- }
215
- if (!isObject(parsed))
216
- throw new ErrorClass(message2);
217
- return parsed;
218
- }
219
161
  var JWS_RECOGNIZED = { __proto__: null, b64: true };
220
- function validateAlgorithms(option, algorithms) {
221
- if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s != "string")))
222
- throw new TypeError(`"${option}" option must be an array of strings`);
223
- return algorithms === void 0 ? void 0 : new Set(algorithms);
224
- }
225
162
  function validateCritDuplicates(Err, protectedHeader) {
226
163
  const { crit } = protectedHeader ?? {};
227
164
  if (Array.isArray(crit) && new Set(crit).size !== crit.length)
@@ -266,7 +203,7 @@ function serializeJoseHeader(Err, header) {
266
203
  return [parsed, serialized];
267
204
  }
268
205
 
269
- // ../../node_modules/jose/dist/webapi/lib/key.js
206
+ // node_modules/jose/dist/webapi/lib/key.js
270
207
  var tag = (key) => key[Symbol.toStringTag];
271
208
  var jwkMatchesOp = (entry, key, usage) => {
272
209
  const { alg } = entry;
@@ -346,7 +283,6 @@ function message(msg, actual, ...types) {
346
283
  } else types.length === 2 ? msg += `one of type ${types[0]} or ${types[1]}.` : msg += `of type ${types[0]}.`;
347
284
  return actual == null ? msg += ` Received ${actual}` : typeof actual == "function" && actual.name ? msg += ` Received function ${actual.name}` : typeof actual == "object" && actual != null && actual.constructor?.name && (msg += ` Received an instance of ${actual.constructor.name}`), msg;
348
285
  }
349
- var invalidKeyInput = (actual, ...types) => message("Key must be ", actual, ...types);
350
286
  function invalidKeyType(alg, actual, secret) {
351
287
  const types = ["CryptoKey", "KeyObject", "JSON Web Key"];
352
288
  return secret && types.push("Uint8Array"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));
@@ -388,11 +324,6 @@ function normalizeJwk(jwk) {
388
324
  }
389
325
  return normalized;
390
326
  }
391
- function validateExtractableOption(extractable) {
392
- if (extractable !== void 0 && typeof extractable != "boolean")
393
- throw new TypeError('"extractable" option must be a boolean');
394
- return extractable;
395
- }
396
327
  async function jwkToKey(entry, jwk, extractable) {
397
328
  if (!entry.kty.includes(jwk.kty))
398
329
  throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
@@ -403,7 +334,7 @@ async function rawKey(key, expected, usage, extractable = false) {
403
334
  return key instanceof Uint8Array && (key = await crypto.subtle.importKey("raw", key, expected, extractable, [usage])), checkCryptoKey(key, expected, usage), key;
404
335
  }
405
336
 
406
- // ../../node_modules/jose/dist/webapi/lib/key_descriptor.js
337
+ // node_modules/jose/dist/webapi/lib/key_descriptor.js
407
338
  function table(entries) {
408
339
  const out = { __proto__: null };
409
340
  for (const alg in entries)
@@ -411,108 +342,7 @@ function table(entries) {
411
342
  return out;
412
343
  }
413
344
 
414
- // ../../node_modules/jose/dist/webapi/lib/jwe_algorithms.js
415
- var wrap = [
416
- ["encrypt", "wrapKey"],
417
- ["decrypt", "unwrapKey"]
418
- ];
419
- var derive = [[], ["deriveBits"]];
420
- var none = [[], []];
421
- function rsaes(bits) {
422
- return {
423
- kty: ["RSA"],
424
- mode: "key-encryption",
425
- subtle: { name: "RSA-OAEP", hash: `SHA-${bits}` },
426
- usages: wrap,
427
- ops: ["wrapKey", "unwrapKey"]
428
- };
429
- }
430
- function ecdh(mode) {
431
- return {
432
- kty: ["EC", "OKP"],
433
- mode,
434
- subtle: { name: "ECDH" },
435
- resolve: ({ kty, crv, asymmetricKeyType }) => {
436
- if (crv === "X25519" || asymmetricKeyType === "x25519")
437
- return { name: "X25519" };
438
- if (kty === "OKP")
439
- throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
440
- return { name: "ECDH", namedCurve: crv };
441
- },
442
- usages: derive,
443
- ops: [void 0, "deriveBits"]
444
- };
445
- }
446
- function aeskw(bits, gcm = false) {
447
- return {
448
- kty: ["oct"],
449
- mode: "key-wrapping",
450
- secret: true,
451
- subtle: { name: gcm ? "AES-GCM" : "AES-KW", length: bits },
452
- usages: none,
453
- ops: gcm ? ["encrypt", "decrypt"] : ["wrapKey", "unwrapKey"]
454
- };
455
- }
456
- function pbes2() {
457
- return {
458
- kty: ["oct"],
459
- mode: "key-wrapping",
460
- secret: true,
461
- subtle: { name: "PBKDF2" },
462
- usages: none,
463
- ops: ["deriveBits", "deriveBits"]
464
- };
465
- }
466
- var JWE = table({
467
- dir: {
468
- kty: ["oct"],
469
- mode: "direct-encryption",
470
- secret: true,
471
- subtle: { name: "AES-GCM" },
472
- usages: none,
473
- ops: ["encrypt", "decrypt"]
474
- },
475
- "RSA-OAEP": rsaes(1),
476
- "RSA-OAEP-256": rsaes(256),
477
- "RSA-OAEP-384": rsaes(384),
478
- "RSA-OAEP-512": rsaes(512),
479
- "ECDH-ES": ecdh("direct-key-agreement"),
480
- "ECDH-ES+A128KW": ecdh("key-agreement-with-key-wrapping"),
481
- "ECDH-ES+A192KW": ecdh("key-agreement-with-key-wrapping"),
482
- "ECDH-ES+A256KW": ecdh("key-agreement-with-key-wrapping"),
483
- A128KW: aeskw(128),
484
- A192KW: aeskw(192),
485
- A256KW: aeskw(256),
486
- A128GCMKW: aeskw(128, true),
487
- A192GCMKW: aeskw(192, true),
488
- A256GCMKW: aeskw(256, true),
489
- "PBES2-HS256+A128KW": pbes2(),
490
- "PBES2-HS384+A192KW": pbes2(),
491
- "PBES2-HS512+A256KW": pbes2()
492
- });
493
- var contentOps = ["encrypt", "decrypt"];
494
- function contentEncryption(bits, cbc = false) {
495
- return {
496
- kty: ["oct"],
497
- secret: true,
498
- subtle: { name: cbc ? "AES-CBC" : "AES-GCM", length: bits },
499
- usages: none,
500
- ops: contentOps,
501
- cekBits: bits,
502
- ivBits: cbc ? 128 : 96,
503
- cbc
504
- };
505
- }
506
- var ENC = table({
507
- A128GCM: contentEncryption(128),
508
- A192GCM: contentEncryption(192),
509
- A256GCM: contentEncryption(256),
510
- "A128CBC-HS256": contentEncryption(256, true),
511
- "A192CBC-HS384": contentEncryption(384, true),
512
- "A256CBC-HS512": contentEncryption(512, true)
513
- });
514
-
515
- // ../../node_modules/jose/dist/webapi/lib/jws_algorithms.js
345
+ // node_modules/jose/dist/webapi/lib/jws_algorithms.js
516
346
  var sig = [["verify"], ["sign"]];
517
347
  function hmac(bits) {
518
348
  const subtle = { name: "HMAC", hash: `SHA-${bits}` };
@@ -582,59 +412,7 @@ function jwsAlgorithm(alg) {
582
412
  return entry;
583
413
  }
584
414
 
585
- // ../../node_modules/jose/dist/webapi/lib/jws_verify.js
586
- function prepareVerify(options) {
587
- return [options && validateAlgorithms("algorithms", options.algorithms), options?.crit];
588
- }
589
- function parseProtectedHeader(encodedProtected) {
590
- return encodedProtected === void 0 ? {} : parseJoseHeader(encodedProtected, JWSInvalid, "JWS Protected Header is invalid");
591
- }
592
- function encodeCompactUnencodedPayload(payload) {
593
- try {
594
- return encode(payload);
595
- } catch {
596
- throw new JWSInvalid("JWS Compact Serialization payload must use only ASCII characters");
597
- }
598
- }
599
- async function verifySignature(jws, shared, key, encodeUnencodedPayload, parsedProtected) {
600
- const { protected: encodedProtected, header, payload: inputPayload } = jws, parsedProt = parsedProtected ?? parseProtectedHeader(encodedProtected);
601
- if (!isDisjoint(parsedProt, header))
602
- throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
603
- const joseHeader = { ...parsedProt, ...header }, b64 = validateB64(parsedProt, validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader)), { alg } = joseHeader;
604
- if (typeof alg != "string" || !alg)
605
- throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
606
- if (shared[0] && !shared[0].has(alg))
607
- throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
608
- if (b64) {
609
- if (typeof inputPayload != "string")
610
- throw new JWSInvalid("JWS Payload must be a string");
611
- } else if (typeof inputPayload != "string" && !(inputPayload instanceof Uint8Array))
612
- throw new JWSInvalid("JWS Payload must be a string or an Uint8Array instance");
613
- const signingPayload = b64 || typeof inputPayload != "string" ? inputPayload : encodeUnencodedPayload(inputPayload);
614
- let resolvedKey = false;
615
- typeof key == "function" && (key = await key(parsedProt, jws), resolvedKey = true);
616
- const entry = jwsAlgorithm(alg), data = concat(encodedProtected !== void 0 ? encode(encodedProtected) : new Uint8Array(), encode("."), typeof signingPayload == "string" ? shared[2] ??= encodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload), signature = decodeBase64url(jws.signature, "signature", JWSInvalid), k = await prepareKey(entry, key, "verify"), cryptoKey = await rawKey(k, entry.subtle, "verify");
617
- entry.minRsaBits && checkModulusLength(entry.alg, cryptoKey);
618
- let verified = false;
619
- try {
620
- verified = await crypto.subtle.verify(entry.signing, cryptoKey, signature, data);
621
- } catch {
622
- }
623
- if (!verified)
624
- throw new JWSSignatureVerificationFailed();
625
- const result = { payload: typeof signingPayload == "string" ? decodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload };
626
- return encodedProtected !== void 0 && (result.protectedHeader = parsedProt), header !== void 0 && (result.unprotectedHeader = header), resolvedKey ? [{ ...result, key: k }, b64] : [result, b64];
627
- }
628
- async function verifyCompact(jws, shared, key) {
629
- if (jws instanceof Uint8Array && (jws = decoder.decode(jws)), typeof jws != "string")
630
- throw new JWSInvalid("Compact JWS must be a string or Uint8Array");
631
- const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split(".");
632
- if (length !== 3)
633
- throw new JWSInvalid("Invalid Compact JWS");
634
- return verifySignature({ payload, protected: protectedHeader, signature }, shared, key, encodeCompactUnencodedPayload);
635
- }
636
-
637
- // ../../node_modules/jose/dist/webapi/lib/jwt_claims_set.js
415
+ // node_modules/jose/dist/webapi/lib/jwt_claims_set.js
638
416
  var epoch = (date) => Math.floor(date.getTime() / 1e3);
639
417
  var multipliers = {
640
418
  s: 1,
@@ -645,7 +423,6 @@ var multipliers = {
645
423
  y: 31557600
646
424
  };
647
425
  var REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
648
- var checkFailed = "check_failed";
649
426
  function invalidDuration() {
650
427
  throw new TypeError("Invalid time period format");
651
428
  }
@@ -672,64 +449,6 @@ function validateAudienceClaim(value) {
672
449
  function numericDate(value, label) {
673
450
  return typeof value == "number" ? validateInput(label, value) : value instanceof Date ? validateInput(label, epoch(value)) : epoch(/* @__PURE__ */ new Date()) + secs(value);
674
451
  }
675
- var normalizeTyp = (value) => {
676
- const normalized = value.toLowerCase();
677
- return value.includes("/") ? normalized : `application/${normalized}`;
678
- };
679
- var checkAudiencePresence = (audPayload, audOption) => typeof audPayload == "string" ? audOption.includes(audPayload) : Array.isArray(audPayload) ? audOption.some((aud) => audPayload.includes(aud)) : false;
680
- function validateNumericDate(payload, claim, required = false) {
681
- const value = payload[claim];
682
- if (!(value === void 0 && !required)) {
683
- if (typeof value != "number")
684
- throw new JWTClaimValidationFailed(`"${claim}" claim must be a number`, payload, claim, "invalid");
685
- return value;
686
- }
687
- }
688
- function unexpectedClaim(payload, claim) {
689
- throw new JWTClaimValidationFailed(`unexpected "${claim}" claim value`, payload, claim, checkFailed);
690
- }
691
- function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
692
- let payload;
693
- try {
694
- payload = JSON.parse(strictDecoder.decode(encodedPayload));
695
- } catch {
696
- }
697
- if (!isObject(payload))
698
- throw new JWTInvalid("JWT Claims Set must be a top-level JSON object");
699
- const { typ } = options;
700
- if (typ !== void 0 && (typeof protectedHeader.typ != "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ)))
701
- throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, "typ", checkFailed);
702
- const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options, presenceCheck = [...requiredClaims];
703
- maxTokenAge !== void 0 && presenceCheck.push("iat"), audience !== void 0 && presenceCheck.push("aud"), subject !== void 0 && presenceCheck.push("sub"), issuer !== void 0 && presenceCheck.push("iss");
704
- for (const claim of new Set(presenceCheck.reverse()))
705
- if (!Object.hasOwn(payload, claim))
706
- throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, "missing");
707
- issuer !== void 0 && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss) && unexpectedClaim(payload, "iss"), subject !== void 0 && payload.sub !== subject && unexpectedClaim(payload, "sub"), audience !== void 0 && !checkAudiencePresence(payload.aud, typeof audience == "string" ? [audience] : audience) && unexpectedClaim(payload, "aud");
708
- const { clockTolerance } = options;
709
- let tolerance = 0;
710
- if (typeof clockTolerance == "string")
711
- tolerance = secs(clockTolerance);
712
- else if (clockTolerance !== void 0) {
713
- if (typeof clockTolerance != "number")
714
- throw new TypeError("Invalid clockTolerance option type");
715
- tolerance = clockTolerance;
716
- }
717
- validateInput("clockTolerance option", tolerance);
718
- const { currentDate } = options, now = validateInput("currentDate option", epoch(currentDate === void 0 ? /* @__PURE__ */ new Date() : currentDate)), iat = validateNumericDate(payload, "iat", maxTokenAge !== void 0), nbf = validateNumericDate(payload, "nbf");
719
- if (nbf !== void 0 && nbf > now + tolerance)
720
- throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, "nbf", checkFailed);
721
- const exp = validateNumericDate(payload, "exp");
722
- if (exp !== void 0 && exp <= now - tolerance)
723
- throw new JWTExpired('"exp" claim timestamp check failed', payload, "exp", checkFailed);
724
- if (maxTokenAge !== void 0) {
725
- const age = now - iat, max = validateInput("maxTokenAge option", typeof maxTokenAge == "number" ? maxTokenAge : secs(maxTokenAge));
726
- if (age - tolerance > max)
727
- throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, "iat", checkFailed);
728
- if (age < -tolerance)
729
- throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, "iat", checkFailed);
730
- }
731
- return payload;
732
- }
733
452
  var producerPayloads;
734
453
  function producerPayload(producer) {
735
454
  return producerPayloads.get(producer);
@@ -773,16 +492,7 @@ var JWTClaimsBuilder = class {
773
492
  }
774
493
  };
775
494
 
776
- // ../../node_modules/jose/dist/webapi/jwt/verify.js
777
- async function jwtVerify(jwt, key, options) {
778
- const [verified, b64] = await verifyCompact(jwt, prepareVerify(options), key);
779
- if (!b64)
780
- throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
781
- const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);
782
- return { ...verified, payload };
783
- }
784
-
785
- // ../../node_modules/jose/dist/webapi/lib/jws_sign.js
495
+ // node_modules/jose/dist/webapi/lib/jws_sign.js
786
496
  async function createSignature(input, key, rejectUnencoded) {
787
497
  let [payload, protectedHeader, unprotectedHeader, crit] = input, protectedHeaderString = "";
788
498
  if (protectedHeader !== void 0) {
@@ -820,7 +530,7 @@ async function createCompactSignature(payload, protectedHeader, crit, key, rejec
820
530
  return `${jws.protected}.${jws.payload}.${jws.signature}`;
821
531
  }
822
532
 
823
- // ../../node_modules/jose/dist/webapi/jwt/sign.js
533
+ // node_modules/jose/dist/webapi/jwt/sign.js
824
534
  var SignJWT_base = JWTClaimsBuilder;
825
535
  var SignJWT = class extends SignJWT_base {
826
536
  #protectedHeader;
@@ -834,123 +544,95 @@ var SignJWT = class extends SignJWT_base {
834
544
  }
835
545
  };
836
546
 
837
- // ../../node_modules/jose/dist/webapi/lib/key_algorithm.js
838
- function unsupportedAlg(source = 'JWK "alg" (Algorithm) Parameter') {
839
- throw new JOSENotSupported(`Invalid or unsupported ${source} value`);
547
+ // src/crypto.ts
548
+ var privateJwkMembers = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth", "k"]);
549
+ function algorithm(value) {
550
+ if (value !== "ES256" && value !== "RS256") {
551
+ throw new TypeError("PAP JOSE algorithms must be ES256 or RS256");
552
+ }
553
+ return value;
840
554
  }
841
- function keyAlgorithm(alg, source) {
842
- return (typeof alg == "string" ? JWS[alg] ?? JWE[alg] : void 0) ?? unsupportedAlg(source);
555
+ function publicJwk(value, alg) {
556
+ for (const member of privateJwkMembers) {
557
+ if (member in value) {
558
+ throw new TypeError("DPoP public JWK must not contain private key material");
559
+ }
560
+ }
561
+ const expected = alg === "ES256" ? "EC" : "RSA";
562
+ if (value.kty !== expected) throw new TypeError(`${alg} requires a ${expected} public JWK`);
563
+ if (alg === "ES256") {
564
+ if (value.crv !== "P-256" || !value.x || !value.y) {
565
+ throw new TypeError("ES256 requires a complete P-256 public JWK");
566
+ }
567
+ } else if (!value.n || !value.e) {
568
+ throw new TypeError("RS256 requires a complete RSA public JWK");
569
+ }
570
+ return { ...value };
843
571
  }
844
-
845
- // ../../node_modules/jose/dist/webapi/key/export.js
846
- async function exportJWK(key) {
847
- if (isKeyObject(key))
848
- if (key.type === "secret")
849
- key = key.export();
850
- else
851
- return key.export({ format: "jwk" });
852
- if (key instanceof Uint8Array)
853
- return {
854
- kty: "oct",
855
- k: encode2(key)
856
- };
857
- if (!isCryptoKey(key))
858
- throw new TypeError(invalidKeyInput(key, "CryptoKey", "KeyObject", "Uint8Array"));
859
- if (!key.extractable)
860
- throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");
861
- const jwk = await crypto.subtle.exportKey("jwk", key);
862
- delete jwk.ext, delete jwk.key_ops, delete jwk.use, jwk.kty !== "AKP" && delete jwk.alg;
863
- for (const parameter of Object.keys(jwk))
864
- jwk[parameter] === void 0 && delete jwk[parameter];
865
- return jwk;
572
+ function normalizedHtu(value) {
573
+ let url;
574
+ try {
575
+ url = new URL(value);
576
+ } catch (cause) {
577
+ throw new ProofError("invalid_htu", "DPoP target must be an HTTPS URL", { cause });
578
+ }
579
+ if (url.protocol !== "https:" || url.username || url.password) {
580
+ throw new ProofError("invalid_htu", "DPoP target must be an HTTPS URL");
581
+ }
582
+ url.search = "";
583
+ url.hash = "";
584
+ return url.href;
866
585
  }
867
-
868
- // ../../node_modules/jose/dist/webapi/jwk/thumbprint.js
869
- var check = (value, description) => {
870
- if (typeof value != "string" || !value)
871
- throw new JWKInvalid(`${description} missing or invalid`);
872
- };
873
- async function calculateJwkThumbprint(key, digestAlgorithm) {
874
- let jwk;
875
- if (isObject(key)) {
876
- if (jwk = snapshotJwk(key), typeof jwk.kty != "string")
877
- throw new TypeError(invalidKeyInput(key, "CryptoKey", "KeyObject", "JSON Web Key"));
878
- } else if (isKeyLike(key))
879
- jwk = snapshotJwk(await exportJWK(key));
880
- else
881
- throw new TypeError(invalidKeyInput(key, "CryptoKey", "KeyObject", "JSON Web Key"));
882
- if (digestAlgorithm ??= "sha256", digestAlgorithm !== "sha256" && digestAlgorithm !== "sha384" && digestAlgorithm !== "sha512")
883
- throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
884
- let components;
885
- switch (jwk.kty) {
886
- case "AKP":
887
- check(jwk.alg, '"alg" (Algorithm) Parameter'), check(jwk.pub, '"pub" (Public key) Parameter'), components = { alg: jwk.alg, kty: jwk.kty, pub: jwk.pub };
888
- break;
889
- case "EC":
890
- check(jwk.crv, '"crv" (Curve) Parameter'), check(jwk.x, '"x" (X Coordinate) Parameter'), check(jwk.y, '"y" (Y Coordinate) Parameter'), components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
891
- break;
892
- case "OKP":
893
- check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter'), check(jwk.x, '"x" (Public Key) Parameter'), components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
894
- break;
895
- case "RSA":
896
- check(jwk.e, '"e" (Exponent) Parameter'), check(jwk.n, '"n" (Modulus) Parameter'), components = { e: jwk.e, kty: jwk.kty, n: jwk.n };
897
- break;
898
- case "oct":
899
- if (typeof jwk.k != "string")
900
- throw new JWKInvalid('"k" (Key Value) Parameter missing or invalid');
901
- components = { k: jwk.k, kty: jwk.kty };
902
- break;
903
- default:
904
- throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported');
905
- }
906
- const data = encode(JSON.stringify(components));
907
- return encode2(await digest(digestAlgorithm, data));
586
+ function base64Url(bytes) {
587
+ let binary = "";
588
+ for (const value of bytes) binary += String.fromCharCode(value);
589
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "");
908
590
  }
909
-
910
- // ../../node_modules/jose/dist/webapi/key/import.js
911
- async function importJWK(jwk, alg, options) {
912
- if (!isObject(jwk))
913
- throw new TypeError("JWK must be an object");
914
- const normalized = normalizeJwk(jwk), extractable = validateExtractableOption(options?.extractable), { alg: jwkAlg } = normalized;
915
- if (alg ??= jwkAlg, normalized.kty !== "oct" && !alg)
916
- throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
917
- switch (normalized.kty) {
918
- case "oct":
919
- if (typeof normalized.k != "string")
920
- throw new TypeError('missing "k" (Key Value) Parameter value');
921
- return decode(normalized.k);
922
- case "AKP": {
923
- if (typeof jwkAlg != "string" || !jwkAlg)
924
- throw new TypeError('missing "alg" (Algorithm) Parameter value');
925
- if (alg !== jwkAlg)
926
- throw new TypeError("JWK alg and alg option value mismatch");
927
- return jwkToKey(keyAlgorithm(alg), normalized, extractable);
928
- }
929
- case "RSA":
930
- case "EC":
931
- case "OKP":
932
- return jwkToKey(keyAlgorithm(alg), normalized, extractable);
933
- default:
934
- throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value');
935
- }
591
+ function randomBytes(length) {
592
+ if (!globalThis.crypto) throw new ProofError("crypto_unavailable", "Web Crypto is required");
593
+ return globalThis.crypto.getRandomValues(new Uint8Array(length));
936
594
  }
937
-
938
- // ../../node_modules/jose/dist/webapi/util/decode_protected_header.js
939
- function decodeProtectedHeader(token) {
940
- let protectedB64u;
941
- if (typeof token == "string") {
942
- const parts = token.split(".");
943
- (parts.length === 3 || parts.length === 5) && ([protectedB64u] = parts);
944
- } else if (typeof token == "object" && token)
945
- if ("protected" in token)
946
- protectedB64u = token.protected;
947
- else
948
- throw new TypeError("Token does not contain a Protected Header");
949
- const invalid2 = "Invalid Token or Protected Header formatting";
950
- if (typeof protectedB64u != "string" || !protectedB64u)
951
- throw new TypeError(invalid2);
952
- return parseJoseHeader(protectedB64u, TypeError, invalid2);
595
+ async function accessTokenHash(token) {
596
+ if (!globalThis.crypto) throw new ProofError("crypto_unavailable", "Web Crypto is required");
597
+ const digest = await globalThis.crypto.subtle.digest(
598
+ "SHA-256",
599
+ new TextEncoder().encode(token.reveal())
600
+ );
601
+ return base64Url(new Uint8Array(digest));
953
602
  }
603
+ var JoseDpopProofProvider = class {
604
+ #resolveKey;
605
+ #now;
606
+ #randomBytes;
607
+ constructor(options) {
608
+ this.#resolveKey = options.resolveKey;
609
+ this.#now = options.now ?? (() => /* @__PURE__ */ new Date());
610
+ this.#randomBytes = options.randomBytes ?? randomBytes;
611
+ }
612
+ async create(request) {
613
+ const material = await this.#resolveKey(request);
614
+ const alg = algorithm(material.algorithm ?? "ES256");
615
+ const jwk = publicJwk(material.publicJwk, alg);
616
+ const entropy2 = this.#randomBytes(16);
617
+ if (entropy2.byteLength < 16) {
618
+ throw new ProofError("weak_randomness", "DPoP proof IDs require 128 random bits");
619
+ }
620
+ const claims = {
621
+ jti: base64Url(entropy2),
622
+ htm: request.method.toUpperCase(),
623
+ htu: normalizedHtu(request.url),
624
+ iat: Math.floor(this.#now().getTime() / 1e3),
625
+ ...request.nonce === void 0 ? {} : { nonce: request.nonce },
626
+ ...request.accessToken === void 0 ? {} : { ath: await accessTokenHash(request.accessToken) }
627
+ };
628
+ try {
629
+ const token = await new SignJWT(claims).setProtectedHeader({ typ: "dpop+jwt", alg, jwk }).sign(material.privateKey);
630
+ return new SecretValue(token);
631
+ } catch (cause) {
632
+ throw new ProofError("signing_failed", "Could not sign DPoP proof", { cause });
633
+ }
634
+ }
635
+ };
954
636
 
955
637
  // src/json.ts
956
638
  function parseJsonStrict(source) {
@@ -1088,7 +770,27 @@ var StrictJsonParser = class {
1088
770
  }
1089
771
  };
1090
772
 
1091
- // ../../node_modules/@datalayer/reactor/lib/core/contributions.js
773
+ // src/secret.ts
774
+ var SecretValue = class {
775
+ #value;
776
+ constructor(value) {
777
+ if (!value) {
778
+ throw new ProtocolValidationError("invalid_secret", "Secret value must not be empty");
779
+ }
780
+ this.#value = value;
781
+ }
782
+ reveal() {
783
+ return this.#value;
784
+ }
785
+ toString() {
786
+ return "[REDACTED]";
787
+ }
788
+ toJSON() {
789
+ return "[REDACTED]";
790
+ }
791
+ };
792
+
793
+ // node_modules/@datalayer/reactor/lib/core/contributions.js
1092
794
  function defineContributionPoint(id) {
1093
795
  if (!id) {
1094
796
  throw new Error("defineContributionPoint: a contribution point needs an id");
@@ -1188,7 +890,7 @@ var ContributionRegistry = class {
1188
890
  }
1189
891
  };
1190
892
 
1191
- // ../../node_modules/@datalayer/reactor/lib/core/commands.js
893
+ // node_modules/@datalayer/reactor/lib/core/commands.js
1192
894
  var CommandRegistry = class {
1193
895
  byId = /* @__PURE__ */ new Map();
1194
896
  byPlugin = /* @__PURE__ */ new Map();
@@ -1303,7 +1005,7 @@ var CommandRegistry = class {
1303
1005
  }
1304
1006
  };
1305
1007
 
1306
- // ../../node_modules/@datalayer/reactor/lib/core/activation.js
1008
+ // node_modules/@datalayer/reactor/lib/core/activation.js
1307
1009
  var ON_STARTUP = "onStartup";
1308
1010
  var ON_ANY = "*";
1309
1011
  function onContributionPoint(point) {
@@ -1325,12 +1027,12 @@ function activatesAtStartup(declared) {
1325
1027
  return matchesActivation(declared, ON_STARTUP);
1326
1028
  }
1327
1029
 
1328
- // ../../node_modules/@datalayer/reactor/lib/core/extension.js
1030
+ // node_modules/@datalayer/reactor/lib/core/extension.js
1329
1031
  function isExtension(ref) {
1330
1032
  return typeof ref === "object" && ref !== null && typeof ref.name === "string" && Array.isArray(ref.plugins);
1331
1033
  }
1332
1034
 
1333
- // ../../node_modules/@datalayer/reactor/lib/core/gates.js
1035
+ // node_modules/@datalayer/reactor/lib/core/gates.js
1334
1036
  function isRefusal(answer) {
1335
1037
  return answer !== true;
1336
1038
  }
@@ -1356,7 +1058,7 @@ function resolveGate(answers, context) {
1356
1058
  };
1357
1059
  }
1358
1060
 
1359
- // ../../node_modules/@datalayer/reactor/lib/core/reactor.js
1061
+ // node_modules/@datalayer/reactor/lib/core/reactor.js
1360
1062
  function placeholderFor(ref) {
1361
1063
  return {
1362
1064
  name: ref.name,
@@ -2110,7 +1812,7 @@ function buildReactorFromPlugins(pluginsInput, options = {}) {
2110
1812
  };
2111
1813
  }
2112
1814
 
2113
- // ../../node_modules/@datalayer/reactor/lib/core/plugin.js
1815
+ // node_modules/@datalayer/reactor/lib/core/plugin.js
2114
1816
  function definePlugin(plugin) {
2115
1817
  return plugin;
2116
1818
  }
@@ -2131,7 +1833,7 @@ function mergeWithDefaults(plugin, override) {
2131
1833
  return shallowMergeConfig(defaults, override);
2132
1834
  }
2133
1835
 
2134
- // ../../node_modules/@datalayer/reactor/lib/core/origins.js
1836
+ // node_modules/@datalayer/reactor/lib/core/origins.js
2135
1837
  var ANY_ORIGIN = "*";
2136
1838
  var OriginNotAllowedError = class extends Error {
2137
1839
  /** The URL that was refused, as it was written. */
@@ -2228,7 +1930,7 @@ function assertOriginAllowed(url, extra = [], what = "a remote") {
2228
1930
  throw new OriginNotAllowedError(url, origin, what);
2229
1931
  }
2230
1932
 
2231
- // ../../node_modules/@datalayer/reactor/lib/core/federation.js
1933
+ // node_modules/@datalayer/reactor/lib/core/federation.js
2232
1934
  var runtimeOverride;
2233
1935
  var runtimeLoad;
2234
1936
  async function federationRuntime() {
@@ -2308,14 +2010,14 @@ function createFederationLoader() {
2308
2010
  }
2309
2011
  var federationLoader = createFederationLoader();
2310
2012
 
2311
- // ../../node_modules/@datalayer/reactor/lib/core/remote.js
2013
+ // node_modules/@datalayer/reactor/lib/core/remote.js
2312
2014
  var SHARED_GLOBAL = "__DATALAYER_REACTOR__";
2313
2015
  function getReactorSharedModules() {
2314
2016
  const target = globalThis;
2315
2017
  return target[SHARED_GLOBAL]?.shared ?? {};
2316
2018
  }
2317
2019
 
2318
- // ../../node_modules/@datalayer/reactor/lib/core/agentTools.js
2020
+ // node_modules/@datalayer/reactor/lib/core/agentTools.js
2319
2021
  var AgentTools = defineContributionPoint("reactor.agentTools");
2320
2022
 
2321
2023
  // src/reactor.ts
@@ -2336,6 +2038,12 @@ var RandomSources = defineContributionPoint("pap.randomSource");
2336
2038
  var Signers = defineContributionPoint("pap.signer");
2337
2039
  var KeyStores = defineContributionPoint("pap.keyStore");
2338
2040
  var TokenStores = defineContributionPoint("pap.tokenStore");
2041
+ var PairwiseUserIdStores = defineContributionPoint(
2042
+ "pap.pairwiseUserIdStore"
2043
+ );
2044
+ var AuthorizationStateStores = defineContributionPoint(
2045
+ "pap.authorizationStateStore"
2046
+ );
2339
2047
  var Caches = defineContributionPoint("pap.cache");
2340
2048
  var BrowserHandoffs = defineContributionPoint(
2341
2049
  "pap.browserHandoff"
@@ -2371,7 +2079,7 @@ function createPapCorePlugin(options = {}) {
2371
2079
  }
2372
2080
  return definePlugin({
2373
2081
  name: "@datalayer/personal-agent-protocol-core",
2374
- version: "0.2.0",
2082
+ version: "0.3.1",
2375
2083
  contributionPoints: [
2376
2084
  DiscoveryTransports,
2377
2085
  UrlSafetyPolicies,
@@ -2382,6 +2090,8 @@ function createPapCorePlugin(options = {}) {
2382
2090
  Signers,
2383
2091
  KeyStores,
2384
2092
  TokenStores,
2093
+ PairwiseUserIdStores,
2094
+ AuthorizationStateStores,
2385
2095
  Caches,
2386
2096
  BrowserHandoffs,
2387
2097
  DpopProofProviders
@@ -2428,24 +2138,6 @@ function firstContribution(reactor, point, label) {
2428
2138
  // src/sessions.ts
2429
2139
  var JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
2430
2140
  var JWT_CLIENT_ASSERTION = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
2431
- var SecretValue = class {
2432
- #value;
2433
- constructor(value) {
2434
- if (!value) {
2435
- throw new ProtocolValidationError("invalid_secret", "Secret value must not be empty");
2436
- }
2437
- this.#value = value;
2438
- }
2439
- reveal() {
2440
- return this.#value;
2441
- }
2442
- toString() {
2443
- return "[REDACTED]";
2444
- }
2445
- toJSON() {
2446
- return "[REDACTED]";
2447
- }
2448
- };
2449
2141
  function defaultRandomBytes(length) {
2450
2142
  const cryptoApi = globalThis.crypto;
2451
2143
  if (!cryptoApi) {
@@ -2453,7 +2145,7 @@ function defaultRandomBytes(length) {
2453
2145
  }
2454
2146
  return cryptoApi.getRandomValues(new Uint8Array(length));
2455
2147
  }
2456
- function base64Url(bytes) {
2148
+ function base64Url2(bytes) {
2457
2149
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2458
2150
  let output = "";
2459
2151
  for (let index = 0; index < bytes.length; index += 3) {
@@ -2476,11 +2168,19 @@ function opaqueId(prefix, randomBytes2) {
2476
2168
  if (bytes.byteLength < 16) {
2477
2169
  throw new Error("Opaque PAP identifiers require at least 128 random bits");
2478
2170
  }
2479
- return `${prefix}_${base64Url(bytes)}`;
2171
+ return `${prefix}_${base64Url2(bytes)}`;
2480
2172
  }
2481
2173
  function generatePairwiseUserId(randomBytes2 = defaultRandomBytes) {
2482
2174
  return opaqueId("usr", randomBytes2);
2483
2175
  }
2176
+ async function resolvePairwiseUserId(reactor, options) {
2177
+ const store = firstContribution(
2178
+ reactor,
2179
+ PairwiseUserIdStores,
2180
+ "pairwise user ID store"
2181
+ );
2182
+ return store.getOrCreate(options);
2183
+ }
2484
2184
  function httpsUrl(value, label) {
2485
2185
  let parsed;
2486
2186
  try {
@@ -2758,209 +2458,37 @@ var SessionClient = class {
2758
2458
  }
2759
2459
  };
2760
2460
 
2761
- // src/crypto.ts
2762
- var privateJwkMembers = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth", "k"]);
2763
- function algorithm(value) {
2764
- if (value !== "ES256" && value !== "RS256") {
2765
- throw new TypeError("PAP JOSE algorithms must be ES256 or RS256");
2461
+ // src/auth.ts
2462
+ function defaultRandomBytes2(length) {
2463
+ if (!globalThis.crypto) {
2464
+ throw new AuthorizationError("crypto_unavailable", "Web Crypto is required");
2465
+ }
2466
+ return globalThis.crypto.getRandomValues(new Uint8Array(length));
2467
+ }
2468
+ function entropy(length, randomBytes2) {
2469
+ const value = randomBytes2(length);
2470
+ if (value.byteLength < length) {
2471
+ throw new AuthorizationError(
2472
+ "weak_entropy",
2473
+ `Authorization requires ${length * 8} random bits`
2474
+ );
2766
2475
  }
2767
2476
  return value;
2768
2477
  }
2769
- function publicJwk(value, alg) {
2770
- for (const member of privateJwkMembers) {
2771
- if (member in value) {
2772
- throw new TypeError("DPoP public JWK must not contain private key material");
2478
+ function base64Url3(bytes) {
2479
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2480
+ let output = "";
2481
+ for (let index = 0; index < bytes.length; index += 3) {
2482
+ const first = bytes[index] ?? 0;
2483
+ const second = bytes[index + 1];
2484
+ const third = bytes[index + 2];
2485
+ output += alphabet[first >>> 2];
2486
+ output += alphabet[(first & 3) << 4 | (second ?? 0) >>> 4];
2487
+ if (second !== void 0) {
2488
+ output += alphabet[(second & 15) << 2 | (third ?? 0) >>> 6];
2773
2489
  }
2774
- }
2775
- const expected = alg === "ES256" ? "EC" : "RSA";
2776
- if (value.kty !== expected) throw new TypeError(`${alg} requires a ${expected} public JWK`);
2777
- if (alg === "ES256") {
2778
- if (value.crv !== "P-256" || !value.x || !value.y) {
2779
- throw new TypeError("ES256 requires a complete P-256 public JWK");
2780
- }
2781
- } else if (!value.n || !value.e) {
2782
- throw new TypeError("RS256 requires a complete RSA public JWK");
2783
- }
2784
- return { ...value };
2785
- }
2786
- function normalizedHtu(value) {
2787
- let url;
2788
- try {
2789
- url = new URL(value);
2790
- } catch (cause) {
2791
- throw new ProofError("invalid_htu", "DPoP target must be an HTTPS URL", { cause });
2792
- }
2793
- if (url.protocol !== "https:" || url.username || url.password) {
2794
- throw new ProofError("invalid_htu", "DPoP target must be an HTTPS URL");
2795
- }
2796
- url.search = "";
2797
- url.hash = "";
2798
- return url.href;
2799
- }
2800
- function base64Url2(bytes) {
2801
- let binary = "";
2802
- for (const value of bytes) binary += String.fromCharCode(value);
2803
- return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "");
2804
- }
2805
- function randomBytes(length) {
2806
- if (!globalThis.crypto) throw new ProofError("crypto_unavailable", "Web Crypto is required");
2807
- return globalThis.crypto.getRandomValues(new Uint8Array(length));
2808
- }
2809
- async function accessTokenHash(token) {
2810
- if (!globalThis.crypto) throw new ProofError("crypto_unavailable", "Web Crypto is required");
2811
- const digest2 = await globalThis.crypto.subtle.digest(
2812
- "SHA-256",
2813
- new TextEncoder().encode(token.reveal())
2814
- );
2815
- return base64Url2(new Uint8Array(digest2));
2816
- }
2817
- var JoseSigner = class {
2818
- #privateKey;
2819
- #algorithm;
2820
- #keyId;
2821
- constructor(options) {
2822
- this.#privateKey = options.privateKey;
2823
- this.#algorithm = algorithm(options.algorithm ?? "ES256");
2824
- this.#keyId = options.keyId;
2825
- }
2826
- async sign(claims, _options) {
2827
- try {
2828
- const token = await new SignJWT(claims).setProtectedHeader({
2829
- alg: this.#algorithm,
2830
- typ: "JWT",
2831
- ...this.#keyId === void 0 ? {} : { kid: this.#keyId }
2832
- }).sign(this.#privateKey);
2833
- return new SecretValue(token);
2834
- } catch (cause) {
2835
- throw new ProofError("signing_failed", "Could not sign PAP assertion", { cause });
2836
- }
2837
- }
2838
- };
2839
- var JoseDpopProofProvider = class {
2840
- #resolveKey;
2841
- #now;
2842
- #randomBytes;
2843
- constructor(options) {
2844
- this.#resolveKey = options.resolveKey;
2845
- this.#now = options.now ?? (() => /* @__PURE__ */ new Date());
2846
- this.#randomBytes = options.randomBytes ?? randomBytes;
2847
- }
2848
- async create(request) {
2849
- const material = await this.#resolveKey(request);
2850
- const alg = algorithm(material.algorithm ?? "ES256");
2851
- const jwk = publicJwk(material.publicJwk, alg);
2852
- const entropy2 = this.#randomBytes(16);
2853
- if (entropy2.byteLength < 16) {
2854
- throw new ProofError("weak_randomness", "DPoP proof IDs require 128 random bits");
2855
- }
2856
- const claims = {
2857
- jti: base64Url2(entropy2),
2858
- htm: request.method.toUpperCase(),
2859
- htu: normalizedHtu(request.url),
2860
- iat: Math.floor(this.#now().getTime() / 1e3),
2861
- ...request.nonce === void 0 ? {} : { nonce: request.nonce },
2862
- ...request.accessToken === void 0 ? {} : { ath: await accessTokenHash(request.accessToken) }
2863
- };
2864
- try {
2865
- const token = await new SignJWT(claims).setProtectedHeader({ typ: "dpop+jwt", alg, jwk }).sign(material.privateKey);
2866
- return new SecretValue(token);
2867
- } catch (cause) {
2868
- throw new ProofError("signing_failed", "Could not sign DPoP proof", { cause });
2869
- }
2870
- }
2871
- };
2872
- async function verifyDpopProof(proof, options) {
2873
- const compact = proof instanceof SecretValue ? proof.reveal() : proof;
2874
- const now = options.now ?? /* @__PURE__ */ new Date();
2875
- const windowSeconds = options.acceptanceWindowSeconds ?? 60;
2876
- if (!Number.isInteger(windowSeconds) || windowSeconds <= 0) {
2877
- throw new RangeError("acceptanceWindowSeconds must be a positive integer");
2878
- }
2879
- let jwk;
2880
- let payload;
2881
- try {
2882
- const header = decodeProtectedHeader(compact);
2883
- const alg = algorithm(header.alg ?? "");
2884
- if (header.typ !== "dpop+jwt" || typeof header.jwk !== "object") {
2885
- throw new ProofError("invalid_dpop_proof", "DPoP protected header is invalid");
2886
- }
2887
- jwk = publicJwk(header.jwk, alg);
2888
- const key = await importJWK(jwk, alg);
2889
- ({ payload } = await jwtVerify(compact, key, {
2890
- algorithms: [alg],
2891
- typ: "dpop+jwt",
2892
- requiredClaims: ["jti", "htm", "htu", "iat"]
2893
- }));
2894
- } catch (cause) {
2895
- if (cause instanceof ProofError) throw cause;
2896
- throw new ProofError("invalid_dpop_proof", "DPoP signature or claims are invalid", {
2897
- cause
2898
- });
2899
- }
2900
- if (typeof payload.jti !== "string" || !payload.jti || typeof payload.iat !== "number") {
2901
- throw new ProofError("invalid_dpop_proof", "DPoP proof identity is invalid");
2902
- }
2903
- const issuedAt = new Date(payload.iat * 1e3);
2904
- const difference = Math.abs(now.getTime() - issuedAt.getTime());
2905
- if (difference > windowSeconds * 1e3) {
2906
- throw new ProofError("invalid_dpop_proof", "DPoP proof is outside the acceptance window");
2907
- }
2908
- if (payload.htm !== options.method.toUpperCase() || payload.htu !== normalizedHtu(options.url)) {
2909
- throw new ProofError("invalid_dpop_proof", "DPoP proof does not match the request");
2910
- }
2911
- if (options.nonce !== void 0 && payload.nonce !== options.nonce) {
2912
- throw new ProofError("use_dpop_nonce", "DPoP proof nonce is missing or invalid");
2913
- }
2914
- if (options.accessToken !== void 0) {
2915
- if (payload.ath !== await accessTokenHash(options.accessToken)) {
2916
- throw new ProofError("invalid_dpop_proof", "DPoP access-token hash is invalid");
2917
- }
2918
- } else if (payload.ath !== void 0) {
2919
- throw new ProofError(
2920
- "invalid_dpop_proof",
2921
- "Token-endpoint DPoP proof must not contain ath"
2922
- );
2923
- }
2924
- const keyThumbprint = await calculateJwkThumbprint(jwk);
2925
- if (!await options.replayCache.markOnce(`${keyThumbprint}:${payload.jti}`, {
2926
- expiresAt: new Date(now.getTime() + windowSeconds * 1e3)
2927
- })) {
2928
- throw new ProofError("invalid_dpop_proof", "DPoP proof has already been used");
2929
- }
2930
- return { jti: payload.jti, issuedAt, publicJwk: jwk, keyThumbprint };
2931
- }
2932
-
2933
- // src/auth.ts
2934
- function defaultRandomBytes2(length) {
2935
- if (!globalThis.crypto) {
2936
- throw new AuthorizationError("crypto_unavailable", "Web Crypto is required");
2937
- }
2938
- return globalThis.crypto.getRandomValues(new Uint8Array(length));
2939
- }
2940
- function entropy(length, randomBytes2) {
2941
- const value = randomBytes2(length);
2942
- if (value.byteLength < length) {
2943
- throw new AuthorizationError(
2944
- "weak_entropy",
2945
- `Authorization requires ${length * 8} random bits`
2946
- );
2947
- }
2948
- return value;
2949
- }
2950
- function base64Url3(bytes) {
2951
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2952
- let output = "";
2953
- for (let index = 0; index < bytes.length; index += 3) {
2954
- const first = bytes[index] ?? 0;
2955
- const second = bytes[index + 1];
2956
- const third = bytes[index + 2];
2957
- output += alphabet[first >>> 2];
2958
- output += alphabet[(first & 3) << 4 | (second ?? 0) >>> 4];
2959
- if (second !== void 0) {
2960
- output += alphabet[(second & 15) << 2 | (third ?? 0) >>> 6];
2961
- }
2962
- if (third !== void 0) {
2963
- output += alphabet[third & 63];
2490
+ if (third !== void 0) {
2491
+ output += alphabet[third & 63];
2964
2492
  }
2965
2493
  }
2966
2494
  return output;
@@ -2989,8 +2517,12 @@ async function buildDirectSignInRequest(options) {
2989
2517
  const randomBytes2 = options.randomBytes ?? defaultRandomBytes2;
2990
2518
  const state = base64Url3(entropy(16, randomBytes2));
2991
2519
  const verifier = base64Url3(entropy(32, randomBytes2));
2520
+ const cryptoApi = globalThis.crypto;
2521
+ if (!cryptoApi) {
2522
+ throw new AuthorizationError("crypto_unavailable", "Web Crypto is required");
2523
+ }
2992
2524
  const challenge = base64Url3(
2993
- new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier)))
2525
+ new Uint8Array(await cryptoApi.subtle.digest("SHA-256", new TextEncoder().encode(verifier)))
2994
2526
  );
2995
2527
  const endpoint = httpsUrl2(options.authorizationEndpoint, "authorizationEndpoint");
2996
2528
  const scopes2 = validScopes(options.scopes);
@@ -3106,6 +2638,276 @@ var AuthorizationCodeGrant = class {
3106
2638
  };
3107
2639
  }
3108
2640
  };
2641
+ async function stateReference(state) {
2642
+ const bytes = new TextEncoder().encode(state);
2643
+ if ([...bytes].some((byte) => byte > 127)) {
2644
+ throw new AuthorizationError("invalid_callback", "Authorization state is invalid");
2645
+ }
2646
+ const cryptoApi = globalThis.crypto;
2647
+ if (!cryptoApi) {
2648
+ throw new AuthorizationError("crypto_unavailable", "Web Crypto is required");
2649
+ }
2650
+ const digest = await cryptoApi.subtle.digest("SHA-256", bytes);
2651
+ return `direct:${Array.from(
2652
+ new Uint8Array(digest),
2653
+ (byte) => byte.toString(16).padStart(2, "0")
2654
+ ).join("")}`;
2655
+ }
2656
+ function callbackState(callbackUrl) {
2657
+ const callback = httpsUrl2(callbackUrl, "callbackUrl");
2658
+ if (callback.hash) {
2659
+ throw new AuthorizationError("invalid_callback", "Authorization callback has a fragment");
2660
+ }
2661
+ const states = callback.searchParams.getAll("state");
2662
+ if (states.length !== 1 || !states[0]) {
2663
+ throw new AuthorizationError(
2664
+ "invalid_callback",
2665
+ "Authorization callback must contain one state value"
2666
+ );
2667
+ }
2668
+ return states[0];
2669
+ }
2670
+ function isPendingDirectSignIn(value) {
2671
+ if (typeof value !== "object" || value === null) return false;
2672
+ const pending = value;
2673
+ return pending.kind === "pap.direct-sign-in.v1" && typeof pending.tokenEndpoint === "string" && typeof pending.tenantId === "string" && typeof pending.pairwiseUserId === "string" && typeof pending.sessionId === "string" && pending.expiresAt instanceof Date && typeof pending.request === "object" && pending.request !== null;
2674
+ }
2675
+ var DirectSignInClient = class {
2676
+ #reactor;
2677
+ #fetch;
2678
+ #maxResponseBytes;
2679
+ constructor(options) {
2680
+ const fetcher = options.fetch ?? globalThis.fetch;
2681
+ if (!fetcher) {
2682
+ throw new AuthorizationError("fetch_unavailable", "A fetch implementation is required");
2683
+ }
2684
+ const maxResponseBytes = options.maxResponseBytes ?? 100 * 1024;
2685
+ if (!Number.isInteger(maxResponseBytes) || maxResponseBytes <= 0) {
2686
+ throw new RangeError("maxResponseBytes must be a positive integer");
2687
+ }
2688
+ this.#reactor = options.reactor;
2689
+ this.#fetch = fetcher;
2690
+ this.#maxResponseBytes = maxResponseBytes;
2691
+ }
2692
+ async begin(options) {
2693
+ const lifetimeSeconds = options.lifetimeSeconds ?? 600;
2694
+ if (!Number.isInteger(lifetimeSeconds) || lifetimeSeconds < 1 || lifetimeSeconds > 900) {
2695
+ throw new RangeError("Direct Sign-In state lifetime must be between 1 and 900 seconds");
2696
+ }
2697
+ if (!options.tenantId || options.tenantId.length > 256) {
2698
+ throw new RangeError("tenantId must contain between 1 and 256 characters");
2699
+ }
2700
+ if (!/^[A-Za-z0-9_-]{1,256}$/.test(options.pairwiseUserId)) {
2701
+ throw new AuthorizationError(
2702
+ "invalid_user_id",
2703
+ "Pairwise user ID must be an opaque URL-safe identifier"
2704
+ );
2705
+ }
2706
+ if (!/^[A-Za-z0-9_-]{1,256}$/.test(options.sessionId)) {
2707
+ throw new AuthorizationError("invalid_session_id", "Invalid PAP session ID");
2708
+ }
2709
+ const clock = firstContribution(this.#reactor, Clocks, "clock");
2710
+ const random = firstContribution(this.#reactor, RandomSources, "random source");
2711
+ const store = firstContribution(
2712
+ this.#reactor,
2713
+ AuthorizationStateStores,
2714
+ "authorization state store"
2715
+ );
2716
+ const browser = firstContribution(this.#reactor, BrowserHandoffs, "browser handoff");
2717
+ const request = await buildDirectSignInRequest({
2718
+ authorizationEndpoint: options.authorizationEndpoint,
2719
+ issuer: options.issuer,
2720
+ clientId: options.clientId,
2721
+ redirectUri: options.redirectUri,
2722
+ scopes: options.scopes,
2723
+ randomBytes: (length) => random.bytes(length)
2724
+ });
2725
+ const tokenEndpoint = httpsUrl2(options.tokenEndpoint, "tokenEndpoint").toString();
2726
+ const expiresAt = new Date(clock.now().getTime() + lifetimeSeconds * 1e3);
2727
+ const reference = await stateReference(request.state.reveal());
2728
+ const pending = {
2729
+ kind: "pap.direct-sign-in.v1",
2730
+ request,
2731
+ tokenEndpoint,
2732
+ tenantId: options.tenantId,
2733
+ pairwiseUserId: options.pairwiseUserId,
2734
+ sessionId: options.sessionId,
2735
+ expiresAt
2736
+ };
2737
+ await store.put(reference, pending, { expiresAt });
2738
+ try {
2739
+ await browser.open(request.authorizationUrl);
2740
+ } catch (cause) {
2741
+ await store.take(reference);
2742
+ throw new AuthorizationError("browser_handoff_failed", "Browser handoff failed", { cause });
2743
+ }
2744
+ return { expiresAt, requestedScopes: request.scopes };
2745
+ }
2746
+ async complete(options) {
2747
+ const state = callbackState(options.callbackUrl);
2748
+ const store = firstContribution(
2749
+ this.#reactor,
2750
+ AuthorizationStateStores,
2751
+ "authorization state store"
2752
+ );
2753
+ const pending = await store.take(await stateReference(state));
2754
+ if (!isPendingDirectSignIn(pending)) {
2755
+ throw new AuthorizationError(
2756
+ "invalid_or_expired_state",
2757
+ "Authorization state is unknown, expired, or already used"
2758
+ );
2759
+ }
2760
+ const clock = firstContribution(this.#reactor, Clocks, "clock");
2761
+ if (clock.now().getTime() > pending.expiresAt.getTime()) {
2762
+ throw new AuthorizationError("invalid_or_expired_state", "Authorization state has expired");
2763
+ }
2764
+ const callback = validateDirectSignInCallback(pending.request, options.callbackUrl);
2765
+ return this.#exchange(pending, callback, options.signal);
2766
+ }
2767
+ async #exchange(pending, callback, signal) {
2768
+ const clock = firstContribution(this.#reactor, Clocks, "clock");
2769
+ const random = firstContribution(this.#reactor, RandomSources, "random source");
2770
+ const signer = firstContribution(this.#reactor, Signers, "signer");
2771
+ const dpop = firstContribution(
2772
+ this.#reactor,
2773
+ DpopProofProviders,
2774
+ "DPoP proof provider"
2775
+ );
2776
+ let nonce;
2777
+ for (let attempt = 0; attempt < 2; attempt += 1) {
2778
+ const iat = Math.floor(clock.now().getTime() / 1e3);
2779
+ const clientAssertion = await signer.sign(
2780
+ {
2781
+ iss: pending.request.clientId,
2782
+ sub: pending.request.clientId,
2783
+ aud: pending.tokenEndpoint,
2784
+ iat,
2785
+ exp: iat + 60,
2786
+ jti: `jti_${base64Url3(entropy(16, (length) => random.bytes(length)))}`
2787
+ },
2788
+ { purpose: "client_assertion" }
2789
+ );
2790
+ const proof = await dpop.create({
2791
+ method: "POST",
2792
+ url: pending.tokenEndpoint,
2793
+ tenantId: pending.tenantId,
2794
+ subject: pending.pairwiseUserId,
2795
+ issuer: pending.request.issuer,
2796
+ ...nonce === void 0 ? {} : { nonce }
2797
+ });
2798
+ const grant = new AuthorizationCodeGrant({
2799
+ code: callback.code.reveal(),
2800
+ redirectUri: pending.request.redirectUri,
2801
+ codeVerifier: pending.request.codeVerifier.reveal(),
2802
+ sessionId: pending.sessionId,
2803
+ clientId: pending.request.clientId,
2804
+ clientAssertion: clientAssertion.reveal()
2805
+ });
2806
+ let response;
2807
+ try {
2808
+ response = await this.#fetch(pending.tokenEndpoint, {
2809
+ method: "POST",
2810
+ headers: { Accept: "application/json", DPoP: proof.reveal() },
2811
+ body: grant.toForm(),
2812
+ credentials: "omit",
2813
+ redirect: "error",
2814
+ ...signal === void 0 ? {} : { signal }
2815
+ });
2816
+ } catch (cause) {
2817
+ throw new AuthorizationError(
2818
+ "request_failed",
2819
+ "Direct Sign-In token exchange failed",
2820
+ { cause }
2821
+ );
2822
+ }
2823
+ const payload = await this.#decode(response);
2824
+ if (response.ok) return this.#result(response, payload, pending);
2825
+ const source = typeof payload === "object" && payload !== null ? payload : {};
2826
+ const remoteCode = "error" in source ? source.error : void 0;
2827
+ const code = typeof remoteCode === "string" ? remoteCode : "http_error";
2828
+ if (code === "use_dpop_nonce" && attempt === 0) {
2829
+ nonce = response.headers.get("dpop-nonce") ?? void 0;
2830
+ if (!nonce) {
2831
+ throw new AuthorizationError(
2832
+ "missing_dpop_nonce",
2833
+ "Company requested a DPoP nonce but did not send one",
2834
+ { statusCode: response.status }
2835
+ );
2836
+ }
2837
+ continue;
2838
+ }
2839
+ throw new AuthorizationError(code, "Company refused the Direct Sign-In token exchange", {
2840
+ statusCode: response.status
2841
+ });
2842
+ }
2843
+ throw new AuthorizationError("use_dpop_nonce", "Company rejected the refreshed DPoP proof");
2844
+ }
2845
+ async #decode(response) {
2846
+ const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim();
2847
+ if (contentType !== "application/json") {
2848
+ throw new AuthorizationError(
2849
+ "invalid_content_type",
2850
+ "Direct Sign-In token endpoint must return JSON",
2851
+ { statusCode: response.status }
2852
+ );
2853
+ }
2854
+ const body = await response.text();
2855
+ if (new TextEncoder().encode(body).byteLength > this.#maxResponseBytes) {
2856
+ throw new AuthorizationError(
2857
+ "response_too_large",
2858
+ "Direct Sign-In response exceeds size limit",
2859
+ { statusCode: response.status }
2860
+ );
2861
+ }
2862
+ try {
2863
+ return parseJsonStrict(body);
2864
+ } catch (cause) {
2865
+ throw new AuthorizationError(
2866
+ "invalid_json",
2867
+ "Direct Sign-In token endpoint returned invalid JSON",
2868
+ { statusCode: response.status, cause }
2869
+ );
2870
+ }
2871
+ }
2872
+ #result(response, payload, pending) {
2873
+ let token;
2874
+ try {
2875
+ token = parseSessionToken(payload);
2876
+ } catch (cause) {
2877
+ throw new AuthorizationError(
2878
+ "invalid_token_response",
2879
+ "Direct Sign-In token response is malformed",
2880
+ { statusCode: response.status, cause }
2881
+ );
2882
+ }
2883
+ if (token.tokenType !== "DPoP" || !token.signedIn) {
2884
+ throw new AuthorizationError(
2885
+ "invalid_token_response",
2886
+ "Direct Sign-In must return a signed-in DPoP Session Token",
2887
+ { statusCode: response.status }
2888
+ );
2889
+ }
2890
+ if (token.sessionId !== pending.sessionId) {
2891
+ throw new AuthorizationError(
2892
+ "session_mismatch",
2893
+ "Direct Sign-In response changed the existing Session",
2894
+ { statusCode: response.status }
2895
+ );
2896
+ }
2897
+ if (token.scopes.some((scope) => !pending.request.scopes.includes(scope))) {
2898
+ throw new AuthorizationError(
2899
+ "scope_escalation",
2900
+ "Company returned a scope that was not requested",
2901
+ { statusCode: response.status }
2902
+ );
2903
+ }
2904
+ return {
2905
+ token,
2906
+ grantedScopes: token.scopes,
2907
+ missingScopes: pending.request.scopes.filter((scope) => !token.scopes.includes(scope))
2908
+ };
2909
+ }
2910
+ };
3109
2911
 
3110
2912
  // src/models.ts
3111
2913
  function record(value, label) {
@@ -3421,6 +3223,66 @@ function parseClientMetadata(value) {
3421
3223
  extensions: extensions(source.extensions, "extensions")
3422
3224
  };
3423
3225
  }
3226
+ var privateJwkMembers2 = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth", "k"]);
3227
+ function parseJsonWebKey(value) {
3228
+ const source = record(value, "JWKS key");
3229
+ if (Object.keys(source).some((key) => privateJwkMembers2.has(key))) {
3230
+ throw new ProtocolValidationError(
3231
+ "private_jwk",
3232
+ "JWKS keys must not contain private or symmetric key material"
3233
+ );
3234
+ }
3235
+ const kty = oneOf(source.kty, ["EC", "RSA"], "JWKS key kty");
3236
+ const kid = source.kid === void 0 ? void 0 : string(source.kid, "JWKS key kid");
3237
+ const use = source.use === void 0 ? void 0 : oneOf(source.use, ["sig"], "use");
3238
+ const alg = source.alg === void 0 ? void 0 : oneOf(source.alg, ["ES256", "RS256"], "alg");
3239
+ if (kty === "EC") {
3240
+ const crv = oneOf(source.crv, ["P-256"], "EC key crv");
3241
+ const x = string(source.x, "EC key x");
3242
+ const y = string(source.y, "EC key y");
3243
+ if (alg !== void 0 && alg !== "ES256") {
3244
+ throw new ProtocolValidationError(
3245
+ "invalid_jwk_algorithm",
3246
+ "P-256 signing keys may advertise only ES256"
3247
+ );
3248
+ }
3249
+ return {
3250
+ ...source,
3251
+ kty,
3252
+ ...kid === void 0 ? {} : { kid },
3253
+ ...use === void 0 ? {} : { use },
3254
+ ...alg === void 0 ? {} : { alg },
3255
+ crv,
3256
+ x,
3257
+ y
3258
+ };
3259
+ }
3260
+ const n = string(source.n, "RSA key n");
3261
+ const e = string(source.e, "RSA key e");
3262
+ if (alg !== void 0 && alg !== "RS256") {
3263
+ throw new ProtocolValidationError(
3264
+ "invalid_jwk_algorithm",
3265
+ "RSA signing keys may advertise only RS256"
3266
+ );
3267
+ }
3268
+ return {
3269
+ ...source,
3270
+ kty,
3271
+ ...kid === void 0 ? {} : { kid },
3272
+ ...use === void 0 ? {} : { use },
3273
+ ...alg === void 0 ? {} : { alg },
3274
+ n,
3275
+ e
3276
+ };
3277
+ }
3278
+ function parseJsonWebKeySet(value) {
3279
+ const source = record(value, "JWKS");
3280
+ const keys = array(source.keys, "JWKS keys").map(parseJsonWebKey);
3281
+ if (keys.length === 0) {
3282
+ throw new ProtocolValidationError("invalid_jwks", "JWKS keys must not be empty");
3283
+ }
3284
+ return { ...source, keys };
3285
+ }
3424
3286
  function parseOperation(value) {
3425
3287
  const source = record(value, "operation");
3426
3288
  const state = oneOf(
@@ -3623,7 +3485,7 @@ function metadataUrl(issuer) {
3623
3485
  parsed.hash = "";
3624
3486
  return parsed;
3625
3487
  }
3626
- async function readJson(initialUrl, transport, urlPolicy, maxRedirects, maxResponseBytes) {
3488
+ async function readJson(initialUrl, transport, urlPolicy, maxRedirects, maxResponseBytes, signal) {
3627
3489
  let current = initialUrl;
3628
3490
  for (let redirects = 0; redirects <= maxRedirects; redirects += 1) {
3629
3491
  await urlPolicy.assertSafe(current);
@@ -3632,7 +3494,8 @@ async function readJson(initialUrl, transport, urlPolicy, maxRedirects, maxRespo
3632
3494
  response = await transport.fetch(current, {
3633
3495
  headers: { Accept: "application/json" },
3634
3496
  redirect: "manual",
3635
- credentials: "omit"
3497
+ credentials: "omit",
3498
+ ...signal === void 0 ? {} : { signal }
3636
3499
  });
3637
3500
  } catch (cause) {
3638
3501
  throw new DiscoveryError("request_failed", "Discovery request failed", { cause });
@@ -3694,7 +3557,8 @@ async function discoverCompany(domain2, options = {}) {
3694
3557
  transport,
3695
3558
  urlPolicy,
3696
3559
  maxRedirects,
3697
- maxBytes
3560
+ maxBytes,
3561
+ options.signal
3698
3562
  );
3699
3563
  const document = parseDiscoveryDocument(discovery.value);
3700
3564
  if (document.organization.domain.replace(/^www\./, "") !== requestedDomain.replace(/^www\./, "")) {
@@ -3711,7 +3575,8 @@ async function discoverCompany(domain2, options = {}) {
3711
3575
  transport,
3712
3576
  urlPolicy,
3713
3577
  maxRedirects,
3714
- maxBytes
3578
+ maxBytes,
3579
+ options.signal
3715
3580
  );
3716
3581
  const oauthMetadata = parseOAuthServerMetadata(metadataResponse.value);
3717
3582
  if (oauthMetadata.issuer !== document.auth.issuer) {
@@ -3729,21 +3594,166 @@ async function discoverCompany(domain2, options = {}) {
3729
3594
  oauthMetadata
3730
3595
  };
3731
3596
  }
3597
+
3598
+ // src/identity.ts
3599
+ var AgentIdentityClient = class {
3600
+ #reactor;
3601
+ #maxJwksRedirects;
3602
+ #maxResponseBytes;
3603
+ constructor(options = {}) {
3604
+ const maxRedirects = options.maxJwksRedirects ?? 3;
3605
+ const maxBytes = options.maxResponseBytes ?? 100 * 1024;
3606
+ if (!Number.isInteger(maxRedirects) || maxRedirects < 0) {
3607
+ throw new RangeError("maxJwksRedirects must be a non-negative integer");
3608
+ }
3609
+ if (!Number.isInteger(maxBytes) || maxBytes <= 0) {
3610
+ throw new RangeError("maxResponseBytes must be a positive integer");
3611
+ }
3612
+ this.#reactor = options.reactor ?? buildPapReactor(options);
3613
+ this.#maxJwksRedirects = maxRedirects;
3614
+ this.#maxResponseBytes = maxBytes;
3615
+ }
3616
+ async fetchMetadata(clientId, options = {}) {
3617
+ const requested = this.#clientIdUrl(clientId);
3618
+ const value = await this.#readJson(requested, 0, void 0, options.signal);
3619
+ let metadata;
3620
+ try {
3621
+ metadata = parseClientMetadata(value);
3622
+ } catch (cause) {
3623
+ throw new ProtocolValidationError(
3624
+ "invalid_client_metadata",
3625
+ "Client metadata failed protocol validation",
3626
+ { cause }
3627
+ );
3628
+ }
3629
+ if (metadata.client_id !== requested.toString()) {
3630
+ throw new IdentityError(
3631
+ "client_id_mismatch",
3632
+ "Client metadata client_id does not match the URL that was fetched"
3633
+ );
3634
+ }
3635
+ return metadata;
3636
+ }
3637
+ async fetchJwks(clientId, options = {}) {
3638
+ const metadata = await this.fetchMetadata(clientId, options);
3639
+ const jwksUrl = new URL(metadata.jwks_uri);
3640
+ const value = await this.#readJson(
3641
+ jwksUrl,
3642
+ this.#maxJwksRedirects,
3643
+ new URL(metadata.client_id).hostname,
3644
+ options.signal
3645
+ );
3646
+ try {
3647
+ return parseJsonWebKeySet(value);
3648
+ } catch (cause) {
3649
+ throw new ProtocolValidationError(
3650
+ "invalid_jwks",
3651
+ "Personal-agent JWKS failed protocol validation",
3652
+ { cause }
3653
+ );
3654
+ }
3655
+ }
3656
+ #clientIdUrl(clientId) {
3657
+ let requested;
3658
+ try {
3659
+ requested = new URL(clientId);
3660
+ } catch (cause) {
3661
+ throw new IdentityError(
3662
+ "invalid_client_id",
3663
+ "client_id must be a valid HTTPS URL",
3664
+ { cause }
3665
+ );
3666
+ }
3667
+ if (requested.hash) {
3668
+ throw new IdentityError("invalid_client_id", "client_id must not contain a fragment");
3669
+ }
3670
+ return requested;
3671
+ }
3672
+ async #readJson(initialUrl, maxRedirects, requiredHost, signal) {
3673
+ const transport = firstContribution(
3674
+ this.#reactor,
3675
+ DiscoveryTransports,
3676
+ "metadata transport"
3677
+ );
3678
+ const policy2 = firstContribution(
3679
+ this.#reactor,
3680
+ UrlSafetyPolicies,
3681
+ "URL safety policy"
3682
+ );
3683
+ let current = initialUrl;
3684
+ for (let redirects = 0; redirects <= maxRedirects; redirects += 1) {
3685
+ if (requiredHost !== void 0 && current.hostname !== requiredHost) {
3686
+ throw new IdentityError("jwks_domain_mismatch", "JWKS redirect changed agent domain");
3687
+ }
3688
+ await policy2.assertSafe(current);
3689
+ let response;
3690
+ try {
3691
+ response = await transport.fetch(current, {
3692
+ headers: { Accept: "application/json" },
3693
+ redirect: "manual",
3694
+ credentials: "omit",
3695
+ ...signal === void 0 ? {} : { signal }
3696
+ });
3697
+ } catch (cause) {
3698
+ throw new IdentityError("request_failed", "Agent identity request failed", { cause });
3699
+ }
3700
+ if (response.status >= 300 && response.status < 400) {
3701
+ if (redirects === maxRedirects) {
3702
+ const code = maxRedirects === 0 ? "metadata_redirect" : "too_many_redirects";
3703
+ throw new IdentityError(code, "Agent identity endpoint exceeded redirect policy");
3704
+ }
3705
+ const location2 = response.headers.get("location");
3706
+ if (!location2) {
3707
+ throw new IdentityError(
3708
+ "invalid_redirect",
3709
+ "Agent identity redirect has no location"
3710
+ );
3711
+ }
3712
+ current = new URL(location2, current);
3713
+ continue;
3714
+ }
3715
+ if (!response.ok) {
3716
+ throw new IdentityError(
3717
+ "http_error",
3718
+ "Agent identity endpoint returned an unsuccessful status",
3719
+ { statusCode: response.status }
3720
+ );
3721
+ }
3722
+ const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim();
3723
+ if (contentType !== "application/json" && contentType !== "application/jwk-set+json") {
3724
+ throw new IdentityError("invalid_content_type", "Agent identity response must be JSON");
3725
+ }
3726
+ const body = await response.text();
3727
+ if (new TextEncoder().encode(body).byteLength > this.#maxResponseBytes) {
3728
+ throw new IdentityError(
3729
+ "response_too_large",
3730
+ "Agent identity response exceeds size limit"
3731
+ );
3732
+ }
3733
+ return parseJsonStrict(body);
3734
+ }
3735
+ throw new IdentityError("too_many_redirects", "Agent identity endpoint exceeded redirect policy");
3736
+ }
3737
+ };
3732
3738
  export {
3739
+ AgentIdentityClient,
3733
3740
  AuthorizationCodeGrant,
3734
3741
  AuthorizationError,
3742
+ AuthorizationStateStores,
3735
3743
  BrowserHandoffs,
3736
3744
  Caches,
3737
3745
  Clocks,
3746
+ DirectSignInClient,
3738
3747
  DiscoveryError,
3739
3748
  DiscoveryTransports,
3740
3749
  DpopProofProviders,
3741
3750
  ExtensionHandlers,
3751
+ IdentityError,
3742
3752
  JWT_BEARER_GRANT,
3743
3753
  JWT_CLIENT_ASSERTION,
3744
3754
  JoseDpopProofProvider,
3745
- JoseSigner,
3746
3755
  KeyStores,
3756
+ PairwiseUserIdStores,
3747
3757
  PersonalAgentProtocolError,
3748
3758
  ProofError,
3749
3759
  ProtocolProfiles,
@@ -3768,11 +3778,12 @@ export {
3768
3778
  parseConversationEventPage,
3769
3779
  parseDiscoveryDocument,
3770
3780
  parseJsonStrict,
3781
+ parseJsonWebKeySet,
3771
3782
  parseMessage,
3772
3783
  parseOAuthServerMetadata,
3773
3784
  parseOperation,
3774
3785
  parseSessionToken,
3775
- validateDirectSignInCallback,
3776
- verifyDpopProof
3786
+ resolvePairwiseUserId,
3787
+ validateDirectSignInCallback
3777
3788
  };
3778
3789
  //# sourceMappingURL=index.js.map