unjwt 0.8.4 → 0.8.5

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 (53) hide show
  1. package/README.md +3 -9
  2. package/dist/_chunks/{_import-key.mjs → algorithms.mjs} +194 -399
  3. package/dist/_chunks/{_codec-jwe.d.mts → codec-jwe.d.mts} +40 -9
  4. package/dist/_chunks/{_codec-jws.mjs → codec-jws.mjs} +133 -80
  5. package/dist/_chunks/cookie.mjs +50 -0
  6. package/dist/_chunks/digest.mjs +10 -3
  7. package/dist/_chunks/error.d.mts +103 -0
  8. package/dist/_chunks/export.mjs +97 -0
  9. package/dist/_chunks/index.d.mts +5 -103
  10. package/dist/_chunks/index2.d.mts +206 -12
  11. package/dist/_chunks/index3.d.mts +12 -109
  12. package/dist/_chunks/{jwe.d.mts → index4.d.mts} +2 -1
  13. package/dist/_chunks/{jws.d.mts → index5.d.mts} +2 -1
  14. package/dist/_chunks/index6.d.mts +113 -0
  15. package/dist/_chunks/jwe.mjs +205 -72
  16. package/dist/{core → _chunks}/jwk.mjs +210 -495
  17. package/dist/_chunks/jws.mjs +280 -193
  18. package/dist/_chunks/jwt.d.mts +2 -1
  19. package/dist/_chunks/{_jwt-internal.mjs → jwt.mjs} +110 -54
  20. package/dist/_chunks/libs/unsecure.d.mts +35 -25
  21. package/dist/_chunks/libs/unsecure.mjs +101 -268
  22. package/dist/_chunks/{_internal.d.mts → limits.d.mts} +1 -1
  23. package/dist/_chunks/payload.mjs +23 -0
  24. package/dist/_chunks/wrap.mjs +209 -0
  25. package/dist/adapters/elysia/index.d.mts +4 -4
  26. package/dist/adapters/elysia/index.mjs +4 -3
  27. package/dist/adapters/h3/index.d.mts +13 -13
  28. package/dist/adapters/h3/index.mjs +8 -11
  29. package/dist/adapters/h3v1/index.d.mts +5 -5
  30. package/dist/adapters/h3v1/index.mjs +6 -9
  31. package/dist/core/internal.mjs +1 -1
  32. package/dist/core/{jwe.d.mts → jwe/index.d.mts} +5 -4
  33. package/dist/core/jwe/index.mjs +5 -0
  34. package/dist/core/{jwk.d.mts → jwk/index.d.mts} +5 -4
  35. package/dist/core/jwk/index.mjs +7 -0
  36. package/dist/core/{jws.d.mts → jws/index.d.mts} +5 -4
  37. package/dist/core/jws/index.mjs +5 -0
  38. package/dist/dpop/index.d.mts +73 -0
  39. package/dist/dpop/index.mjs +296 -0
  40. package/dist/error.d.mts +3 -0
  41. package/dist/error.mjs +20 -0
  42. package/dist/index.d.mts +10 -9
  43. package/dist/index.mjs +7 -4
  44. package/dist/session/index.d.mts +4 -4
  45. package/dist/session/index.mjs +6 -47
  46. package/dist/utils/index.d.mts +7 -0
  47. package/dist/{core/utils → utils}/index.mjs +4 -3
  48. package/package.json +21 -10
  49. package/dist/_chunks/jwk.d.mts +0 -205
  50. package/dist/core/jwe.mjs +0 -5
  51. package/dist/core/jws.mjs +0 -5
  52. package/dist/core/utils/index.d.mts +0 -6
  53. /package/dist/_chunks/{_cookie-attributes.d.mts → cookie-attributes.d.mts} +0 -0
