unjwt 0.5.5 → 0.5.6

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.
package/README.md CHANGED
@@ -97,6 +97,8 @@ import {
97
97
  } from "https://esm.sh/unjwt/utils";
98
98
  ```
99
99
 
100
+ ---
101
+
100
102
  ### JWS (JSON Web Signature, [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515))
101
103
 
102
104
  Functions to sign and verify data according to the JWS specification.
@@ -110,6 +112,8 @@ Creates a JWS token.
110
112
  - `options`:
111
113
  - `alg`: (Required) The JWS algorithm (e.g., `"HS256"`, `"RS256"`, `"ES256"`, `"PS256"`).
112
114
  - `protectedHeader`: An object for additional JWS Protected Header parameters (e.g., `kid`, `typ`, `cty`, `crit`, `b64`). If `payload` is an object and `typ` is not set, it defaults to `"JWT"`. The `b64` parameter ([RFC7797 section-3](https://datatracker.ietf.org/doc/html/rfc7797#section-3)) controls payload encoding (defaults to `true`, meaning Base64URL encoded).
115
+ - `expiresIn`: Sets an expiration time in seconds (e.g., `3600` for 1 hour). If `iat` is missing it will be set to the current time. If `exp` is missing it will be set to `iat + expiresIn`. This is only applied if `payload` is a JWT.
116
+ - `currentDate`: The current date for computing `expiresIn` option. Defaults to `new Date()`.
113
117
 
114
118
  Returns a `Promise<string>` resolving to the JWS token in Compact Serialization format.
115
119
 
@@ -191,6 +195,8 @@ const { payload } = await verify(token, keyLookup, { algorithms: ["RS256"] });
191
195
  console.log(payload); // { userId: 123, permissions: ["read"] }
192
196
  ```
193
197
 
198
+ ---
199
+
194
200
  ### JWE (JSON Web Encryption, [RFC 7516](https://datatracker.ietf.org/doc/html/rfc7516))
195
201
 
196
202
  Functions to encrypt and decrypt data according to the JWE specification.
@@ -293,6 +299,8 @@ async function decryptData(jweToken: string, kek: CryptoKey) {
293
299
  }
294
300
  ```
295
301
 
302
+ ---
303
+
296
304
  ### JWK (JSON Web Key, [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517))
297
305
 
298
306
  Utilities for working with JSON Web Keys.
@@ -591,6 +599,8 @@ console.log(rsaPublicSpki);
591
599
  // -----END PUBLIC KEY-----
592
600
  ```
593
601
 
602
+ ---
603
+
594
604
  ### Utility Functions
595
605
 
596
606
  `unjwt/utils` exports several helpful functions:
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { C as CompositeKey, d as ContentEncryptionAlgorithm, D as DeriveKeyOptions, c as DeriveKeyReturn, G as GenerateKeyAlgorithm, a as GenerateKeyOptions, b as GenerateKeyReturn, m as JWK, E as JWKAlgorithm, F as JWKPEMAlgorithm, J as JWKParameters, n as JWKSet, v as JWK_AES_CBC_HMAC, w as JWK_AES_GCM, u as JWK_AES_GCM_KW, t as JWK_AES_KW, B as JWK_Asymmetric_Algorithm, h as JWK_EC, y as JWK_ECDH_ES, r as JWK_ECDSA, g as JWK_EC_Private, f as JWK_EC_Public, z as JWK_ED, o as JWK_HMAC, x as JWK_PBES2, k as JWK_RSA, s as JWK_RSA_ENC, q as JWK_RSA_PSS, j as JWK_RSA_Private, i as JWK_RSA_Public, p as JWK_RSA_SIGN, A as JWK_Symmetric_Algorithm, l as JWK_oct, K as KeyManagementAlgorithm, U as UnwrapKeyOptions, W as WrapKeyOptions, e as WrapKeyResult } from './shared/unjwt.D9da8HQF.mjs';
2
2
  export { b as JWT, a as JWTClaims, J as JoseHeaderParameters } from './shared/unjwt.BvIkEGGz.mjs';
