unjwt 0.8.2 → 0.8.4

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.
@@ -91,7 +91,7 @@ function concatUint8Arrays(...arrays) {
91
91
  return result;
92
92
  }
93
93
  function uint32be(value) {
94
- if (value < 0 || value >= 2 ** 32) throw new JWTError(`value must be >= 0 and <= ${2 ** 32 - 1}. Received ${value}`, "ERR_INVALID_OPTION");
94
+ if (value < 0 || value >= 2 ** 32) throw new JWTError(`value must be >= 0 and <= ${2 ** 32 - 1} (got ${value})`, "ERR_INVALID_OPTION");
95
95
  return new Uint8Array([
96
96
  value >>> 24,
97
97
  value >>> 16,
@@ -142,7 +142,7 @@ function parseDurationSeconds(value) {
142
142
  }
143
143
  if (typeof value === "string") {
144
144
  const match = value.match(DURATION_REGEX);
145
- if (!match || !match[1]) throw new JWTError("Invalid duration format. Must be an integer or a string like '10m', '2h', '7D', etc.", "ERR_INVALID_OPTION");
145
+ if (!match || !match[1]) throw new JWTError("Duration must be an integer or a string like '10m' or '7D'.", "ERR_INVALID_OPTION");
146
146
  const n = Number.parseInt(match[1], 10);
147
147
  const unit = match[2] || "s";
148
148
  return n * TIME_CONSTANTS[unit];
@@ -155,7 +155,7 @@ function computeDurationInSeconds(duration) {
155
155
  return seconds;
156
156
  }
157
157
  function expiredClaimMessage(exp) {
158
- return `JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(exp * 1e3)).toISOString()})`;
158
+ return `JWT "exp" is in the past (${(/* @__PURE__ */ new Date(exp * 1e3)).toISOString()}).`;
159
159
  }
160
160
  function validateJwtClaims(jwtClaims, options = {}) {
161
161
  assertClaimValidationOptions(options);
@@ -171,29 +171,29 @@ function validateJwtClaims(jwtClaims, options = {}) {
171
171
  for (const claimName of allRequiredClaims) if (!Object.hasOwn(jwtClaims, claimName)) missingClaims.push(claimName);
172
172
  if (missingClaims.length > 0) throw new JWTError(`Missing required JWT Claims: ${missingClaims.join(", ")}`, "ERR_JWT_CLAIM_MISSING");
173
173
  }
174
- _assertNumericDate(jwtClaims.exp, "exp", "Expiration Time");
175
- _assertNumericDate(jwtClaims.nbf, "nbf", "Not Before");
176
- _assertNumericDate(jwtClaims.iat, "iat", "Issued At");
174
+ _assertNumericDate(jwtClaims.exp, "exp");
175
+ _assertNumericDate(jwtClaims.nbf, "nbf");
176
+ _assertNumericDate(jwtClaims.iat, "iat");
177
177
  if (options.issuer !== void 0) {
178
178
  const expectedIssuers = maybeArray(options.issuer);
179
- if (!jwtClaims.iss || !expectedIssuers.includes(jwtClaims.iss)) throw new JWTError(`Invalid JWT "iss" (Issuer) Claim: Expected ${expectedIssuers.join(" or ")}, got ${jwtClaims.iss}`, "ERR_JWT_CLAIM_INVALID");
179
+ if (!jwtClaims.iss || !expectedIssuers.includes(jwtClaims.iss)) throw new JWTError(`Invalid JWT "iss": expected ${expectedIssuers.join(" or ")}, got ${jwtClaims.iss}`, "ERR_JWT_CLAIM_INVALID");
180
180
  }
181
- if (options.subject !== void 0 && jwtClaims.sub !== options.subject) throw new JWTError(`Invalid JWT "sub" (Subject) Claim: Expected ${options.subject}, got ${jwtClaims.sub}`, "ERR_JWT_CLAIM_INVALID");
181
+ if (options.subject !== void 0 && jwtClaims.sub !== options.subject) throw new JWTError(`Invalid JWT "sub": expected ${options.subject}, got ${jwtClaims.sub}`, "ERR_JWT_CLAIM_INVALID");
182
182
  if (options.audience !== void 0) {
183
183
  const expectedAudiences = maybeArray(options.audience);
184
184
  const claimAudience = maybeArray(jwtClaims.aud || []);
185
- if (!claimAudience.some((aud) => expectedAudiences.includes(aud))) throw new JWTError(`Invalid JWT "aud" (Audience) Claim: Expected ${expectedAudiences.join(" or ")}, got ${claimAudience.join(", ")}`, "ERR_JWT_CLAIM_INVALID");
185
+ if (!claimAudience.some((aud) => expectedAudiences.includes(aud))) throw new JWTError(`Invalid JWT "aud": expected ${expectedAudiences.join(" or ")}, got ${claimAudience.join(", ")}`, "ERR_JWT_CLAIM_INVALID");
186
186
  }
187
- if (typeof jwtClaims.nbf === "number" && jwtClaims.nbf > currentTime + clockTolerance) throw new JWTError(`JWT "nbf" (Not Before) Claim validation failed: Token is not yet valid (nbf: ${(/* @__PURE__ */ new Date(jwtClaims.nbf * 1e3)).toISOString()})`, "ERR_JWT_NBF");
187
+ if (typeof jwtClaims.nbf === "number" && jwtClaims.nbf > currentTime + clockTolerance) throw new JWTError(`JWT "nbf" is in the future (${(/* @__PURE__ */ new Date(jwtClaims.nbf * 1e3)).toISOString()}).`, "ERR_JWT_NBF");
188
188
  if (typeof jwtClaims.exp === "number" && jwtClaims.exp <= currentTime - clockTolerance) throw new JWTError(expiredClaimMessage(jwtClaims.exp), "ERR_JWT_EXPIRED", {
189
189
  jti: jwtClaims.jti,
190
190
  iat: jwtClaims.iat,
191
191
  exp: jwtClaims.exp
192
192
  });
193
193
  if (options.maxTokenAge !== void 0) {
194
- if (typeof jwtClaims.iat !== "number") throw new JWTError("JWT \"iat\" (Issued At) Claim must be a number when maxTokenAge is set.", "ERR_JWT_CLAIM_INVALID");
195
- if (jwtClaims.iat > currentTime + clockTolerance) throw new JWTError(`JWT "iat" (Issued At) Claim validation failed: Token was issued in the future (iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`, "ERR_JWT_CLAIM_INVALID");
196
- if (jwtClaims.iat < currentTime - computeDurationInSeconds(options.maxTokenAge) - clockTolerance) throw new JWTError(`JWT "iat" (Issued At) Claim validation failed: Token is too old (maxTokenAge: ${options.maxTokenAge}, iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
194
+ if (typeof jwtClaims.iat !== "number") throw new JWTError("JWT \"iat\" must be a number when maxTokenAge is set.", "ERR_JWT_CLAIM_INVALID");
195
+ if (jwtClaims.iat > currentTime + clockTolerance) throw new JWTError(`JWT "iat" is in the future (${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()}).`, "ERR_JWT_CLAIM_INVALID");
196
+ if (jwtClaims.iat < currentTime - computeDurationInSeconds(options.maxTokenAge) - clockTolerance) throw new JWTError(`JWT "iat" is older than maxTokenAge ${options.maxTokenAge}.`, "ERR_JWT_EXPIRED", {
197
197
  jti: jwtClaims.jti,
198
198
  iat: jwtClaims.iat,
199
199
  exp: jwtClaims.exp
@@ -238,10 +238,10 @@ function _assertExpectedValues(value, name) {
238
238
  function _isNonEmptyString(value) {
239
239
  return typeof value === "string" && value !== "";
240
240
  }
241
- function _assertNumericDate(value, claim, label) {
241
+ function _assertNumericDate(value, claim) {
242
242
  if (value === void 0) return;
243
- if (typeof value !== "number" || !Number.isFinite(value)) throw new JWTError(`JWT "${claim}" (${label}) Claim must be a number.`, "ERR_JWT_CLAIM_INVALID");
244
- if (value < 0) throw new JWTError(`JWT "${claim}" (${label}) Claim must be a non-negative NumericDate.`, "ERR_JWT_CLAIM_INVALID");
243
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new JWTError(`JWT "${claim}" must be a number.`, "ERR_JWT_CLAIM_INVALID");
244
+ if (value < 0) throw new JWTError(`JWT "${claim}" must be a non-negative NumericDate.`, "ERR_JWT_CLAIM_INVALID");
245
245
  }
246
246
  function applyTypDefault(header, payload) {
247
247
  const declared = header.typ;
@@ -283,29 +283,32 @@ function getPlaintextBytes(payload) {
283
283
  throw new JWTError("Plaintext must be a string, Uint8Array, or a JSON-serializable object.", "ERR_INVALID_OPTION");
284
284
  }
285
285
  function computeJwtTimeClaims(payload, options) {
286
- const { currentDate, expiresIn, expiresAt, notBeforeIn, notBeforeAt } = options ?? {};
287
- if (expiresIn !== void 0 && expiresAt !== void 0) throw new JWTError("\"expiresIn\" and \"expiresAt\" are mutually exclusive; pass only one.", "ERR_JWT_CLAIM_INVALID");
288
- if (notBeforeIn !== void 0 && notBeforeAt !== void 0) throw new JWTError("\"notBeforeIn\" and \"notBeforeAt\" are mutually exclusive; pass only one.", "ERR_JWT_CLAIM_INVALID");
286
+ const { currentDate, issuedAt, expiresIn, expiresAt, notBeforeIn, notBeforeAt } = options ?? {};
287
+ if (issuedAt !== void 0 && typeof issuedAt !== "boolean" && !(issuedAt instanceof Date)) throw new JWTError("\"issuedAt\" must be a boolean or a Date.", "ERR_INVALID_OPTION");
288
+ if (expiresIn !== void 0 && expiresAt !== void 0) throw new JWTError("\"expiresIn\" and \"expiresAt\" are mutually exclusive.", "ERR_JWT_CLAIM_INVALID");
289
+ if (notBeforeIn !== void 0 && notBeforeAt !== void 0) throw new JWTError("\"notBeforeIn\" and \"notBeforeAt\" are mutually exclusive.", "ERR_JWT_CLAIM_INVALID");
289
290
  const hasExpHint = expiresIn !== void 0 || expiresAt !== void 0;
290
291
  const hasNbfHint = notBeforeIn !== void 0 || notBeforeAt !== void 0;
291
- if (!hasExpHint && !hasNbfHint) return void 0;
292
+ const hasIatHint = issuedAt === true || issuedAt instanceof Date;
293
+ if (!hasExpHint && !hasNbfHint && !hasIatHint) return void 0;
292
294
  if (!payload || typeof payload !== "object" || payload instanceof Uint8Array) return void 0;
293
295
  const wantsExp = hasExpHint && (!("exp" in payload) || payload.exp === void 0);
294
296
  const wantsNbf = hasNbfHint && (!("nbf" in payload) || payload.nbf === void 0);
295
- if (!wantsExp && !wantsNbf) return void 0;
297
+ const wantsIat = hasIatHint && (!("iat" in payload) || payload.iat === void 0);
298
+ if (!wantsExp && !wantsNbf && !wantsIat) return void 0;
296
299
  const now = Math.round((currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
297
300
  const claims = { ...payload };
298
- claims.iat ||= now;
301
+ claims.iat ||= issuedAt instanceof Date ? Math.floor(_instantOf(issuedAt, "issuedAt") / 1e3) : now;
299
302
  if (wantsExp) {
300
303
  if (expiresAt !== void 0) claims.exp = Math.floor(_instantOf(expiresAt, "expiresAt") / 1e3);
301
- else if (expiresIn !== void 0) claims.exp = claims.iat + computeDurationInSeconds(expiresIn);
304
+ else if (expiresIn !== void 0) claims.exp = now + computeDurationInSeconds(expiresIn);
302
305
  }
303
306
  if (wantsNbf) {
304
307
  if (notBeforeAt !== void 0) claims.nbf = Math.floor(_instantOf(notBeforeAt, "notBeforeAt") / 1e3);
305
308
  else if (notBeforeIn !== void 0) {
306
309
  const offset = parseDurationSeconds(notBeforeIn);
307
310
  if (offset < 0) throw new JWTError("\"notBeforeIn\" must be zero or a positive duration.", "ERR_JWT_CLAIM_INVALID");
308
- claims.nbf = claims.iat + offset;
311
+ claims.nbf = now + offset;
309
312
  }
310
313
  }
311
314
  return claims;
@@ -381,7 +384,7 @@ function validateCriticalHeaders(protectedHeader, options) {
381
384
  const { family, recognizedHeaders = [] } = options;
382
385
  assertCritShape(crit, { family });
383
386
  for (const param of crit) {
384
- if (!Object.hasOwn(protectedHeader, param)) throw new JWTError(`Critical header parameter "${param}" listed in "crit" but not present in the protected header.`, `ERR_${family}_INVALID`);
387
+ if (!Object.hasOwn(protectedHeader, param)) throw new JWTError(`Critical header parameter "${param}" is missing from the protected header.`, `ERR_${family}_INVALID`);
385
388
  if (!recognizedHeaders.includes(param) && !BASE_PROCESSED[family].includes(param)) throw new JWTError(`Unprocessed critical header parameters: ${param}`, `ERR_${family}_INVALID`);
386
389
  }
387
390
  }
@@ -210,13 +210,15 @@ export interface JWTClaimValidationOptions {
210
210
  }
211
211
  /** Time claims a producer computes; a claim already in the payload is kept as given. */
212
212
  export interface JWTTimeClaimOptions {
213
- /** Reference moment for `iat` and relative durations. @default new Date() */
213
+ /** The clock: `iat` when written, and the base of `expiresIn` / `notBeforeIn`. @default new Date() */
214
214
  currentDate?: Date;
215
- /** Duration until `exp`; exclusive with `expiresAt`. */
215
+ /** Writes `iat` on its own: `true` as `currentDate`, a `Date` as that instant; `false` is unset. `exp`/`nbf` options write it either way. */
216
+ issuedAt?: boolean | Date;
217
+ /** Duration from `currentDate` until `exp`; exclusive with `expiresAt`. */
216
218
  expiresIn?: ExpiresIn;
217
219
  /** Absolute `exp`; exclusive with `expiresIn`. */
218
220
  expiresAt?: Date;
219
- /** Duration from `iat` until `nbf` (`0` sets `nbf = iat`); exclusive with `notBeforeAt`. */
221
+ /** Duration from `currentDate` until `nbf` (`0` sets `nbf` to it); exclusive with `notBeforeAt`. */
220
222
  notBeforeIn?: ExpiresIn;
221
223
  /** Absolute `nbf` (RFC 7519 §4.1.5); exclusive with `notBeforeIn`. */
222
224
  notBeforeAt?: Date;
@@ -49,6 +49,7 @@ export declare function encrypt(payload: JOSEPayload, key: CryptoKey | JWEEncryp
49
49
  /**
50
50
  * Decrypt a JWE Compact Serialization string; `key` may be a lookup function.
51
51
  * The payload type follows `options.payload` (claims `T` by default).
52
+ * A compact JWE has five sections (RFC 7516 §3).
52
53
  */
53
54
  export declare function decrypt<T extends object = JWTClaims>(jwe: string, key: CryptoKey | JWKSet | JWEDecryptJWK | string | Uint8Array<ArrayBuffer> | JWEKeyLookup, options?: JWEDecryptOptions<"json"> & {
54
55
  forceUint8Array?: false;
@@ -1,7 +1,8 @@
1
1
  import { sanitizeObjectCopy, secureRandomBytes, textEncoder } from "./libs/unsecure.mjs";
2
2
  import { JWTError, applyTypDefault, base64UrlStringify, computeJwtTimeClaims, getPlaintextBytes, isJWK, isJWKSet, validateCriticalHeadersJWE } from "./_jwt-internal.mjs";
3
3
  import { isContentEncryptionAlgorithm, isPBES2Algorithm } from "./digest.mjs";
4
- import { DEFAULT_PBES2_ITERATIONS, JWE_ALG_CTX, JWE_DIRECT_ALGS, asDecryptionError, asKeyLookupError, assertCompactToken, assertConsumerOptions, assertDirectKeyRole, assertEntryLimit, assertJWEAlgSupported, assertJWEEncSupported, assertJWKSetCandidates, assertNoZipOption, assertOptionEntry, assertPBES2Params, assertProducerCEK, assertProducerConfirmation, assertProducerCrit, assertProducerIV, assertProducerKeyHeaders, assertProtectedOnlyOption, assertSerializationObject, buildECDHAgreementParams, buildJWKSetFilter, buildUnwrapKeyOptions, checkAlgAllowed, checkJWEAlgSupported, checkJWEContentSegments, checkJWEDirectEncryptedKey, checkJWEEncAllowed, checkJWEEncSupported, checkJWEUnprotectedCrit, checkJWEZip, checkProtectedTyp, checkSerializationEntry, createPBES2Budget, decodeJWEPayload, decodeProtectedHeader, decodeSegment, decrypt as decrypt$1, encrypt as encrypt$1, encryptKey, exportReturnedCek, generateCEK, generateIV, getJWKsFromSet, importKey, isJWEAlgSupported, keyMatchesHeader, resolveKeyInput, resolveLimit, resolvePasswordKey, resolvePayloadMode, serializeProtectedHeader, unwrapKey, validateClaimsIfJson } from "./jwk.mjs";
4
+ import { DEFAULT_PBES2_ITERATIONS, JWE_ALG_CTX, JWE_DIRECT_ALGS, asDecryptionError, asKeyLookupError, assertCompactToken, assertConsumerOptions, assertDirectKeyRole, assertEntryLimit, assertJWEAlgSupported, assertJWEEncSupported, assertJWKSetCandidates, assertNoZipOption, assertOptionEntry, assertPBES2Params, assertProducerCEK, assertProducerConfirmation, assertProducerCrit, assertProducerIV, assertProducerKeyHeaders, assertProtectedOnlyOption, assertSerializationObject, buildECDHAgreementParams, buildJWKSetFilter, buildUnwrapKeyOptions, checkAlgAllowed, checkJWEAlgSupported, checkJWEContentSegments, checkJWEDirectEncryptedKey, checkJWEEncAllowed, checkJWEEncSupported, checkJWEUnprotectedCrit, checkJWEZip, checkProtectedTyp, checkSerializationEntry, createPBES2Budget, decodeJWEPayload, decodeProtectedHeader, decodeSegment, decrypt as decrypt$1, encrypt as encrypt$1, encryptKey, exportReturnedCek, generateCEK, generateIV, getJWKsFromSet, importKey, isJWEAlgSupported, keyMatchesHeader, resolveKeyInput, resolveLimit, resolvePasswordKey, resolvePayloadMode, serializeProtectedHeader, validateClaimsIfJson } from "./_import-key.mjs";
5
+ import { unwrapKey } from "../core/jwk.mjs";
5
6
  function buildKeyManagementParams(alg, source) {
6
7
  const { keyManagementIV, p2s, p2c, ecdh } = source;
7
8
  const isPBES2 = alg.startsWith("PBES2");
@@ -339,7 +340,7 @@ function _resolveRecipientAlg(key, index) {
339
340
  const declared = key.alg;
340
341
  if (!declared) throw new JWTError(`Cannot infer "alg" for recipient[${index}]: the JWK has no "alg" property.`, "ERR_JWE_RECIPIENT_ALG_INFERENCE");
341
342
  const alg = key.kty === "oct" && isContentEncryptionAlgorithm(declared) ? "dir" : declared;
342
- if (JWE_DIRECT_ALGS.has(alg)) throw new JWTError(`Recipient[${index}] resolves to "${alg}", which requires exactly one recipient. Use encrypt() instead.`, "ERR_JWE_ALG_FORBIDDEN_IN_MULTI");
343
+ if (JWE_DIRECT_ALGS.has(alg)) throw new JWTError(`Recipient[${index}] "${alg}" allows a single recipient, use encrypt().`, "ERR_JWE_ALG_FORBIDDEN_IN_MULTI");
343
344
  assertJWEAlgSupported(alg);
344
345
  return alg;
345
346
  }
@@ -357,7 +358,7 @@ function _assertDisjointHeaders(protectedHeader, sharedUnprotected, recipientHea
357
358
  const check = (obj) => {
358
359
  if (!obj) return;
359
360
  for (const k of Object.keys(obj)) {
360
- if (seen.has(k)) throw new JWTError(`JWE header parameter "${k}" appears in multiple header tiers; protected, shared unprotected, and per-recipient headers must be disjoint (RFC 7516 §7.2.1).`, "ERR_JWE_HEADER_PARAMS_NOT_DISJOINT");
361
+ if (seen.has(k)) throw new JWTError(`Invalid JWE: header parameter "${k}" is set in more than one header.`, "ERR_JWE_HEADER_PARAMS_NOT_DISJOINT");
361
362
  seen.add(k);
362
363
  }
363
364
  };
@@ -374,7 +375,7 @@ function _mergeHeaders(protectedHeader, sharedUnprotected, recipientHeader) {
374
375
  function _normalizeSerialization(input) {
375
376
  assertSerializationObject(input, "JWE");
376
377
  if ("recipients" in input) {
377
- if ("encrypted_key" in input || "header" in input) throw new JWTError("Invalid JWE JSON Serialization: \"recipients\" must not be combined with a top-level \"header\" or \"encrypted_key\".", "ERR_JWE_INVALID_SERIALIZATION");
378
+ if ("encrypted_key" in input || "header" in input) throw new JWTError("Invalid JWE JSON Serialization: \"recipients\" excludes top-level \"header\" and \"encrypted_key\".", "ERR_JWE_INVALID_SERIALIZATION");
378
379
  return input;
379
380
  }
380
381
  const flattened = input;
@@ -416,7 +417,7 @@ async function encrypt(payload, key, options = {}) {
416
417
  else if (cekRole) alg = "dir";
417
418
  else if (isJWK(key)) alg = key.alg;
418
419
  }
419
- if (!alg) throw new JWTError("JWE \"alg\" (Key Management Algorithm) must be provided in options or inferable from the key", "ERR_JWE_ALG_MISSING");
420
+ if (!alg) throw new JWTError("JWE \"alg\" must be provided in options or inferable from the key", "ERR_JWE_ALG_MISSING");
420
421
  if (!enc) {
421
422
  if (alg === "dir") {
422
423
  enc = cekRole ?? (isJWK(key) ? key.enc : void 0);
@@ -466,7 +467,7 @@ async function decrypt(jwe, key, options = {}) {
466
467
  const keyCandidateLimit = resolveLimit(options.limits, "keyCandidates");
467
468
  const pbes2Budget = createPBES2Budget(resolveLimit(options.limits, "pbes2Iterations"));
468
469
  const parts = jwe.split(".");
469
- if (parts.length !== 5) throw new JWTError("Invalid JWE: Must contain five sections (RFC7516, section-3).", "ERR_JWE_INVALID");
470
+ if (parts.length !== 5) throw new JWTError("Invalid JWE: Must contain five sections.", "ERR_JWE_INVALID");
470
471
  const [protectedHeaderEncoded, encryptedKeyEncoded, ivEncoded, ciphertextEncoded, authTagEncoded] = parts;
471
472
  const protectedHeader = decodeProtectedHeader(protectedHeaderEncoded, "JWE");
472
473
  assertHeaderAlgorithms(protectedHeader);
@@ -550,7 +551,7 @@ function _buildJWEHeader(alg, enc, key, userHeader, keyManagementParams, payload
550
551
  return header;
551
552
  }
552
553
  function assertHeaderAlgorithms(header) {
553
- if (!header.alg || !header.enc) throw new JWTError("Invalid JWE: Protected header must be an object with \"alg\" and \"enc\" properties.", "ERR_JWE_INVALID");
554
+ if (!header.alg || !header.enc) throw new JWTError("Invalid JWE: Protected header must carry \"alg\" and \"enc\".", "ERR_JWE_INVALID");
554
555
  assertJWEAlgSupported(header.alg);
555
556
  assertJWEEncSupported(header.enc);
556
557
  }
@@ -0,0 +1,205 @@
1
+ import { ContentEncryptionAlgorithm, DeriveJWKOptions, DeriveJWKReturn, DeriveKeyOptions, DeriveKeyReturn, GenerateJWKOptions, GenerateJWKReturn, GenerateKeyAlgorithm, GenerateKeyOptions, GenerateKeyReturn, ImportedKey, ImportedPEMJWK, JWK, JWKAlgorithm, JWKCacheAdapter, JWKMetadata, JWKPEMAlgorithm, JWKSet, JWK_Asymmetric, JWK_ECDH_ES, JWK_EC_Private, JWK_EC_Public, JWK_PBES2, JWK_oct, KeyManagementAlgorithm, PEMType, ToPublicJWKReturn, UnwrapKeyOptions, UnwrapKeyResult, UnwrappingKeyFor, WrapKeyOptions, WrapKeyResult, WrappingKeyFor } from "./index.mjs";
2
+ import "./index2.mjs";
3
+ /**
4
+ * Default JWK import cache, keyed by JWK object identity: a copied JWK misses it, and a JWK
5
+ * mutated in place keeps serving the old `CryptoKey`. Rotate by replacing the object.
6
+ */
7
+ export declare class WeakMapJWKCache implements JWKCacheAdapter {
8
+ private readonly _map;
9
+ get(jwk: JWK, alg: JWKAlgorithm): CryptoKey | undefined;
10
+ set(jwk: JWK, alg: JWKAlgorithm, key: CryptoKey): void;
11
+ }
12
+ /**
13
+ * Replaces the JWK import cache used by `importKey`, or disables it with `false`.
14
+ *
15
+ * A cache shared across JWK objects keys on the RFC 7638 thumbprint, the pair half (`d` present)
16
+ * and `alg`: the thumbprint covers public members only, and a `kid` may name different keys.
17
+ * `get` and `set` may return promises.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const cacheKey = async (jwk: JWK, alg: JWKAlgorithm) =>
22
+ * `${await thumbprint(jwk)}:${"d" in jwk ? "private" : "public"}:${alg}`;
23
+ * const map = new Map<string, CryptoKey>();
24
+ * configureJWKCache({
25
+ * get: async (jwk, alg) => map.get(await cacheKey(jwk, alg)),
26
+ * set: async (jwk, alg, key) => {
27
+ * map.set(await cacheKey(jwk, alg), key);
28
+ * },
29
+ * });
30
+ * ```
31
+ */
32
+ export declare function configureJWKCache(cache: JWKCacheAdapter | false): void;
33
+ /** Resets the JWK import cache to a fresh `WeakMapJWKCache`. */
34
+ export declare function clearJWKCache(): void;
35
+ /** Imports the secret of an `oct` JWK as a `CryptoKey` under `algorithm` and `usage`. */
36
+ export declare function importKey(key: JWK_oct, options: {
37
+ asCryptoKey: true;
38
+ /** Algorithm to import the key as (e.g. `{ name: "AES-GCM", length: 256 }`). */
39
+ algorithm: Parameters<typeof crypto.subtle.importKey>[2];
40
+ usage: KeyUsage[];
41
+ /** Mark the key as extractable. Defaults to `false`. */
42
+ extractable?: boolean;
43
+ }): Promise<CryptoKey>;
44
+ /**
45
+ * Imports key material: a `CryptoKey` or `Uint8Array` is returned as is, an `oct` JWK as its raw
46
+ * bytes, an asymmetric JWK as a `CryptoKey` under its own `alg` or the hint. A string throws
47
+ * `ERR_JWK_INVALID`.
48
+ *
49
+ * `expect` and `extractable` apply to asymmetric keys only; `expect` rejects the other pair half
50
+ * with `ERR_JWK_INVALID`. A JWK's `alg` is authoritative (RFC 7517 §4.4): a different requested
51
+ * `alg` throws `ERR_JWK_INVALID`, except an `oct` JWK declaring a content encryption `alg` under
52
+ * `"dir"`.
53
+ */
54
+ export declare function importKey<K extends CryptoKey | JWK | Uint8Array<ArrayBuffer>>(key: K, options?: JWKAlgorithm | {
55
+ /** Algorithm hint. Required when `key.alg` is absent on an asymmetric JWK. */
56
+ alg?: JWKAlgorithm;
57
+ /** Reject on shape mismatch instead of silently importing. */
58
+ expect?: "public" | "private";
59
+ /** Defaults to `false` for private, `true` for public material; JWK `ext: false` wins. */
60
+ extractable?: boolean;
61
+ }): Promise<ImportedKey<K>>;
62
+ /**
63
+ * Returns the JWKs of a JWK Set, dropping non-JWK entries, optionally narrowed by `filter`.
64
+ * An invalid set throws `ERR_JWK_INVALID`.
65
+ */
66
+ export declare function getJWKsFromSet(jwkSet: JWKSet, filter?: (jwk: JWK) => boolean): JWK[];
67
+ /**
68
+ * Generates a key for a JWA algorithm: a `CryptoKey` for symmetric algorithms, raw bytes for
69
+ * the `A*CBC-HS*` composites, a `CryptoKeyPair` for asymmetric ones; with `toJWK: true`, a JWK
70
+ * (or JWK pair) named by the `kid` option or its RFC 7638 thumbprint. PBES2, `"dir"` and `"none"` are refused.
71
+ *
72
+ * @param options
73
+ * - `extractable?` — defaults to `true`; ignored when `toJWK` is set.
74
+ * - `kid?` — with `toJWK`, a string set verbatim or `false` for no `kid`, either skipping the
75
+ * thumbprint digest; anything else throws `ERR_INVALID_OPTION`.
76
+ * - `keyUsage?` — narrows the usages derived from `alg`; leaving either half of a pair with
77
+ * no usage throws `ERR_INVALID_OPTION`.
78
+ * - `modulusLength?` — RSA only; defaults to `2048`.
79
+ * - `publicExponent?` — RSA only; big-endian bytes, defaults to `0x010001`.
80
+ * - `namedCurve?` — EC/OKP only; defaults to `"P-256"` / `"Ed25519"`. One that contradicts
81
+ * the curve `ES*` or `Ed25519` names throws `ERR_INVALID_OPTION`.
82
+ */
83
+ export declare function generateKey<TAlg extends GenerateKeyAlgorithm, ToJWK extends boolean = false, Kid extends string | false = string>(alg: TAlg, options?: GenerateKeyOptions<ToJWK, Kid>): Promise<GenerateKeyReturn<TAlg, GenerateKeyOptions<ToJWK, Kid>>>;
84
+ /**
85
+ * Generates a JWK (or JWK pair) for a JWA algorithm, its `kid` the caller's, none with
86
+ * `kid: false`, or else its RFC 7638 thumbprint. PBES2, `"dir"` and `"none"` are refused.
87
+ *
88
+ * @param options
89
+ * - `keyUsage?`, `modulusLength?` (default `2048`), `publicExponent?` (default `0x010001`),
90
+ * `namedCurve?` (default `"P-256"` / `"Ed25519"`); a narrowing that empties a pair half or
91
+ * a curve that contradicts `alg` throws `ERR_INVALID_OPTION`.
92
+ * - `kid?` — a string set verbatim or `false` for no `kid`, either skipping the thumbprint
93
+ * digest; anything else throws `ERR_INVALID_OPTION`.
94
+ * - JWK metadata (`use`, `x5c`, `x5t`, `x5t#S256`, `x5u`) is merged onto the result;
95
+ * `alg`, `kty`, `key_ops`, `ext` and key-material members are reserved (`ERR_INVALID_OPTION`).
96
+ */
97
+ export declare function generateJWK<TAlg extends GenerateKeyAlgorithm, Kid extends string | false = string>(alg: TAlg, options?: GenerateJWKOptions<Kid> & Omit<JWKMetadata, "kid">): Promise<GenerateJWKReturn<TAlg, Kid>>;
98
+ /**
99
+ * Derives an AES-KW key from a password with PBKDF2 per PBES2 (RFC 7518 §4.8).
100
+ *
101
+ * @param options
102
+ * - `alg` — a PBES2 algorithm; any other throws `ERR_JWK_UNSUPPORTED`.
103
+ * - `salt` — PBKDF2 salt (`p2s`), at least 8 octets (RFC 7518 §4.8.1.1).
104
+ * - `iterations` — PBKDF2 count (`p2c`), an integer of at least 1000 (RFC 7518 §4.8.1.2);
105
+ * anything else throws `ERR_INVALID_OPTION`.
106
+ * - `keyUsage?` — defaults to `["wrapKey", "unwrapKey"]`.
107
+ * - `extractable?` — defaults to `false`; ignored when `toJWK` is set.
108
+ * - `toJWK?` — return a `JWK_oct` carrying the inner AES-KW `alg` instead of a `CryptoKey`.
109
+ */
110
+ export declare function deriveKeyFromPassword<TOptions extends DeriveKeyOptions>(password: string | Uint8Array<ArrayBuffer>, options: TOptions): Promise<DeriveKeyReturn<TOptions, TOptions["alg"]>>;
111
+ /**
112
+ * Derives a `JWK_oct` from a password per PBES2 (RFC 7518 §4.8); its `alg` is the inner AES-KW
113
+ * variant and its `kid` the caller's, none with `kid: false`, or else its RFC 7638 thumbprint.
114
+ *
115
+ * @param options
116
+ * - `alg` (PBES2 only, else `ERR_JWK_UNSUPPORTED`), `salt` (at least 8 octets), `iterations`
117
+ * (integer of at least 1000, else `ERR_INVALID_OPTION`), `keyUsage?` (default
118
+ * `["wrapKey", "unwrapKey"]`).
119
+ * - `kid?` — a string set verbatim or `false` for no `kid`, either skipping the thumbprint
120
+ * digest; anything else throws `ERR_INVALID_OPTION`.
121
+ * - JWK metadata (`use`, `x5c`, `x5t`, `x5t#S256`, `x5u`) is merged onto the result;
122
+ * `alg`, `kty`, `key_ops`, `ext` and key-material members are reserved (`ERR_INVALID_OPTION`).
123
+ */
124
+ export declare function deriveJWKFromPassword<TAlg extends JWK_PBES2, Kid extends string | false = string>(password: string | Uint8Array<ArrayBuffer>, options: DeriveJWKOptions<TAlg, Kid> & Omit<JWKMetadata, "kid">): Promise<DeriveJWKReturn<TAlg, Kid>>;
125
+ /**
126
+ * Exports an extractable `CryptoKey` as a JWK, merging optional `jwkParams` metadata.
127
+ *
128
+ * The `alg` is WebCrypto's, which knows no JOSE roles: an AES-GCM key minted for `A128GCMKW`
129
+ * exports as `A128GCM`, so key-wrapping keys are minted with `generateJWK` instead.
130
+ *
131
+ * @throws `ERR_INVALID_OPTION` if `key` is not a `CryptoKey` or `jwkParams` sets a reserved member
132
+ * (`alg`, `kty`, `key_ops`, `ext`, key material); `ERR_JWK_INVALID` for a non-extractable key.
133
+ */
134
+ export declare function exportJWK<T extends JWK = JWK>(key: CryptoKey, jwkParams?: JWKMetadata): Promise<T>;
135
+ /** @deprecated Use `exportJWK`; removed in 0.9.0. */
136
+ export declare function exportKey<T extends JWK = JWK>(key: CryptoKey, jwkParams?: JWKMetadata): Promise<T>;
137
+ /**
138
+ * Utility to derive the public half of an asymmetric JWK, or throw if the input is not asymmetric.
139
+ *
140
+ * @param jwk An asymmetric JWK (EC, OKP, RSA) with a private scalar (`d`).
141
+ * @throws `ERR_JWK_INVALID` if the input is not an asymmetric JWK.
142
+ * @returns A JWK with the same public members, without the private scalar.
143
+ */
144
+ export declare function toPublicJWK<T extends JWK_Asymmetric>(jwk: T): ToPublicJWKReturn<T>;
145
+ /**
146
+ * Wraps a CEK under a JWA key management algorithm; the result shape follows `alg`.
147
+ *
148
+ * @param wrappingKey A `CryptoKey`, a JWK, or for PBES2 a password string or bytes.
149
+ * @param options `alg` plus its parameters (`p2s`/`p2c` for PBES2, `ecdh` for ECDH-ES, …).
150
+ */
151
+ export declare function wrapKey<TAlg extends KeyManagementAlgorithm>(keyToWrap: CryptoKey | Uint8Array<ArrayBuffer>, wrappingKey: WrappingKeyFor<TAlg>, options: WrapKeyOptions<TAlg>): Promise<WrapKeyResult<TAlg>>;
152
+ /**
153
+ * Unwraps a CEK under a JWA key management algorithm.
154
+ *
155
+ * @param unwrappingKey A `CryptoKey`, a JWK, or for PBES2 a password string or bytes.
156
+ * @param options `alg`, `format?` (`"cryptokey"` by default, or `"raw"`), plus the algorithm's
157
+ * parameters (`iv`/`tag`, `p2s`/`p2c`, `epk`, …).
158
+ * @throws `ERR_JWE_DECRYPTION_FAILED` for any failed unwrap, its cause undisclosed (RFC 7516
159
+ * §11.5); `ERR_INVALID_OPTION` for a malformed `wrappedKey` or `options`; `ERR_JWE_INVALID` for
160
+ * missing or malformed algorithm parameters.
161
+ */
162
+ export declare function unwrapKey<TOptions extends UnwrapKeyOptions>(wrappedKey: Uint8Array<ArrayBuffer>, unwrappingKey: UnwrappingKeyFor<TOptions["alg"]>, options: TOptions): Promise<UnwrapKeyResult<TOptions>>;
163
+ /**
164
+ * Imports a PEM key as a JWK carrying `alg`. The type follows the label — `PRIVATE KEY`
165
+ * (PKCS#8), `PUBLIC KEY` (SPKI), `CERTIFICATE` (X.509, only its SPKI kept) — unless
166
+ * `options.pemType` overrides it. The JWK holds the key material in plain JavaScript.
167
+ *
168
+ * @throws `ERR_JWK_INVALID` if the type cannot be inferred or does not match the label;
169
+ * `ERR_JWK_UNSUPPORTED` for an unknown `pemType`; `ERR_INVALID_OPTION` if `jwkParams` sets
170
+ * `alg`, `kty`, `key_ops`, `ext` or a key-material member.
171
+ */
172
+ export declare function importPEM<A extends JWKPEMAlgorithm, Type extends PEMType | undefined = undefined>(pem: string, alg: A, options?: {
173
+ /** PEM type. Inferred from the PEM label when omitted, which leaves the result public or private. */
174
+ pemType?: Type;
175
+ /** Additional JWK properties merged into the exported key (e.g. `kid`). */
176
+ jwkParams?: JWKMetadata;
177
+ }): Promise<ImportedPEMJWK<A, Type>>;
178
+ /**
179
+ * Exports an asymmetric JWK as PEM: PKCS#8 when it carries `d`, SPKI otherwise.
180
+ *
181
+ * @throws `ERR_JWK_UNSUPPORTED` for `oct` JWKs or an unknown format or algorithm;
182
+ * `ERR_JWK_INVALID` for a missing `alg` or a `pemFormat` that contradicts the JWK's half.
183
+ */
184
+ export declare function exportPEM(jwk: JWK, options?: {
185
+ /** PEM format. Inferred from the JWK shape when omitted. */
186
+ pemFormat?: "pkcs8" | "spki";
187
+ /** Algorithm hint used when `jwk.alg` is absent. */
188
+ alg?: JWKPEMAlgorithm;
189
+ }): Promise<string>;
190
+ /**
191
+ * Derives ECDH-ES key material (RFC 7518 §4.6): ECDH followed by the Concat KDF.
192
+ *
193
+ * @param publicKey The recipient's static public key (the ephemeral one when decrypting).
194
+ * @param privateKey The ephemeral private key (the recipient's static one when decrypting).
195
+ */
196
+ export declare function deriveSharedSecret(publicKey: CryptoKey | JWK_EC_Public, privateKey: CryptoKey | JWK_EC_Private, options: {
197
+ /** Algorithm identifier fed to the concat KDF as `AlgorithmID`. */
198
+ enc: JWK_ECDH_ES | ContentEncryptionAlgorithm;
199
+ /** Derived length in bits. Defaults to `enc`'s length; required for `"ECDH-ES"`. */
200
+ keyLength?: number;
201
+ /** Agreement PartyUInfo (apu). */
202
+ partyUInfo?: Uint8Array<ArrayBuffer>;
203
+ /** Agreement PartyVInfo (apv). */
204
+ partyVInfo?: Uint8Array<ArrayBuffer>;
205
+ }): Promise<Uint8Array<ArrayBuffer>>;
@@ -37,7 +37,10 @@ export declare function verifyMultiAll(jws: JWSGeneralSerialization | JWSFlatten
37
37
  export declare function verifyMultiAll<Mode extends JOSEPayloadMode = JOSEPayloadMode>(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyResolver: JWSKeyLookup, options?: JWSMultiVerifyAllOptions<Mode>): Promise<JWSMultiVerifyOutcome<PayloadOf<Mode>>[]>;
38
38
  /** Converts a single-signature General JWS to Flattened form (RFC 7515 §7.2.2); otherwise `ERR_JWS_INVALID_SERIALIZATION`. */
39
39
  export declare function generalToFlattenedJWS(jws: JWSGeneralSerialization): JWSFlattenedSerialization;
40
- /** Creates a JWS Compact Serialization; an object payload is JSON-serialized, `alg` is required unless the JWK carries one. */
40
+ /**
41
+ * Creates a JWS Compact Serialization; an object payload is JSON-serialized, `alg` is required unless the JWK carries one.
42
+ * An unencoded (`b64: false`) compact payload cannot contain "." (RFC 7797 §5.2).
43
+ */
41
44
  export declare function sign(payload: JOSEPayload, key: JWSSignJWK, options?: JWSSignOptions): Promise<string>;
42
45
  export declare function sign(payload: JOSEPayload, key: CryptoKey | Uint8Array<ArrayBuffer>, options: JWSSignOptions & {
43
46
  alg: JWSAlgorithm;
@@ -1,7 +1,7 @@
1
1
  import { sanitizeObjectCopy, textEncoder } from "./libs/unsecure.mjs";
2
2
  import { JWTError, applyTypDefault, base64UrlStringify, computeJwtTimeClaims, getPlaintextBytes, isJWK, isJWKSet, parsePayloadText, validateCriticalHeadersJWS } from "./_jwt-internal.mjs";
3
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 "./jwk.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";
5
5
  function buildJWSProtectedHeader(alg, key, userHeader, options) {
6
6
  const safeHeader = sanitizeObjectCopy(userHeader);
7
7
  const header = {};
@@ -256,8 +256,8 @@ const PER_SIGNATURE_ERROR_CODES = /* @__PURE__ */ new Set([
256
256
  ]);
257
257
  function _checkUnprotectedHeader(header) {
258
258
  if (!header) return void 0;
259
- if (Object.hasOwn(header, "crit")) return new JWTError("Invalid JWS: \"crit\" must be integrity protected (RFC 7515 §4.1.11).", "ERR_JWS_INVALID");
260
- if (Object.hasOwn(header, "b64")) return new JWTError("Invalid JWS: \"b64\" must occur only within the protected header (RFC 7797 §3).", "ERR_JWS_INVALID");
259
+ if (Object.hasOwn(header, "crit")) return new JWTError("Invalid JWS: \"crit\" must be integrity protected.", "ERR_JWS_INVALID");
260
+ if (Object.hasOwn(header, "b64")) return new JWTError("Invalid JWS: \"b64\" must be in the protected header.", "ERR_JWS_INVALID");
261
261
  }
262
262
  function _readSignatureHeaders(entry, index) {
263
263
  const shapeError = checkSerializationEntry(entry, {
@@ -290,7 +290,7 @@ function _readSignatureHeaders(entry, index) {
290
290
  }
291
291
  const effective = _mergeHeaders(protectedHeader, unprotectedHeader);
292
292
  const alg = effective.alg;
293
- if (!alg) return failed(new JWTError(`Invalid JWS: Signature[${index}] has no "alg" in its protected or unprotected header.`, "ERR_JWS_INVALID"));
293
+ if (!alg) return failed(new JWTError(`Invalid JWS: Signature[${index}] has no "alg" in its headers.`, "ERR_JWS_INVALID"));
294
294
  const algError = checkJWSAlgSupported(alg) ?? checkJWSB64(protectedHeader);
295
295
  if (algError) return failed(algError);
296
296
  return {
@@ -323,13 +323,13 @@ function _resolveB64(signers) {
323
323
  const raw = signers[i].protectedHeader?.b64;
324
324
  if (raw === void 0) continue;
325
325
  if (decided === void 0) decided = raw;
326
- else if (decided !== raw) throw new JWTError(`Inconsistent "b64" across signers (RFC 7797 §3 requires all signers use the same b64 value).`, "ERR_JWS_B64_INCONSISTENT");
326
+ else if (decided !== raw) throw new JWTError("Inconsistent \"b64\" across signers.", "ERR_JWS_B64_INCONSISTENT");
327
327
  }
328
328
  return decided ?? true;
329
329
  }
330
330
  function _assertDisjointHeaders(protectedHeader, unprotectedHeader) {
331
331
  if (!protectedHeader || !unprotectedHeader) return;
332
- for (const k of Object.keys(unprotectedHeader)) if (k in protectedHeader) throw new JWTError(`JWS header parameter "${k}" appears in both protected and unprotected headers of a signature; they must be disjoint (RFC 7515 §7.2.1).`, "ERR_JWS_HEADER_PARAMS_NOT_DISJOINT");
332
+ for (const k of Object.keys(unprotectedHeader)) if (k in protectedHeader) throw new JWTError(`Invalid JWS: header parameter "${k}" is set in more than one header.`, "ERR_JWS_HEADER_PARAMS_NOT_DISJOINT");
333
333
  }
334
334
  function _mergeHeaders(protectedHeader, unprotectedHeader) {
335
335
  if (!unprotectedHeader) return protectedHeader;
@@ -350,7 +350,7 @@ function _readGeneralSerialization(input, signatureLimit) {
350
350
  function _normalizeSerialization(input) {
351
351
  assertSerializationObject(input, "JWS");
352
352
  if ("signatures" in input && Array.isArray(input.signatures)) {
353
- if ("signature" in input || "protected" in input) throw new JWTError("Invalid JWS JSON Serialization: \"signatures\" must not be combined with a top-level \"signature\" or \"protected\".", "ERR_JWS_INVALID_SERIALIZATION");
353
+ if ("signature" in input || "protected" in input) throw new JWTError("Invalid JWS JSON Serialization: \"signatures\" excludes top-level \"signature\" and \"protected\".", "ERR_JWS_INVALID_SERIALIZATION");
354
354
  return input;
355
355
  }
356
356
  if ("signature" in input) {
@@ -363,7 +363,7 @@ function _normalizeSerialization(input) {
363
363
  signatures: [signature]
364
364
  };
365
365
  }
366
- throw new JWTError("Invalid JWS JSON Serialization: missing both signatures[] and flattened signature.", "ERR_JWS_INVALID_SERIALIZATION");
366
+ throw new JWTError("Invalid JWS JSON Serialization: missing \"signatures\" or \"signature\".", "ERR_JWS_INVALID_SERIALIZATION");
367
367
  }
368
368
  async function _verifyOneSignature(payload, entry, index, keyResolver, context) {
369
369
  const { options, payloadMode, keyCandidates } = context;
@@ -466,7 +466,7 @@ async function sign(payload, key, options = {}) {
466
466
  let { alg } = options;
467
467
  if (!alg) {
468
468
  if (isJWK(key) && key.alg) alg = key.alg;
469
- else throw new JWTError("JWS \"alg\" (Algorithm) must be provided in options", "ERR_JWS_ALG_MISSING");
469
+ else throw new JWTError("JWS \"alg\" must be provided in options", "ERR_JWS_ALG_MISSING");
470
470
  }
471
471
  assertJWSAlgSupported(alg);
472
472
  let signingKey;
@@ -488,7 +488,7 @@ async function sign(payload, key, options = {}) {
488
488
  if (useB64) payloadEncoded = base64UrlStringify(payloadBytes);
489
489
  else {
490
490
  payloadEncoded = parsePayloadText(payloadBytes);
491
- if (payloadEncoded.includes(".")) throw new JWTError("Invalid JWS: unencoded compact payload must not contain \".\" (RFC 7797 §5.2).", "ERR_JWS_INVALID");
491
+ if (payloadEncoded.includes(".")) throw new JWTError("Invalid JWS: unencoded compact payload must not contain \".\".", "ERR_JWS_INVALID");
492
492
  }
493
493
  const signingInputString = `${protectedHeaderEncoded}.${payloadEncoded}`;
494
494
  const signingInputBytes = textEncoder.encode(signingInputString);
@@ -24,7 +24,8 @@ export declare function getPlaintextBytes(payload: JOSEPayload): Uint8Array<Arra
24
24
  export type ComputeJwtTimeClaimsOptions = JWTTimeClaimOptions;
25
25
  /**
26
26
  * Return a copy of an object payload with `iat` and the requested `exp` / `nbf`, or `undefined` when none applies.
27
- * Both members of `expiresIn`/`expiresAt` or `notBeforeIn`/`notBeforeAt` throw `ERR_JWT_CLAIM_INVALID`.
27
+ * `currentDate` is the clock: `iat` when written, and the base of `expiresIn` / `notBeforeIn`;
28
+ * `issuedAt` writes `iat` alone, a `Date` as that instant. Both members of `expiresIn`/`expiresAt` or `notBeforeIn`/`notBeforeAt` throw `ERR_JWT_CLAIM_INVALID`.
28
29
  */
29
30
  export declare function computeJwtTimeClaims(payload: unknown, options?: ComputeJwtTimeClaimsOptions): JWTClaims | undefined;
30
31
  /** Validate critical headers in JWS semantics. */