@@ -1,9 +1,94 @@
1
- import { sanitizeObjectCopy, textEncoder } from "./libs/unsecure.mjs";
2
- import { JWTError, applyTypDefault, base64UrlStringify, computeJwtTimeClaims, getPlaintextBytes, isJWK, isJWKSet, parsePayloadText, validateCriticalHeadersJWS } from "./_jwt-internal.mjs";
3
- import { inferJWSAllowedAlgorithms } from "./digest.mjs";
4
- import { JWS_ALG_CTX, applyB64Header, asJWTError, asKeyLookupError, asKeyUsageError, assertB64Option, assertCompactToken, assertConsumerOptions, assertEntryLimit, assertJWKSetCandidates, assertJWSAlgSupported, assertNotStringKey, assertOptionEntry, assertProducerConfirmation, assertProducerCrit, assertProducerKeyHeaders, assertProtectedOnlyOption, assertSerializationObject, buildJWKSetFilter, checkAlgAllowed, checkJWSAlgSupported, checkJWSB64, checkProtectedTyp, checkSerializationEntry, decodeJWSPayload, decodeProtectedHeader, decodeSegment, getJWKsFromSet, importKey, keyMatchesHeader, resolveKeyInput, resolveLimit, resolvePayloadMode, resolveSigningKey, serializeProtectedHeader, sign as sign$1, stringKeyError, validateClaimsIfJson, verify as verify$1 } from "./_import-key.mjs";
1
+ import { textEncoder } from "./libs/unsecure.mjs";
2
+ import { JWTError } from "../error.mjs";
3
+ import { applyTypDefault, base64UrlStringify, computeJwtTimeClaims, getPlaintextBytes, isJWK, isJWKSet, parsePayloadBytes, parsePayloadSegment, parsePayloadText, validateCriticalHeadersJWS } from "./jwt.mjs";
4
+ import { JWS_ALG_CTX, asJWTError, asKeyLookupError, assertCompactToken, assertConsumerOptions, assertEntryLimit, assertJWKSetCandidates, assertOctKeyLength, assertOptionEntry, assertProducerConfirmation, assertProducerCrit, assertProducerKeyHeaders, assertProtectedOnlyOption, assertSerializationObject, buildJWKSetFilter, checkAlgAllowed, checkAlgSupported, checkProtectedTyp, checkSerializationEntry, copyJsonObject, decodeProtectedHeader, decodeSegment, getJWKsFromSet, importKey, keyMatchesHeader, resolveKeyInput, resolveLimit, serializeProtectedHeader, sign as sign$1, stringKeyError, verify as verify$1 } from "./algorithms.mjs";
5
+ import { inferJWSAllowedAlgorithms, isJWSAlgorithm, jwsHashOf, modulusLengthOf } from "./digest.mjs";
6
+ import { resolvePayloadMode, validateClaimsIfJson } from "./payload.mjs";
7
+ async function resolveSigningKey(alg, key, usage) {
8
+ if (key instanceof Uint8Array) {
9
+ if (alg !== "HS256" && alg !== "HS384" && alg !== "HS512") throw new JWTError(`${alg} needs an asymmetric key, not raw bytes.`, "ERR_JWK_INVALID");
10
+ assertOctKeyLength(key, alg);
11
+ return crypto.subtle.importKey("raw", key, {
12
+ name: "HMAC",
13
+ hash: jwsHashOf(alg)
14
+ }, false, [usage]);
15
+ }
16
+ if (RSA_SIGNATURE_ALGORITHMS.has(alg)) {
17
+ const modulusLength = modulusLengthOf(key.algorithm);
18
+ if (typeof modulusLength !== "number" || modulusLength < 2048) throw new JWTError(`${alg} requires a key modulusLength of at least 2048 bits`, "ERR_JWK_INVALID");
19
+ }
20
+ return key;
21
+ }
22
+ function assertNotStringKey(key) {
23
+ if (typeof key === "string") throw stringKeyError();
24
+ }
25
+ function asKeyUsageError(error, alg) {
26
+ return asJWTError(error, {
27
+ code: "ERR_JWK_INVALID",
28
+ message: `Key is not usable for ${alg}.`
29
+ });
30
+ }
31
+ const RSA_SIGNATURE_ALGORITHMS = /* @__PURE__ */ new Set([
32
+ "RS256",
33
+ "RS384",
34
+ "RS512",
35
+ "PS256",
36
+ "PS384",
37
+ "PS512"
38
+ ]);
39
+ function assertJWSAlgSupported(alg) {
40
+ const error = checkJWSAlgSupported(alg);
41
+ if (error) throw error;
42
+ }
43
+ function checkJWSAlgSupported(alg) {
44
+ return checkAlgSupported(alg, {
45
+ supported: isJWSAlgorithm,
46
+ code: "ERR_JWS_ALG_NOT_ALLOWED",
47
+ label: "Algorithm",
48
+ role: "signing algorithm"
49
+ });
50
+ }
51
+ function checkJWSB64(header) {
52
+ if (Object.hasOwn(header, "b64") && typeof header.b64 !== "boolean") return new JWTError("Invalid JWS: \"b64\" must be a boolean.", "ERR_JWS_INVALID");
53
+ if (header.b64 !== false) return void 0;
54
+ if (Array.isArray(header.crit) && header.crit.includes("b64")) return void 0;
55
+ return new JWTError("Invalid JWS: \"b64\" must be listed in \"crit\".", "ERR_JWS_INVALID");
56
+ }
57
+ function assertB64Option(header) {
58
+ if (!header || !Object.hasOwn(header, "b64") || typeof header.b64 === "boolean") return;
59
+ throw new JWTError("\"b64\" must be a boolean.", "ERR_INVALID_OPTION");
60
+ }
61
+ function applyB64Header(header) {
62
+ assertB64Option(header);
63
+ if (header.b64 === true) {
64
+ if (!(Array.isArray(header.crit) && header.crit.includes("b64"))) delete header.b64;
65
+ return;
66
+ }
67
+ if (header.b64 !== false) return;
68
+ const crit = header.crit;
69
+ if (crit === void 0) header.crit = ["b64"];
70
+ else if (Array.isArray(crit) && !crit.includes("b64")) header.crit = [...crit, "b64"];
71
+ }
72
+ function decodeJWSPayload(payloadSegment, useB64, mode) {
73
+ if (!useB64) return mode === "string" ? payloadSegment : parsePayloadBytes(textEncoder.encode(payloadSegment), {
74
+ payload: mode,
75
+ family: "JWS"
76
+ });
77
+ try {
78
+ return parsePayloadSegment(payloadSegment, {
79
+ payload: mode,
80
+ family: "JWS"
81
+ });
82
+ } catch (error) {
83
+ if (error instanceof JWTError) throw error;
84
+ throw new JWTError("Invalid JWS: Payload could not be decoded.", "ERR_JWS_INVALID", error);
85
+ }
86
+ }
5
87
  function buildJWSProtectedHeader(alg, key, userHeader, options) {
6
- const safeHeader = sanitizeObjectCopy(userHeader);
88
+ const safeHeader = copyJsonObject(userHeader, {
89
+ code: "ERR_INVALID_OPTION",
90
+ message: "\"protectedHeader\" must hold only JSON values."
91
+ });
7
92
  const header = {};
8
93
  if (isJWK(key) && key.kid) header.kid = key.kid;
9
94
  if (safeHeader) Object.assign(header, safeHeader);
@@ -54,189 +139,6 @@ async function verifyWithKeyMaterial(alg, rawKeyMaterial, options) {
54
139
  }), "verify");