3
- export { J as JWSAlgorithm, a as JWSHeaderParameters, f as JWSKeyLookupFunction, b as JWSProtectedHeader, c as JWSSignOptions, e as JWSVerifyOptions, d as JWSVerifyResult, j as jws } from './shared/unjwt.CGkRbYL2.mjs';
3
+ export { J as JWSAlgorithm, a as JWSHeaderParameters, f as JWSKeyLookupFunction, b as JWSProtectedHeader, c as JWSSignOptions, e as JWSVerifyOptions, d as JWSVerifyResult, j as jws } from './shared/unjwt.D_Mabhgz.mjs';
4
4
  export { d as JWEDecryptOptions, e as JWEDecryptResult, b as JWEEncryptOptions, J as JWEHeaderParameters, c as JWEKeyLookupFunction, a as JWEKeyManagementHeaderParameters, j as jwe } from './shared/unjwt.8w7jp2aw.mjs';
5
5
  export { i as utils } from './shared/unjwt.CbvzYE-m.mjs';
6
6
  export { j as jwk } from './shared/unjwt.DH1p_WL8.mjs';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { C as CompositeKey, d as ContentEncryptionAlgorithm, D as DeriveKeyOptions, c as DeriveKeyReturn, G as GenerateKeyAlgorithm, a as GenerateKeyOptions, b as GenerateKeyReturn, m as JWK, E as JWKAlgorithm, F as JWKPEMAlgorithm, J as JWKParameters, n as JWKSet, v as JWK_AES_CBC_HMAC, w as JWK_AES_GCM, u as JWK_AES_GCM_KW, t as JWK_AES_KW, B as JWK_Asymmetric_Algorithm, h as JWK_EC, y as JWK_ECDH_ES, r as JWK_ECDSA, g as JWK_EC_Private, f as JWK_EC_Public, z as JWK_ED, o as JWK_HMAC, x as JWK_PBES2, k as JWK_RSA, s as JWK_RSA_ENC, q as JWK_RSA_PSS, j as JWK_RSA_Private, i as JWK_RSA_Public, p as JWK_RSA_SIGN, A as JWK_Symmetric_Algorithm, l as JWK_oct, K as KeyManagementAlgorithm, U as UnwrapKeyOptions, W as WrapKeyOptions, e as WrapKeyResult } from './shared/unjwt.D9da8HQF.js';
2
2
  export { b as JWT, a as JWTClaims, J as JoseHeaderParameters } from './shared/unjwt.c10n3uM8.js';
3
- export { J as JWSAlgorithm, a as JWSHeaderParameters, f as JWSKeyLookupFunction, b as JWSProtectedHeader, c as JWSSignOptions, e as JWSVerifyOptions, d as JWSVerifyResult, j as jws } from './shared/unjwt.RZpKLLLg.js';
3
+ export { J as JWSAlgorithm, a as JWSHeaderParameters, f as JWSKeyLookupFunction, b as JWSProtectedHeader, c as JWSSignOptions, e as JWSVerifyOptions, d as JWSVerifyResult, j as jws } from './shared/unjwt.w-IAnI15.js';
4
4
  export { d as JWEDecryptOptions, e as JWEDecryptResult, b as JWEEncryptOptions, J as JWEHeaderParameters, c as JWEKeyLookupFunction, a as JWEKeyManagementHeaderParameters, j as jwe } from './shared/unjwt.CoSilGqk.js';
5
5
  export { i as utils } from './shared/unjwt.D4DUiNeM.js';
6
6
  export { j as jwk } from './shared/unjwt.CrcUJklb.js';
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  export { i as utils } from './shared/unjwt.BHpknTpg.mjs';
2
2
  export { j as jwe } from './shared/unjwt.B2olxeGL.mjs';
3
3
  export { j as jwk } from './shared/unjwt.DRUXIabl.mjs';