55
140
  return verify$1(alg, verificationKey, signature, signingInput);
56
141
  }
57
- async function signMulti(payload, signers, options = {}) {
58
- if (!Array.isArray(signers) || signers.length === 0) throw new JWTError("signMulti requires at least one signer.", "ERR_JWS_INVALID");
59
- for (const [index, signer] of signers.entries()) {
60
- assertOptionEntry(signer, {
61
- label: `signers[${index}]`,
62
- objects: ["protectedHeader", "unprotectedHeader"]
63
- });
64
- assertNotStringKey(signer.key);
65
- assertProtectedOnlyOption(signer.unprotectedHeader, {
66
- tier: "unprotected",
67
- family: "JWS"
68
- });
69
- assertProducerKeyHeaders(signer.protectedHeader, {
70
- tier: "protected",
71
- family: "JWS"
72
- });
73
- assertProducerKeyHeaders(signer.unprotectedHeader, {
74
- tier: "unprotected",
75
- family: "JWS"
76
- });
77
- assertB64Option(signer.protectedHeader);
78
- }
79
- assertProducerConfirmation(payload);
80
- const resolvedAlgs = signers.map((s, i) => _resolveSignerAlg(s, i));
81
- const b64 = _resolveB64(signers);
82
- const computedPayload = computeJwtTimeClaims(payload, options);
83
- const payloadBytes = getPlaintextBytes(computedPayload || payload);
84
- const payloadEncoded = b64 ? base64UrlStringify(payloadBytes) : parsePayloadText(payloadBytes);
85
- const wireSignatures = [];
86
- for (let i = 0; i < signers.length; i++) {
87
- const signer = signers[i];
88
- const alg = resolvedAlgs[i];
89
- const protectedHeader = buildJWSProtectedHeader(alg, signer.key, signer.protectedHeader, {
90
- payload,
91
- b64
92
- });
93
- const unprotectedHeader = sanitizeObjectCopy(signer.unprotectedHeader);
94
- _assertDisjointHeaders(protectedHeader, unprotectedHeader);
95
- const protectedHeaderEncoded = base64UrlStringify(serializeProtectedHeader(protectedHeader));
96
- const signingInputBytes = textEncoder.encode(`${protectedHeaderEncoded}.${payloadEncoded}`);
97
- let signatureBytes;
98
- try {
99
- const signingKey = await resolveSigningKey(alg, await importKey(signer.key, {
100
- alg,
101
- expect: "private"
102
- }), "sign");
103
- signatureBytes = await sign$1(alg, signingKey, signingInputBytes);
104
- } catch (error) {
105
- throw asKeyUsageError(error, alg);
106
- }
107
- const wireSig = {
108
- protected: protectedHeaderEncoded,
109
- signature: base64UrlStringify(signatureBytes)
110
- };
111
- if (unprotectedHeader && Object.keys(unprotectedHeader).length > 0) wireSig.header = unprotectedHeader;
112
- wireSignatures.push(wireSig);
113
- }
114
- return {
115
- payload: payloadEncoded,
116
- signatures: wireSignatures
117
- };
118
- }
119
- async function verifyMulti(jws, keyOrLookup, options = {}) {
120
- assertConsumerOptions(options);
121
- const payloadMode = resolvePayloadMode(options);
122
- const signatureLimit = resolveLimit(options.limits, "signatures");
123
- const keyCandidates = resolveLimit(options.limits, "keyCandidates");
124
- const general = _readGeneralSerialization(jws, signatureLimit);
125
- let lastError;
126
- let cryptoAttempted = false;
127
- for (let i = 0; i < general.signatures.length; i++) {
128
- const read = _readSignatureHeaders(general.signatures[i], i);
129
- if (read.error) {
130
- lastError = read.error;
131
- continue;
132
- }
133
- const { wireSig, protectedHeader, unprotectedHeader, effective, alg } = read;
134
- const typError = checkProtectedTyp(options.typ, protectedHeader, {
135
- family: "JWS",
136
- label: `signature[${i}]`
137
- });
138
- if (typError) {
139
- lastError = typError;
140
- continue;
141
- }
142
- let rawKeyMaterial;
143
- try {
144
- rawKeyMaterial = await resolveKeyInput(keyOrLookup, {
145
- header: effective,
146
- token: wireSig.protected ?? "",
147
- what: `signature[${i}]`
148
- });
149
- } catch (err) {
150
- lastError = asKeyLookupError(err, `signature[${i}]`);
151
- continue;
152
- }
153
- if (typeof rawKeyMaterial === "string") {
154
- lastError = stringKeyError();
155
- continue;
156
- }
157
- const algError = checkAlgAllowed(alg, rawKeyMaterial, {
158
- ctx: JWS_ALG_CTX,
159
- algorithms: options.algorithms,
160
- allowSymmetric: options.allowSymmetricJWKSet
161
- });
162
- if (algError) {
163
- lastError = algError;
164
- continue;
165
- }
166
- if (options.strictSignerMatch && !keyMatchesHeader(effective, rawKeyMaterial, {
167
- ctx: JWS_ALG_CTX,
168
- allowSymmetric: options.allowSymmetricJWKSet
169
- })) continue;
170
- let signatureBytes;
171
- try {
172
- signatureBytes = decodeSegment(wireSig.signature, {
173
- family: "JWS",
174
- what: "Signature"
175
- });
176
- } catch (err) {
177
- lastError = asJWTError(err, INVALID_JWS);
178
- continue;
179
- }
180
- const signingInputBytes = textEncoder.encode(`${wireSig.protected ?? ""}.${general.payload}`);
181
- cryptoAttempted = true;
182
- try {
183
- if (!await verifyWithKey(alg, rawKeyMaterial, {
184
- signature: signatureBytes,
185
- signingInput: signingInputBytes,
186
- kid: effective.kid,
187
- allowSymmetric: options.allowSymmetricJWKSet,
188
- keyCandidates
189
- })) {
190
- lastError = new JWTError("JWS signature verification failed.", "ERR_JWS_SIGNATURE_INVALID");
191
- continue;
192
- }
193
- validateCriticalHeadersJWS(protectedHeader, options.recognizedHeaders);
194
- const useB64 = protectedHeader.b64 !== false;
195
- const payload = decodeJWSPayload(general.payload, useB64, payloadMode);
196
- validateClaimsIfJson(payload, payloadMode, options);
197
- const result = {
198
- payload,
199
- protectedHeader,
200
- signerIndex: i
201
- };
202
- if (unprotectedHeader) result.signerHeader = unprotectedHeader;
203
- return result;
204
- } catch (err) {
205
- const error = asJWTError(err, INVALID_SIGNATURE);
206
- if (!PER_SIGNATURE_ERROR_CODES.has(error.code)) throw error;
207
- lastError = error;
208
- }
209
- }
210
- if (options.strictSignerMatch && !cryptoAttempted) throw new JWTError("No signature matched the provided key under strictSignerMatch.", "ERR_JWS_NO_MATCHING_SIGNER");
211
- throw lastError ?? new JWTError("JWS signature verification failed.", "ERR_JWS_SIGNATURE_INVALID");
212
- }
213
- async function verifyMultiAll(jws, keyResolver, options = {}) {
214
- assertConsumerOptions(options);
215
- const payloadMode = resolvePayloadMode(options);
216
- const signatureLimit = resolveLimit(options.limits, "signatures");
217
- const keyCandidates = resolveLimit(options.limits, "keyCandidates");
218
- const general = _readGeneralSerialization(jws, signatureLimit);
219
- const outcomes = [];
220
- for (let i = 0; i < general.signatures.length; i++) outcomes.push(await _verifyOneSignature(general.payload, general.signatures[i], i, keyResolver, {
221
- options,
222
- payloadMode,
223
- keyCandidates
224
- }));
225
- return outcomes;
226
- }
227
- function generalToFlattenedJWS(jws) {
228
- assertSerializationObject(jws, "JWS");
229
- if (!Array.isArray(jws.signatures)) throw new JWTError("Invalid JWS JSON Serialization: missing \"signatures\".", "ERR_JWS_INVALID_SERIALIZATION");
230
- if (jws.signatures.length !== 1) throw new JWTError(`Flattened JWS Serialization requires exactly one signature, got ${jws.signatures.length}.`, "ERR_JWS_INVALID_SERIALIZATION");
231
- const [signature] = jws.signatures;
232
- const flattened = {
233
- payload: jws.payload,
234
- signature: signature.signature
235
- };
236
- if (signature.protected !== void 0) flattened.protected = signature.protected;
237
- if (signature.header !== void 0) flattened.header = signature.header;
238
- return flattened;
239
- }
240
142
  const INVALID_JWS = {
241
143
  code: "ERR_JWS_INVALID",
242
144
  message: "Invalid JWS."
@@ -270,12 +172,13 @@ function _readSignatureHeaders(entry, index) {
270
172
  const wireSig = entry;
271
173
  if (typeof wireSig.signature !== "string") return { error: new JWTError(`Signature[${index}] is missing the "signature" field.`, "ERR_JWS_INVALID") };
272
174
  let protectedHeader;
175
+ let unprotectedHeader;
273
176
  try {
274
177
  protectedHeader = decodeProtectedHeader(wireSig.protected, "JWS");
178
+ unprotectedHeader = copyJsonObject(wireSig.header, INVALID_JWS);
275
179
  } catch (err) {
276
180
  return { error: asJWTError(err, INVALID_JWS) };
277
181
  }
278
- const unprotectedHeader = sanitizeObjectCopy(wireSig.header);
279
182
  const failed = (error) => ({
280
183
  error,
281
184
  protectedHeader,
@@ -432,8 +335,7 @@ async function _verifyOneSignature(payload, entry, index, keyResolver, context)
432
335
  }
433
336
  let decodedPayload;
434
337
  try {
435
- const useB64 = protectedHeader.b64 !== false;
436
- decodedPayload = decodeJWSPayload(payload, useB64, payloadMode);
338
+ decodedPayload = decodeJWSPayload(payload, protectedHeader.b64 !== false, payloadMode);
437
339
  } catch (err) {
438
340
  return fail(asJWTError(err, INVALID_JWS), protectedHeader, unprotectedHeader);
439
341
  }
@@ -451,6 +353,192 @@ async function _verifyOneSignature(payload, entry, index, keyResolver, context)
451
353
  if (unprotectedHeader) outcome.signerHeader = unprotectedHeader;
452
354
  return outcome;
453
355
  }
356
+ async function signMulti(payload, signers, options = {}) {
357
+ if (!Array.isArray(signers) || signers.length === 0) throw new JWTError("signMulti requires at least one signer.", "ERR_JWS_INVALID");
358
+ for (const [index, signer] of signers.entries()) {
359
+ assertOptionEntry(signer, {
360
+ label: `signers[${index}]`,
361
+ objects: ["protectedHeader", "unprotectedHeader"]
362
+ });
363
+ assertNotStringKey(signer.key);
364
+ assertProtectedOnlyOption(signer.unprotectedHeader, {
365
+ tier: "unprotected",
366
+ family: "JWS"
367
+ });
368
+ assertProducerKeyHeaders(signer.protectedHeader, {
369
+ tier: "protected",
370
+ family: "JWS"
371
+ });
372
+ assertProducerKeyHeaders(signer.unprotectedHeader, {
373
+ tier: "unprotected",
374
+ family: "JWS"
375
+ });
376
+ assertB64Option(signer.protectedHeader);
377
+ }
378
+ assertProducerConfirmation(payload);
379
+ const resolvedAlgs = signers.map((s, i) => _resolveSignerAlg(s, i));
380
+ const b64 = _resolveB64(signers);
381
+ const computedPayload = computeJwtTimeClaims(payload, options);
382
+ const payloadBytes = getPlaintextBytes(computedPayload || payload);
383
+ const payloadEncoded = b64 ? base64UrlStringify(payloadBytes) : parsePayloadText(payloadBytes);
384
+ const wireSignatures = [];
385
+ for (let i = 0; i < signers.length; i++) {
386
+ const signer = signers[i];
387
+ const alg = resolvedAlgs[i];
388
+ const protectedHeader = buildJWSProtectedHeader(alg, signer.key, signer.protectedHeader, {
389
+ payload,
390
+ b64
391
+ });
392
+ const unprotectedHeader = copyJsonObject(signer.unprotectedHeader, {
393
+ code: "ERR_INVALID_OPTION",
394
+ message: "\"unprotectedHeader\" must hold only JSON values."
395
+ });
396
+ _assertDisjointHeaders(protectedHeader, unprotectedHeader);
397
+ const protectedHeaderEncoded = base64UrlStringify(serializeProtectedHeader(protectedHeader));
398
+ const signingInputBytes = textEncoder.encode(`${protectedHeaderEncoded}.${payloadEncoded}`);
399
+ let signatureBytes;
400
+ try {
401
+ const signingKey = await resolveSigningKey(alg, await importKey(signer.key, {
402
+ alg,
403
+ expect: "private"
404
+ }), "sign");
405
+ signatureBytes = await sign$1(alg, signingKey, signingInputBytes);
406
+ } catch (error) {
407
+ throw asKeyUsageError(error, alg);
408
+ }
409
+ const wireSig = {
410
+ protected: protectedHeaderEncoded,
411
+ signature: base64UrlStringify(signatureBytes)
412
+ };
413
+ if (unprotectedHeader && Object.keys(unprotectedHeader).length > 0) wireSig.header = unprotectedHeader;
414
+ wireSignatures.push(wireSig);
415
+ }
416
+ return {
417
+ payload: payloadEncoded,
418
+ signatures: wireSignatures
419
+ };
420
+ }
421
+ async function verifyMulti(jws, keyOrLookup, options = {}) {
422
+ assertConsumerOptions(options);
423
+ const payloadMode = resolvePayloadMode(options);
424
+ const signatureLimit = resolveLimit(options.limits, "signatures");
425
+ const keyCandidates = resolveLimit(options.limits, "keyCandidates");
426
+ const general = _readGeneralSerialization(jws, signatureLimit);
427
+ let lastError;
428
+ let cryptoAttempted = false;
429
+ for (let i = 0; i < general.signatures.length; i++) {
430
+ const read = _readSignatureHeaders(general.signatures[i], i);
431
+ if (read.error) {
432
+ lastError = read.error;
433
+ continue;
434
+ }
435
+ const { wireSig, protectedHeader, unprotectedHeader, effective, alg } = read;
436
+ const typError = checkProtectedTyp(options.typ, protectedHeader, {
437
+ family: "JWS",
438
+ label: `signature[${i}]`
439
+ });
440
+ if (typError) {
441
+ lastError = typError;
442
+ continue;
443
+ }
444
+ let rawKeyMaterial;
445
+ try {
446
+ rawKeyMaterial = await resolveKeyInput(keyOrLookup, {
447
+ header: effective,
448
+ token: wireSig.protected ?? "",
449
+ what: `signature[${i}]`
450
+ });
451
+ } catch (err) {
452
+ lastError = asKeyLookupError(err, `signature[${i}]`);
453
+ continue;
454
+ }
455
+ if (typeof rawKeyMaterial === "string") {
456
+ lastError = stringKeyError();
457
+ continue;
458
+ }
459
+ const algError = checkAlgAllowed(alg, rawKeyMaterial, {
460
+ ctx: JWS_ALG_CTX,
461
+ algorithms: options.algorithms,
462
+ allowSymmetric: options.allowSymmetricJWKSet
463
+ });
464
+ if (algError) {
465
+ lastError = algError;
466
+ continue;
467
+ }
468
+ if (options.strictSignerMatch && !keyMatchesHeader(effective, rawKeyMaterial, {
469
+ ctx: JWS_ALG_CTX,
470
+ allowSymmetric: options.allowSymmetricJWKSet
471
+ })) continue;
472
+ let signatureBytes;
473
+ try {
474
+ signatureBytes = decodeSegment(wireSig.signature, {
475
+ family: "JWS",
476
+ what: "Signature"
477
+ });
478
+ } catch (err) {
479
+ lastError = asJWTError(err, INVALID_JWS);
480
+ continue;
481
+ }
482
+ const signingInputBytes = textEncoder.encode(`${wireSig.protected ?? ""}.${general.payload}`);
483
+ cryptoAttempted = true;
484
+ try {
485
+ if (!await verifyWithKey(alg, rawKeyMaterial, {
486
+ signature: signatureBytes,
487
+ signingInput: signingInputBytes,
488
+ kid: effective.kid,
489
+ allowSymmetric: options.allowSymmetricJWKSet,
490
+ keyCandidates
491
+ })) {
492
+ lastError = new JWTError("JWS signature verification failed.", "ERR_JWS_SIGNATURE_INVALID");
493
+ continue;
494
+ }
495
+ validateCriticalHeadersJWS(protectedHeader, options.recognizedHeaders);
496
+ const useB64 = protectedHeader.b64 !== false;
497
+ const payload = decodeJWSPayload(general.payload, useB64, payloadMode);
498
+ validateClaimsIfJson(payload, payloadMode, options);
499
+ const result = {
500
+ payload,
501
+ protectedHeader,
502
+ signerIndex: i
503
+ };
504
+ if (unprotectedHeader) result.signerHeader = unprotectedHeader;
505
+ return result;
506
+ } catch (err) {
507
+ const error = asJWTError(err, INVALID_SIGNATURE);
508
+ if (!PER_SIGNATURE_ERROR_CODES.has(error.code)) throw error;
509
+ lastError = error;
510
+ }
511
+ }
512
+ if (options.strictSignerMatch && !cryptoAttempted) throw new JWTError("No signature matched the provided key under strictSignerMatch.", "ERR_JWS_NO_MATCHING_SIGNER");
513
+ throw lastError ?? new JWTError("JWS signature verification failed.", "ERR_JWS_SIGNATURE_INVALID");
514
+ }
515
+ async function verifyMultiAll(jws, keyResolver, options = {}) {
516
+ assertConsumerOptions(options);
517
+ const payloadMode = resolvePayloadMode(options);
518
+ const signatureLimit = resolveLimit(options.limits, "signatures");
519
+ const keyCandidates = resolveLimit(options.limits, "keyCandidates");
520
+ const general = _readGeneralSerialization(jws, signatureLimit);
521
+ const outcomes = [];
522
+ for (let i = 0; i < general.signatures.length; i++) outcomes.push(await _verifyOneSignature(general.payload, general.signatures[i], i, keyResolver, {
523
+ options,
524
+ payloadMode,
525
+ keyCandidates
526
+ }));
527
+ return outcomes;
528
+ }
529
+ function generalToFlattenedJWS(jws) {
530
+ assertSerializationObject(jws, "JWS");
531
+ if (!Array.isArray(jws.signatures)) throw new JWTError("Invalid JWS JSON Serialization: missing \"signatures\".", "ERR_JWS_INVALID_SERIALIZATION");
532
+ if (jws.signatures.length !== 1) throw new JWTError(`Flattened JWS Serialization requires exactly one signature, got ${jws.signatures.length}.`, "ERR_JWS_INVALID_SERIALIZATION");
533
+ const [signature] = jws.signatures;
534
+ const flattened = {
535
+ payload: jws.payload,
536
+ signature: signature.signature
537
+ };
538
+ if (signature.protected !== void 0) flattened.protected = signature.protected;
539
+ if (signature.header !== void 0) flattened.header = signature.header;
540
+ return flattened;
541
+ }
454
542
  async function sign(payload, key, options = {}) {
455
543
  assertOptionEntry(options, {
456
544
  label: "options",
@@ -538,8 +626,7 @@ async function verify(jws, key, options = {}) {
538
626
  keyCandidates: keyCandidateLimit
539
627
  })) throw new JWTError("JWS signature verification failed.", "ERR_JWS_SIGNATURE_INVALID");
540
628
  validateCriticalHeadersJWS(protectedHeader, options.recognizedHeaders);
541
- const useB64 = protectedHeader.b64 !== false;
542
- const payload = decodeJWSPayload(payloadEncoded, useB64, payloadMode);
629
+ const payload = decodeJWSPayload(payloadEncoded, protectedHeader.b64 !== false, payloadMode);
543
630
  validateClaimsIfJson(payload, payloadMode, options);
544
631
  return {
545
632
  payload,
@@ -1,4 +1,5 @@
1
- import { Duration, JOSEPayload, JOSEPayloadMode, JWTClaimValidationOptions, JWTClaims, JWTErrorCode, JWTTimeClaimOptions, PayloadOf } from "./index.mjs";
1
+ import { JWTErrorCode } from "./error.mjs";
2
+ import { Duration, JOSEPayload, JOSEPayloadMode, JWTClaimValidationOptions, JWTClaims, JWTTimeClaimOptions, PayloadOf } from "./index.mjs";
2
3
  /** Set `typ: "JWT"` on a header without `typ` when the payload is an object (not a `Uint8Array`). */
3
4
  export declare function applyTypDefault<T extends {
4
5
  typ?: string;