4
- export { j as jws } from './shared/unjwt.BY2vKT86.mjs';
4
+ export { j as jws } from './shared/unjwt.DQNXDqfd.mjs';
5
5
  import './shared/unjwt.Ca03jPX3.mjs';
package/dist/jws.d.mts CHANGED
@@ -1,3 +1,3 @@
1
1
  import './shared/unjwt.D9da8HQF.mjs';
2
2
  import './shared/unjwt.BvIkEGGz.mjs';
3
- export { J as JWSAlgorithm, a as JWSHeaderParameters, f as JWSKeyLookupFunction, b as JWSProtectedHeader, c as JWSSignOptions, e as JWSVerifyOptions, d as JWSVerifyResult, s as sign, v as verify } from './shared/unjwt.CGkRbYL2.mjs';
3
+ export { J as JWSAlgorithm, a as JWSHeaderParameters, f as JWSKeyLookupFunction, b as JWSProtectedHeader, c as JWSSignOptions, e as JWSVerifyOptions, d as JWSVerifyResult, s as sign, v as verify } from './shared/unjwt.D_Mabhgz.mjs';
package/dist/jws.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import './shared/unjwt.D9da8HQF.js';
2
2
  import './shared/unjwt.c10n3uM8.js';
3
- export { J as JWSAlgorithm, a as JWSHeaderParameters, f as JWSKeyLookupFunction, b as JWSProtectedHeader, c as JWSSignOptions, e as JWSVerifyOptions, d as JWSVerifyResult, s as sign, v as verify } from './shared/unjwt.RZpKLLLg.js';
3
+ export { J as JWSAlgorithm, a as JWSHeaderParameters, f as JWSKeyLookupFunction, b as JWSProtectedHeader, c as JWSSignOptions, e as JWSVerifyOptions, d as JWSVerifyResult, s as sign, v as verify } from './shared/unjwt.w-IAnI15.js';
package/dist/jws.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  import './shared/unjwt.DRUXIabl.mjs';
2
2
  import './shared/unjwt.BHpknTpg.mjs';
3
3
  import './shared/unjwt.Ca03jPX3.mjs';
4
- export { s as sign, v as verify } from './shared/unjwt.BY2vKT86.mjs';
4
+ export { s as sign, v as verify } from './shared/unjwt.DQNXDqfd.mjs';
@@ -12,6 +12,7 @@ async function sign(payload, key, options = {}) {
12
12
  throw new TypeError('JWS "alg" (Algorithm) must be provided in options');
13
13
  }
14
14
  }
15
+ validateKeyLength(key, alg);
15
16
  const signingKey = await importKey(key, alg);
16
17
  const protectedHeader = {
17
18
  ...additionalProtectedHeader,
@@ -26,6 +27,16 @@ async function sign(payload, key, options = {}) {
26
27
  if (protectedHeader.b64 === true) {
27
28
  delete protectedHeader.b64;
28
29
  }
30
+ let computedPayload = void 0;
31
+ if (options.expiresIn !== void 0 && protectedHeader.typ === "JWT" && typeof payload === "object" && !(payload instanceof Uint8Array) && !payload.exp) {
32
+ computedPayload = { ...payload };
33
+ const currentTime = Math.round(
34
+ (options.currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3
35
+ );
36
+ const iat = typeof payload.iat === "number" ? payload.iat : currentTime;
37
+ computedPayload.iat = iat;
38
+ computedPayload.exp = iat + options.expiresIn;
39
+ }
29
40
  const protectedHeaderString = JSON.stringify(protectedHeader);
30
41
  const protectedHeaderEncoded = base64UrlEncode(protectedHeaderString);
31
42
  let payloadBytes;
@@ -34,7 +45,9 @@ async function sign(payload, key, options = {}) {
34
45
  } else if (typeof payload === "string") {
35
46
  payloadBytes = textEncoder.encode(payload);
36
47
  } else if (typeof payload === "object" && payload !== null) {
37
- payloadBytes = textEncoder.encode(JSON.stringify(payload));
48
+ payloadBytes = textEncoder.encode(
49
+ JSON.stringify(computedPayload || payload)
50
+ );
38
51
  } else {
39
52
  throw new TypeError(
40
53
  "Payload must be a string, Uint8Array, or a JSON-serializable object."
@@ -162,9 +175,10 @@ async function verify(jws, key, options = {}) {
162
175
  }
163
176
  if (payload && typeof payload === "object" && !options.forceUint8Array) {
164
177
  const jwtClaims = payload;
165
- const clockTolerance = (options.clockTolerance ?? 0) * 1e3;
166
- const currentDate = options.currentDate ?? /* @__PURE__ */ new Date();
167
- const currentTime = currentDate.getTime();
178
+ const clockTolerance = options.clockTolerance ?? 0;
179
+ const currentTime = Math.round(
180
+ (options.currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3
181
+ );
168
182
  const allRequiredClaims = new Set(options.requiredClaims || []);
169
183
  const missingClaims = /* @__PURE__ */ new Set();
170
184
  if (options.issuer) allRequiredClaims.add("iss");
@@ -203,12 +217,12 @@ async function verify(jws, key, options = {}) {
203
217
  );
204
218
  }
205
219
  }
206
- if (typeof jwtClaims.nbf === "number" && jwtClaims.nbf * 1e3 > currentTime + clockTolerance) {
220
+ if (typeof jwtClaims.nbf === "number" && jwtClaims.nbf > currentTime + clockTolerance) {
207
221
  throw new Error(
208
222
  `JWT "nbf" (Not Before) Claim validation failed: Token is not yet valid (nbf: ${new Date(jwtClaims.nbf * 1e3).toISOString()})`
209
223
  );
210
224
  }
211
- if (typeof jwtClaims.exp === "number" && jwtClaims.exp * 1e3 <= currentTime - clockTolerance) {
225
+ if (typeof jwtClaims.exp === "number" && jwtClaims.exp <= currentTime - clockTolerance) {
212
226
  throw new Error(
213
227
  `JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${new Date(jwtClaims.exp * 1e3).toISOString()})`
214
228
  );
@@ -219,12 +233,12 @@ async function verify(jws, key, options = {}) {
219
233
  'JWT "iat" (Issued At) Claim must be a number when maxTokenAge is set.'
220
234
  );
221
235
  }
222
- if (jwtClaims.iat * 1e3 > currentTime + clockTolerance) {
236
+ if (jwtClaims.iat > currentTime + clockTolerance) {
223
237
  throw new Error(
224
238
  `JWT "iat" (Issued At) Claim validation failed: Token was issued in the future (iat: ${new Date(jwtClaims.iat * 1e3).toISOString()})`
225
239
  );
226
240
  }
227
- if (jwtClaims.iat * 1e3 < currentTime - options.maxTokenAge * 1e3 - clockTolerance) {
241
+ if (jwtClaims.iat < currentTime - options.maxTokenAge - clockTolerance) {
228
242
  throw new Error(
229
243
  `JWT "iat" (Issued At) Claim validation failed: Token is too old (maxTokenAge: ${options.maxTokenAge}s, iat: ${new Date(jwtClaims.iat * 1e3).toISOString()})`
230
244
  );
@@ -236,6 +250,24 @@ async function verify(jws, key, options = {}) {
236
250
  protectedHeader
237
251
  };
238
252
  }
253
+ function validateKeyLength(key, alg) {
254
+ if (!alg || isJWK(key)) return;
255
+ if (alg.startsWith("HS") && key instanceof Uint8Array) {
256
+ const algLength = Number.parseInt(alg.slice(2)) / 8;
257
+ if (key.length < algLength) {
258
+ throw new TypeError(
259
+ `${alg} requires key length to be ${algLength} bytes or larger`
260
+ );
261
+ }
262
+ } else if ((alg.startsWith("RS") || alg.startsWith("PS")) && key instanceof CryptoKey) {
263
+ const { modulusLength } = key.algorithm;
264
+ if (typeof modulusLength !== "number" || modulusLength < 2048) {
265
+ throw new TypeError(
266
+ `${alg} requires key modulusLength to be 2048 bits or larger`
267
+ );
268
+ }
269
+ }
270
+ }
239
271
 
240
272
  const jws = {
241
273
  __proto__: null,
@@ -31,6 +31,12 @@ interface JWSSignOptions {
31
31
  * `typ` defaults to "JWT" if not provided.
32
32
  */
33
33
  protectedHeader?: JWSHeaderParameters;
34
+ /** Date to use when comparing NumericDate claims, defaults to `new Date()`. */
35
+ currentDate?: Date;
36
+ /**
37
+ * Time at which the JWS should expire, if no `exp` was already provided.
38
+ */
39
+ expiresIn?: number;
34
40
  }
35
41
  /** Result of JWS verification */
36
42
  interface JWSVerifyResult<T extends JWTClaims | Uint8Array | string = JWTClaims | Uint8Array | string> {
@@ -45,8 +51,6 @@ interface JWSVerifyOptions {
45
51
  algorithms?: JWSAlgorithm[];
46
52
  /** If true, forces the payload to be returned as a Uint8Array, otherwise type is inferred. */
47
53
  forceUint8Array?: boolean;
48
- /** List of critical header parameters that must be understood and processed. */
49
- requiredHeaders?: string[];
50
54
  /**
51
55
  * Expected JWT "aud" (Audience) Claim value(s).
52
56
  *
@@ -86,6 +90,8 @@ interface JWSVerifyOptions {
86
90
  typ?: string;
87
91
  /** Date to use when comparing NumericDate claims, defaults to `new Date()`. */
88
92
  currentDate?: Date;
93
+ /** List of critical header parameters that must be understood and processed. */
94
+ requiredHeaders?: string[];
89
95
  /**
90
96
  * Array of required Claim Names that must be present in the JWT Claims Set. Default is that: if
91
97
  * the {@link issuer `issuer` option} is set, then JWT "iss" (Issuer) Claim must be present; if the
@@ -31,6 +31,12 @@ interface JWSSignOptions {
31
31
  * `typ` defaults to "JWT" if not provided.
32
32
  */
33
33
  protectedHeader?: JWSHeaderParameters;
34
+ /** Date to use when comparing NumericDate claims, defaults to `new Date()`. */
35
+ currentDate?: Date;
36
+ /**
37
+ * Time at which the JWS should expire, if no `exp` was already provided.
38
+ */
39
+ expiresIn?: number;
34
40
  }
35
41
  /** Result of JWS verification */
36
42
  interface JWSVerifyResult<T extends JWTClaims | Uint8Array | string = JWTClaims | Uint8Array | string> {
@@ -45,8 +51,6 @@ interface JWSVerifyOptions {
45
51
  algorithms?: JWSAlgorithm[];
46
52
  /** If true, forces the payload to be returned as a Uint8Array, otherwise type is inferred. */
47
53
  forceUint8Array?: boolean;
48
- /** List of critical header parameters that must be understood and processed. */
49
- requiredHeaders?: string[];
50
54
  /**
51
55
  * Expected JWT "aud" (Audience) Claim value(s).
52
56
  *
@@ -86,6 +90,8 @@ interface JWSVerifyOptions {
86
90
  typ?: string;
87
91
  /** Date to use when comparing NumericDate claims, defaults to `new Date()`. */
88
92
  currentDate?: Date;
93
+ /** List of critical header parameters that must be understood and processed. */
94
+ requiredHeaders?: string[];
89
95
  /**
90
96
  * Array of required Claim Names that must be present in the JWT Claims Set. Default is that: if
91
97
  * the {@link issuer `issuer` option} is set, then JWT "iss" (Issuer) Claim must be present; if the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unjwt",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "Low-level JWT utilities",
5
5
  "repository": "sandros94/unjwt",
6
6
  "license": "MIT",