@aura-stack/jose 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSecret
3
- } from "./chunk-GXM4P5MQ.js";
3
+ } from "./chunk-SES6WQL3.js";
4
4
 
5
5
  // src/deriveKey.ts
6
6
  import { hkdfSync } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSecret
3
- } from "./chunk-GXM4P5MQ.js";
3
+ } from "./chunk-SES6WQL3.js";
4
4
  import {
5
5
  isAuraJoseError,
6
6
  isFalsy,
@@ -0,0 +1,57 @@
1
+ import {
2
+ isObject
3
+ } from "./chunk-ZHFHDRQH.js";
4
+ import {
5
+ InvalidSecretError
6
+ } from "./chunk-BMXFAB6Q.js";
7
+
8
+ // src/secret.ts
9
+ var MIN_SECRET_ENTROPY_BITS = 4.5;
10
+ var getEntropy = (secret) => {
11
+ const charFreq = /* @__PURE__ */ new Map();
12
+ for (const char of secret) {
13
+ if (!charFreq.has(char)) {
14
+ charFreq.set(char, 0);
15
+ }
16
+ charFreq.set(char, charFreq.get(char) + 1);
17
+ }
18
+ let entropy = 0;
19
+ const length = secret.length;
20
+ for (const freq of charFreq.values()) {
21
+ const p = freq / length;
22
+ entropy -= p * Math.log2(p);
23
+ }
24
+ return entropy;
25
+ };
26
+ var createSecret = (secret, length = 32) => {
27
+ if (!Boolean(secret)) throw new InvalidSecretError("Secret is required");
28
+ if (typeof secret === "string") {
29
+ const byteLength = new TextEncoder().encode(secret).byteLength;
30
+ if (byteLength < length) {
31
+ throw new InvalidSecretError(`Secret string must be at least ${length} bytes long`);
32
+ }
33
+ const entropy = getEntropy(secret);
34
+ if (entropy < MIN_SECRET_ENTROPY_BITS) {
35
+ throw new InvalidSecretError(
36
+ `Secret string must have an entropy of at least ${MIN_SECRET_ENTROPY_BITS} bits per character`
37
+ );
38
+ }
39
+ return new Uint8Array(Buffer.from(secret, "utf-8"));
40
+ }
41
+ return secret;
42
+ };
43
+ var getSecrets = (secret) => {
44
+ const jwsSecret = isObject(secret) && "jws" in secret ? secret.jws : secret;
45
+ const jweSecret = isObject(secret) && "jwe" in secret ? secret.jwe : secret;
46
+ return {
47
+ jwsSecret,
48
+ jweSecret
49
+ };
50
+ };
51
+
52
+ export {
53
+ MIN_SECRET_ENTROPY_BITS,
54
+ getEntropy,
55
+ createSecret,
56
+ getSecrets
57
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSecret
3
- } from "./chunk-GXM4P5MQ.js";
3
+ } from "./chunk-SES6WQL3.js";
4
4
  import {
5
5
  isAuraJoseError,
6
6
  isFalsy
@@ -24,7 +24,7 @@ __export(deriveKey_exports, {
24
24
  deriveKey: () => deriveKey
25
25
  });
26
26
  module.exports = __toCommonJS(deriveKey_exports);
27
- var import_node_crypto = require("crypto");
27
+ var import_crypto = require("crypto");
28
28
 
29
29
  // src/errors.ts
30
30
  var AuraJoseError = class extends Error {
@@ -42,11 +42,35 @@ var InvalidSecretError = class extends AuraJoseError {
42
42
  };
43
43
 
44
44
  // src/secret.ts
45
- var createSecret = (secret) => {
46
- if (secret === void 0) throw new InvalidSecretError("Secret is required");
45
+ var MIN_SECRET_ENTROPY_BITS = 4.5;
46
+ var getEntropy = (secret) => {
47
+ const charFreq = /* @__PURE__ */ new Map();
48
+ for (const char of secret) {
49
+ if (!charFreq.has(char)) {
50
+ charFreq.set(char, 0);
51
+ }
52
+ charFreq.set(char, charFreq.get(char) + 1);
53
+ }
54
+ let entropy = 0;
55
+ const length = secret.length;
56
+ for (const freq of charFreq.values()) {
57
+ const p = freq / length;
58
+ entropy -= p * Math.log2(p);
59
+ }
60
+ return entropy;
61
+ };
62
+ var createSecret = (secret, length = 32) => {
63
+ if (!Boolean(secret)) throw new InvalidSecretError("Secret is required");
47
64
  if (typeof secret === "string") {
48
- if (new TextEncoder().encode(secret).byteLength < 32) {
49
- throw new InvalidSecretError("Secret string must be at least 32 characters long");
65
+ const byteLength = new TextEncoder().encode(secret).byteLength;
66
+ if (byteLength < length) {
67
+ throw new InvalidSecretError(`Secret string must be at least ${length} bytes long`);
68
+ }
69
+ const entropy = getEntropy(secret);
70
+ if (entropy < MIN_SECRET_ENTROPY_BITS) {
71
+ throw new InvalidSecretError(
72
+ `Secret string must have an entropy of at least ${MIN_SECRET_ENTROPY_BITS} bits per character`
73
+ );
50
74
  }
51
75
  return new Uint8Array(Buffer.from(secret, "utf-8"));
52
76
  }
@@ -56,7 +80,7 @@ var createSecret = (secret) => {
56
80
  // src/deriveKey.ts
57
81
  var deriveKey = (secret, salt, info, length = 32) => {
58
82
  try {
59
- const key = (0, import_node_crypto.hkdfSync)("SHA256", secret, salt, info, length);
83
+ const key = (0, import_crypto.hkdfSync)("SHA256", secret, salt, info, length);
60
84
  const derivedKey = Buffer.from(key);
61
85
  return {
62
86
  key,
@@ -1,3 +1,3 @@
1
- import 'node:crypto';
2
- export { createDeriveKey, deriveKey } from './index.js';
1
+ import 'crypto';
2
+ export { m as createDeriveKey, l as deriveKey } from './sign.js';
3
3
  import 'jose';
package/dist/deriveKey.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createDeriveKey,
3
3
  deriveKey
4
- } from "./chunk-K5BQTFSO.js";
5
- import "./chunk-GXM4P5MQ.js";
4
+ } from "./chunk-CXN54JNY.js";
5
+ import "./chunk-SES6WQL3.js";
6
6
  import "./chunk-ZHFHDRQH.js";
7
7
  import "./chunk-BMXFAB6Q.js";
8
8
  export {
package/dist/encrypt.cjs CHANGED
@@ -35,7 +35,7 @@ __export(encrypt_exports, {
35
35
  encryptJWE: () => encryptJWE
36
36
  });
37
37
  module.exports = __toCommonJS(encrypt_exports);
38
- var import_node_crypto = __toESM(require("crypto"), 1);
38
+ var import_crypto = __toESM(require("crypto"), 1);
39
39
  var import_jose = require("jose");
40
40
 
41
41
  // src/errors.ts
@@ -71,11 +71,35 @@ var isFalsy = (value) => {
71
71
  };
72
72
 
73
73
  // src/secret.ts
74
- var createSecret = (secret) => {
75
- if (secret === void 0) throw new InvalidSecretError("Secret is required");
74
+ var MIN_SECRET_ENTROPY_BITS = 4.5;
75
+ var getEntropy = (secret) => {
76
+ const charFreq = /* @__PURE__ */ new Map();
77
+ for (const char of secret) {
78
+ if (!charFreq.has(char)) {
79
+ charFreq.set(char, 0);
80
+ }
81
+ charFreq.set(char, charFreq.get(char) + 1);
82
+ }
83
+ let entropy = 0;
84
+ const length = secret.length;
85
+ for (const freq of charFreq.values()) {
86
+ const p = freq / length;
87
+ entropy -= p * Math.log2(p);
88
+ }
89
+ return entropy;
90
+ };
91
+ var createSecret = (secret, length = 32) => {
92
+ if (!Boolean(secret)) throw new InvalidSecretError("Secret is required");
76
93
  if (typeof secret === "string") {
77
- if (new TextEncoder().encode(secret).byteLength < 32) {
78
- throw new InvalidSecretError("Secret string must be at least 32 characters long");
94
+ const byteLength = new TextEncoder().encode(secret).byteLength;
95
+ if (byteLength < length) {
96
+ throw new InvalidSecretError(`Secret string must be at least ${length} bytes long`);
97
+ }
98
+ const entropy = getEntropy(secret);
99
+ if (entropy < MIN_SECRET_ENTROPY_BITS) {
100
+ throw new InvalidSecretError(
101
+ `Secret string must have an entropy of at least ${MIN_SECRET_ENTROPY_BITS} bits per character`
102
+ );
79
103
  }
80
104
  return new Uint8Array(Buffer.from(secret, "utf-8"));
81
105
  }
@@ -89,7 +113,7 @@ var encryptJWE = async (payload, secret, options) => {
89
113
  throw new InvalidPayloadError("The payload must be a non-empty string");
90
114
  }
91
115
  const secretKey = createSecret(secret);
92
- const jti = import_node_crypto.default.randomBytes(32).toString("base64url");
116
+ const jti = import_crypto.default.randomBytes(32).toString("base64url");
93
117
  return new import_jose.EncryptJWT({ payload }).setProtectedHeader({ alg: "dir", enc: "A256GCM", typ: "JWT", cty: "JWT" }).setIssuedAt().setNotBefore(options?.nbf ?? "0s").setExpirationTime(options?.exp ?? "15d").setJti(jti).encrypt(secretKey);
94
118
  } catch (error) {
95
119
  if (isAuraJoseError(error)) {
package/dist/encrypt.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { JWTDecryptOptions } from 'jose';
2
- export { EncryptOptions, EncryptedPayload, createJWE, decryptJWE, encryptJWE } from './index.js';
3
- import 'node:crypto';
2
+ export { a as EncryptOptions, E as EncryptedPayload, c as createJWE, d as decryptJWE, e as encryptJWE } from './sign.js';
3
+ import 'crypto';
package/dist/encrypt.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  createJWE,
3
3
  decryptJWE,
4
4
  encryptJWE
5
- } from "./chunk-VPFE27PW.js";
6
- import "./chunk-GXM4P5MQ.js";
5
+ } from "./chunk-URDLFFH3.js";
6
+ import "./chunk-SES6WQL3.js";
7
7
  import "./chunk-ZHFHDRQH.js";
8
8
  import "./chunk-BMXFAB6Q.js";
9
9
  export {
package/dist/index.cjs CHANGED
@@ -30,22 +30,26 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ MIN_SECRET_ENTROPY_BITS: () => MIN_SECRET_ENTROPY_BITS,
33
34
  createDeriveKey: () => createDeriveKey,
34
35
  createJWE: () => createJWE,
35
36
  createJWS: () => createJWS,
36
37
  createJWT: () => createJWT,
38
+ createSecret: () => createSecret,
37
39
  decodeJWT: () => decodeJWT,
38
40
  decryptJWE: () => decryptJWE,
39
41
  deriveKey: () => deriveKey,
40
42
  encodeJWT: () => encodeJWT,
41
43
  encryptJWE: () => encryptJWE,
44
+ getEntropy: () => getEntropy,
45
+ getSecrets: () => getSecrets,
42
46
  signJWS: () => signJWS,
43
47
  verifyJWS: () => verifyJWS
44
48
  });
45
49
  module.exports = __toCommonJS(index_exports);
46
50
 
47
51
  // src/sign.ts
48
- var import_node_crypto = __toESM(require("crypto"), 1);
52
+ var import_crypto = __toESM(require("crypto"), 1);
49
53
  var import_jose = require("jose");
50
54
 
51
55
  // src/errors.ts
@@ -99,11 +103,35 @@ var isInvalidPayload = (payload) => {
99
103
  };
100
104
 
101
105
  // src/secret.ts
102
- var createSecret = (secret) => {
103
- if (secret === void 0) throw new InvalidSecretError("Secret is required");
106
+ var MIN_SECRET_ENTROPY_BITS = 4.5;
107
+ var getEntropy = (secret) => {
108
+ const charFreq = /* @__PURE__ */ new Map();
109
+ for (const char of secret) {
110
+ if (!charFreq.has(char)) {
111
+ charFreq.set(char, 0);
112
+ }
113
+ charFreq.set(char, charFreq.get(char) + 1);
114
+ }
115
+ let entropy = 0;
116
+ const length = secret.length;
117
+ for (const freq of charFreq.values()) {
118
+ const p = freq / length;
119
+ entropy -= p * Math.log2(p);
120
+ }
121
+ return entropy;
122
+ };
123
+ var createSecret = (secret, length = 32) => {
124
+ if (!Boolean(secret)) throw new InvalidSecretError("Secret is required");
104
125
  if (typeof secret === "string") {
105
- if (new TextEncoder().encode(secret).byteLength < 32) {
106
- throw new InvalidSecretError("Secret string must be at least 32 characters long");
126
+ const byteLength = new TextEncoder().encode(secret).byteLength;
127
+ if (byteLength < length) {
128
+ throw new InvalidSecretError(`Secret string must be at least ${length} bytes long`);
129
+ }
130
+ const entropy = getEntropy(secret);
131
+ if (entropy < MIN_SECRET_ENTROPY_BITS) {
132
+ throw new InvalidSecretError(
133
+ `Secret string must have an entropy of at least ${MIN_SECRET_ENTROPY_BITS} bits per character`
134
+ );
107
135
  }
108
136
  return new Uint8Array(Buffer.from(secret, "utf-8"));
109
137
  }
@@ -125,7 +153,7 @@ var signJWS = async (payload, secret) => {
125
153
  throw new InvalidPayloadError("The payload must be a non-empty object");
126
154
  }
127
155
  const secretKey = createSecret(secret);
128
- const jti = import_node_crypto.default.randomBytes(32).toString("base64url");
156
+ const jti = import_crypto.default.randomBytes(32).toString("base64url");
129
157
  return new import_jose.SignJWT(payload).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setIssuedAt().setNotBefore(payload.nbf ?? "0s").setExpirationTime(payload.exp ?? "15d").setJti(jti).sign(secretKey);
130
158
  } catch (error) {
131
159
  if (isAuraJoseError(error)) {
@@ -157,7 +185,7 @@ var createJWS = (secret) => {
157
185
  };
158
186
 
159
187
  // src/encrypt.ts
160
- var import_node_crypto2 = __toESM(require("crypto"), 1);
188
+ var import_crypto2 = __toESM(require("crypto"), 1);
161
189
  var import_jose2 = require("jose");
162
190
  var encryptJWE = async (payload, secret, options) => {
163
191
  try {
@@ -165,7 +193,7 @@ var encryptJWE = async (payload, secret, options) => {
165
193
  throw new InvalidPayloadError("The payload must be a non-empty string");
166
194
  }
167
195
  const secretKey = createSecret(secret);
168
- const jti = import_node_crypto2.default.randomBytes(32).toString("base64url");
196
+ const jti = import_crypto2.default.randomBytes(32).toString("base64url");
169
197
  return new import_jose2.EncryptJWT({ payload }).setProtectedHeader({ alg: "dir", enc: "A256GCM", typ: "JWT", cty: "JWT" }).setIssuedAt().setNotBefore(options?.nbf ?? "0s").setExpirationTime(options?.exp ?? "15d").setJti(jti).encrypt(secretKey);
170
198
  } catch (error) {
171
199
  if (isAuraJoseError(error)) {
@@ -197,10 +225,10 @@ var createJWE = (secret) => {
197
225
  };
198
226
 
199
227
  // src/deriveKey.ts
200
- var import_node_crypto3 = require("crypto");
228
+ var import_crypto3 = require("crypto");
201
229
  var deriveKey = (secret, salt, info, length = 32) => {
202
230
  try {
203
- const key = (0, import_node_crypto3.hkdfSync)("SHA256", secret, salt, info, length);
231
+ const key = (0, import_crypto3.hkdfSync)("SHA256", secret, salt, info, length);
204
232
  const derivedKey = Buffer.from(key);
205
233
  return {
206
234
  key,
@@ -230,13 +258,13 @@ var encodeJWT = async (token, secret) => {
230
258
  throw new JWTEncodingError("JWT encoding failed", { cause: error });
231
259
  }
232
260
  };
233
- var decodeJWT = async (token, secret) => {
261
+ var decodeJWT = async (token, secret, options) => {
234
262
  try {
235
263
  const { jweSecret, jwsSecret } = getSecrets(secret);
236
264
  const { verifyJWS: verifyJWS2 } = createJWS(jwsSecret);
237
265
  const { decryptJWE: decryptJWE2 } = createJWE(jweSecret);
238
- const decrypted = await decryptJWE2(token);
239
- return await verifyJWS2(decrypted);
266
+ const decrypted = await decryptJWE2(token, options?.jwt);
267
+ return await verifyJWS2(decrypted, options?.jws);
240
268
  } catch (error) {
241
269
  if (isAuraJoseError(error)) {
242
270
  throw error;
@@ -252,15 +280,19 @@ var createJWT = (secret) => {
252
280
  };
253
281
  // Annotate the CommonJS export names for ESM import in node:
254
282
  0 && (module.exports = {
283
+ MIN_SECRET_ENTROPY_BITS,
255
284
  createDeriveKey,
256
285
  createJWE,
257
286
  createJWS,
258
287
  createJWT,
288
+ createSecret,
259
289
  decodeJWT,
260
290
  decryptJWE,
261
291
  deriveKey,
262
292
  encodeJWT,
263
293
  encryptJWE,
294
+ getEntropy,
295
+ getSecrets,
264
296
  signJWS,
265
297
  verifyJWS
266
298
  });
package/dist/index.d.ts CHANGED
@@ -1,154 +1,3 @@
1
- import { KeyObject, BinaryLike } from 'node:crypto';
2
- import { JWTPayload, JWTVerifyOptions, JWTDecryptOptions } from 'jose';
3
1
  export { JWTDecryptOptions, JWTVerifyOptions } from 'jose';
4
-
5
- /**
6
- * Sign a standard JWT token with the following claims:
7
- * - alg: algorithm used to sign the JWT
8
- * - typ: type of the token
9
- * - iat: time at which the JWT was issued
10
- * - nbf: not before time of the JWT
11
- * - exp: expiration time of the JWT
12
- * - jti: unique identifier to avoid collisions
13
- *
14
- * @param payload - Payload data information to sign the JWT
15
- * @param secret - Secret key to sign the JWT (CryptoKey, KeyObject, string or Uint8Array)
16
- * @returns Signed JWT string
17
- */
18
- declare const signJWS: (payload: JWTPayload, secret: SecretInput) => Promise<string>;
19
- /**
20
- * Verify the integrity of a JWT token and return the payload if valid, rejecting
21
- * tokens that use the "none" algorithm to prevent unsecured tokens.
22
- *
23
- * @see https://datatracker.ietf.org/doc/html/rfc7519#section-6 Unsecured JWTs
24
- * @param token - JWT string to verify
25
- * @param secret - CryptoKey or KeyObject used to verify the JWT
26
- * @returns verify and return the payload of the JWT
27
- */
28
- declare const verifyJWS: (token: string, secret: SecretInput, options?: JWTVerifyOptions) => Promise<JWTPayload>;
29
- /**
30
- * Create a JWS (JSON Web Signature) signer and verifier. It implements the `signJWS`
31
- * and `verifyJWS` functions of the module.
32
- *
33
- * @param secret - Secret key used for signing and verifying the JWS
34
- * @returns signJWS and verifyJWS functions
35
- */
36
- declare const createJWS: (secret: SecretInput) => {
37
- signJWS: (payload: JWTPayload) => Promise<string>;
38
- verifyJWS: (payload: string, options?: JWTVerifyOptions) => Promise<JWTPayload>;
39
- };
40
-
41
- interface EncryptedPayload {
42
- payload: string;
43
- }
44
- interface EncryptOptions {
45
- nbf?: string | number | Date;
46
- exp?: string | number | Date;
47
- }
48
- /**
49
- * Encrypt a standard JWT token with the following claims:
50
- * - alg: algorithm used to encrypt the JWT
51
- * - enc: encryption method used
52
- * - typ: type of the token
53
- * - cty: content type of the token
54
- *
55
- * @param payload - Payload data information to encrypt the JWT
56
- * @param secret - Secret key to encrypt the JWT (CryptoKey, KeyObject, string or Uint8Array)
57
- * @returns Encrypted JWT string
58
- */
59
- declare const encryptJWE: (payload: string, secret: SecretInput, options?: EncryptOptions) => Promise<string>;
60
- /**
61
- * Decrypt a JWE token and return the payload if valid.
62
- *
63
- * @param token - Encrypted JWT string to decrypt
64
- * @param secret - Secret key to decrypt the JWT (CryptoKey, KeyObject, string or Uint8Array)
65
- * @returns Decrypted JWT payload string
66
- */
67
- declare const decryptJWE: (token: string, secret: SecretInput, options?: JWTDecryptOptions) => Promise<string>;
68
- /**
69
- * Creates a `JWE (JSON Web Encryption)` encrypter and decrypter. It implements the `encryptJWE`
70
- * and `decryptJWE` functions of the module.
71
- *
72
- * @param secret - Secret key used for encrypting and decrypting the JWE
73
- * @returns encryptJWE and decryptJWE functions
74
- */
75
- declare const createJWE: (secret: SecretInput) => {
76
- encryptJWE: (payload: string, options?: EncryptOptions) => Promise<string>;
77
- decryptJWE: (payload: string, options?: JWTDecryptOptions) => Promise<string>;
78
- };
79
-
80
- /**
81
- * @module @aura-stack/jose
82
- */
83
-
84
- type SecretInput = KeyObject | Uint8Array | string;
85
- type DerivedKeyInput = {
86
- jws: SecretInput;
87
- jwe: SecretInput;
88
- };
89
- /**
90
- * Encode a JWT signed and encrypted token. The token first signed using JWS
91
- * and then encrypted using JWE to ensure both integrity and confidentiality.
92
- * It implements the `signJWS` and `encryptJWE` functions of the module.
93
- *
94
- * Based on the RFC 7519 standard
95
- * - Official RFC: https://datatracker.ietf.org/doc/html/rfc7519
96
- * - Nested JWTs should be signed and then encrypted: https://datatracker.ietf.org/doc/html/rfc7519#section-5.2
97
- * - Ensuring the integrity and confidentiality of the claims: https://datatracker.ietf.org/doc/html/rfc7519#section-11.2
98
- *
99
- * @param token - Payload data to encode in the JWT
100
- * @param secret - Secret key used for both signing and encrypting the JWT
101
- * @returns Promise resolving to the signed and encrypted JWT string
102
- */
103
- declare const encodeJWT: (token: JWTPayload, secret: SecretInput | DerivedKeyInput) => Promise<string>;
104
- /**
105
- * Decode a JWT signed and encrypted token. The token is first decrypted using JWE
106
- * and then verified using JWS to ensure both confidentiality and integrity. It
107
- * implements the `decryptJWE` and `verifyJWS` functions of the module.
108
- *
109
- * Based on the RFC 7519 standard
110
- * - Official RFC: https://datatracker.ietf.org/doc/html/rfc7519
111
- * - Validating a JWT: https://datatracker.ietf.org/doc/html/rfc7519#section-7.2
112
- * @param token
113
- * @param secret
114
- * @returns
115
- */
116
- declare const decodeJWT: (token: string, secret: SecretInput | DerivedKeyInput) => Promise<JWTPayload>;
117
- /**
118
- * Create a JWT handler with encode and decode methods to `signJWS/encryptJWE` and `verifyJWS/decryptJWE`
119
- * JWT tokens. The JWTs are signed and verified using JWS and encrypted and decrypted using JWE. It
120
- * implements the `signJWS`, `verifyJWS`, `encryptJWE` and `decryptJWE` functions of the module.
121
- *
122
- * @param secret - Secret key used for signing, verifying, encrypting and decrypting the JWT
123
- * @returns JWT handler object with `signJWS/encryptJWE` and `verifyJWS/decryptJWE` methods
124
- */
125
- declare const createJWT: (secret: SecretInput | DerivedKeyInput) => {
126
- encodeJWT: (payload: JWTPayload) => Promise<string>;
127
- decodeJWT: (token: string) => Promise<JWTPayload>;
128
- };
129
-
130
- /**
131
- * Generate a derived key using HKDF (HMAC-based Extract-and-Expand Key Derivation Function)
132
- *
133
- * @param secret Value used as the input keying material
134
- * @param salt Cryptographic salt
135
- * @param info Context and application specific information
136
- * @param length Size of the derived key in bytes (default is 32 bytes)
137
- * @returns Derived key as Uint8Array and base64 encoded string
138
- */
139
- declare const deriveKey: (secret: SecretInput, salt: BinaryLike, info: string, length?: number) => {
140
- key: ArrayBuffer;
141
- derivedKey: Buffer<ArrayBuffer>;
142
- };
143
- /**
144
- * Create a derived key from a given secret.
145
- *
146
- * @param secret - The secret as a string or Uint8Array
147
- * @returns The secret in Uint8Array format
148
- */
149
- declare const createDeriveKey: (secret: SecretInput, salt?: BinaryLike, info?: string, length?: number) => {
150
- key: ArrayBuffer;
151
- derivedKey: Buffer<ArrayBuffer>;
152
- };
153
-
154
- export { type DerivedKeyInput, type EncryptOptions, type EncryptedPayload, type SecretInput, createDeriveKey, createJWE, createJWS, createJWT, decodeJWT, decryptJWE, deriveKey, encodeJWT, encryptJWE, signJWS, verifyJWS };
2
+ import 'crypto';
3
+ export { h as DecodedJWTPayloadOptions, D as DerivedKeyInput, a as EncryptOptions, E as EncryptedPayload, M as MIN_SECRET_ENTROPY_BITS, S as SecretInput, m as createDeriveKey, c as createJWE, createJWS, k as createJWT, b as createSecret, j as decodeJWT, d as decryptJWE, l as deriveKey, i as encodeJWT, e as encryptJWE, g as getEntropy, f as getSecrets, signJWS, verifyJWS } from './sign.js';
package/dist/index.js CHANGED
@@ -1,20 +1,23 @@
1
1
  import {
2
2
  createDeriveKey,
3
3
  deriveKey
4
- } from "./chunk-K5BQTFSO.js";
4
+ } from "./chunk-CXN54JNY.js";
5
5
  import {
6
6
  createJWE,
7
7
  decryptJWE,
8
8
  encryptJWE
9
- } from "./chunk-VPFE27PW.js";
9
+ } from "./chunk-URDLFFH3.js";
10
10
  import {
11
11
  createJWS,
12
12
  signJWS,
13
13
  verifyJWS
14
- } from "./chunk-ZHDED44B.js";
14
+ } from "./chunk-EX3NULRX.js";
15
15
  import {
16
+ MIN_SECRET_ENTROPY_BITS,
17
+ createSecret,
18
+ getEntropy,
16
19
  getSecrets
17
- } from "./chunk-GXM4P5MQ.js";
20
+ } from "./chunk-SES6WQL3.js";
18
21
  import {
19
22
  isAuraJoseError
20
23
  } from "./chunk-ZHFHDRQH.js";
@@ -38,13 +41,13 @@ var encodeJWT = async (token, secret) => {
38
41
  throw new JWTEncodingError("JWT encoding failed", { cause: error });
39
42
  }
40
43
  };
41
- var decodeJWT = async (token, secret) => {
44
+ var decodeJWT = async (token, secret, options) => {
42
45
  try {
43
46
  const { jweSecret, jwsSecret } = getSecrets(secret);
44
47
  const { verifyJWS: verifyJWS2 } = createJWS(jwsSecret);
45
48
  const { decryptJWE: decryptJWE2 } = createJWE(jweSecret);
46
- const decrypted = await decryptJWE2(token);
47
- return await verifyJWS2(decrypted);
49
+ const decrypted = await decryptJWE2(token, options?.jwt);
50
+ return await verifyJWS2(decrypted, options?.jws);
48
51
  } catch (error) {
49
52
  if (isAuraJoseError(error)) {
50
53
  throw error;
@@ -59,15 +62,19 @@ var createJWT = (secret) => {
59
62
  };
60
63
  };
61
64
  export {
65
+ MIN_SECRET_ENTROPY_BITS,
62
66
  createDeriveKey,
63
67
  createJWE,
64
68
  createJWS,
65
69
  createJWT,
70
+ createSecret,
66
71
  decodeJWT,
67
72
  decryptJWE,
68
73
  deriveKey,
69
74
  encodeJWT,
70
75
  encryptJWE,
76
+ getEntropy,
77
+ getSecrets,
71
78
  signJWS,
72
79
  verifyJWS
73
80
  };
package/dist/secret.cjs CHANGED
@@ -20,7 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/secret.ts
21
21
  var secret_exports = {};
22
22
  __export(secret_exports, {
23
+ MIN_SECRET_ENTROPY_BITS: () => MIN_SECRET_ENTROPY_BITS,
23
24
  createSecret: () => createSecret,
25
+ getEntropy: () => getEntropy,
24
26
  getSecrets: () => getSecrets
25
27
  });
26
28
  module.exports = __toCommonJS(secret_exports);
@@ -46,11 +48,35 @@ var isObject = (value) => {
46
48
  };
47
49
 
48
50
  // src/secret.ts
49
- var createSecret = (secret) => {
50
- if (secret === void 0) throw new InvalidSecretError("Secret is required");
51
+ var MIN_SECRET_ENTROPY_BITS = 4.5;
52
+ var getEntropy = (secret) => {
53
+ const charFreq = /* @__PURE__ */ new Map();
54
+ for (const char of secret) {
55
+ if (!charFreq.has(char)) {
56
+ charFreq.set(char, 0);
57
+ }
58
+ charFreq.set(char, charFreq.get(char) + 1);
59
+ }
60
+ let entropy = 0;
61
+ const length = secret.length;
62
+ for (const freq of charFreq.values()) {
63
+ const p = freq / length;
64
+ entropy -= p * Math.log2(p);
65
+ }
66
+ return entropy;
67
+ };
68
+ var createSecret = (secret, length = 32) => {
69
+ if (!Boolean(secret)) throw new InvalidSecretError("Secret is required");
51
70
  if (typeof secret === "string") {
52
- if (new TextEncoder().encode(secret).byteLength < 32) {
53
- throw new InvalidSecretError("Secret string must be at least 32 characters long");
71
+ const byteLength = new TextEncoder().encode(secret).byteLength;
72
+ if (byteLength < length) {
73
+ throw new InvalidSecretError(`Secret string must be at least ${length} bytes long`);
74
+ }
75
+ const entropy = getEntropy(secret);
76
+ if (entropy < MIN_SECRET_ENTROPY_BITS) {
77
+ throw new InvalidSecretError(
78
+ `Secret string must have an entropy of at least ${MIN_SECRET_ENTROPY_BITS} bits per character`
79
+ );
54
80
  }
55
81
  return new Uint8Array(Buffer.from(secret, "utf-8"));
56
82
  }
@@ -66,6 +92,8 @@ var getSecrets = (secret) => {
66
92
  };
67
93
  // Annotate the CommonJS export names for ESM import in node:
68
94
  0 && (module.exports = {
95
+ MIN_SECRET_ENTROPY_BITS,
69
96
  createSecret,
97
+ getEntropy,
70
98
  getSecrets
71
99
  });
package/dist/secret.d.ts CHANGED
@@ -1,18 +1,3 @@
1
- import * as crypto from 'crypto';
2
- import { SecretInput, DerivedKeyInput } from './index.js';
3
- import 'node:crypto';
1
+ import 'crypto';
2
+ export { M as MIN_SECRET_ENTROPY_BITS, b as createSecret, g as getEntropy, f as getSecrets } from './sign.js';
4
3
  import 'jose';
5
-
6
- /**
7
- * Create a secret in Uint8Array format
8
- *
9
- * @param secret - The secret as a string or Uint8Array
10
- * @returns The secret in Uint8Array format
11
- */
12
- declare const createSecret: (secret: SecretInput) => crypto.KeyObject | Uint8Array<ArrayBufferLike>;
13
- declare const getSecrets: (secret: SecretInput | DerivedKeyInput) => {
14
- jwsSecret: SecretInput;
15
- jweSecret: SecretInput;
16
- };
17
-
18
- export { createSecret, getSecrets };
package/dist/secret.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import {
2
+ MIN_SECRET_ENTROPY_BITS,
2
3
  createSecret,
4
+ getEntropy,
3
5
  getSecrets
4
- } from "./chunk-GXM4P5MQ.js";
6
+ } from "./chunk-SES6WQL3.js";
5
7
  import "./chunk-ZHFHDRQH.js";
6
8
  import "./chunk-BMXFAB6Q.js";
7
9
  export {
10
+ MIN_SECRET_ENTROPY_BITS,
8
11
  createSecret,
12
+ getEntropy,
9
13
  getSecrets
10
14
  };
package/dist/sign.cjs CHANGED
@@ -35,7 +35,7 @@ __export(sign_exports, {
35
35
  verifyJWS: () => verifyJWS
36
36
  });
37
37
  module.exports = __toCommonJS(sign_exports);
38
- var import_node_crypto = __toESM(require("crypto"), 1);
38
+ var import_crypto = __toESM(require("crypto"), 1);
39
39
  var import_jose = require("jose");
40
40
 
41
41
  // src/errors.ts
@@ -77,11 +77,35 @@ var isInvalidPayload = (payload) => {
77
77
  };
78
78
 
79
79
  // src/secret.ts
80
- var createSecret = (secret) => {
81
- if (secret === void 0) throw new InvalidSecretError("Secret is required");
80
+ var MIN_SECRET_ENTROPY_BITS = 4.5;
81
+ var getEntropy = (secret) => {
82
+ const charFreq = /* @__PURE__ */ new Map();
83
+ for (const char of secret) {
84
+ if (!charFreq.has(char)) {
85
+ charFreq.set(char, 0);
86
+ }
87
+ charFreq.set(char, charFreq.get(char) + 1);
88
+ }
89
+ let entropy = 0;
90
+ const length = secret.length;
91
+ for (const freq of charFreq.values()) {
92
+ const p = freq / length;
93
+ entropy -= p * Math.log2(p);
94
+ }
95
+ return entropy;
96
+ };
97
+ var createSecret = (secret, length = 32) => {
98
+ if (!Boolean(secret)) throw new InvalidSecretError("Secret is required");
82
99
  if (typeof secret === "string") {
83
- if (new TextEncoder().encode(secret).byteLength < 32) {
84
- throw new InvalidSecretError("Secret string must be at least 32 characters long");
100
+ const byteLength = new TextEncoder().encode(secret).byteLength;
101
+ if (byteLength < length) {
102
+ throw new InvalidSecretError(`Secret string must be at least ${length} bytes long`);
103
+ }
104
+ const entropy = getEntropy(secret);
105
+ if (entropy < MIN_SECRET_ENTROPY_BITS) {
106
+ throw new InvalidSecretError(
107
+ `Secret string must have an entropy of at least ${MIN_SECRET_ENTROPY_BITS} bits per character`
108
+ );
85
109
  }
86
110
  return new Uint8Array(Buffer.from(secret, "utf-8"));
87
111
  }
@@ -95,7 +119,7 @@ var signJWS = async (payload, secret) => {
95
119
  throw new InvalidPayloadError("The payload must be a non-empty object");
96
120
  }
97
121
  const secretKey = createSecret(secret);
98
- const jti = import_node_crypto.default.randomBytes(32).toString("base64url");
122
+ const jti = import_crypto.default.randomBytes(32).toString("base64url");
99
123
  return new import_jose.SignJWT(payload).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setIssuedAt().setNotBefore(payload.nbf ?? "0s").setExpirationTime(payload.exp ?? "15d").setJti(jti).sign(secretKey);
100
124
  } catch (error) {
101
125
  if (isAuraJoseError(error)) {
package/dist/sign.d.ts CHANGED
@@ -1,3 +1,174 @@
1
+ import * as crypto from 'crypto';
2
+ import { KeyObject, BinaryLike } from 'crypto';
3
+ import { JWTPayload, JWTVerifyOptions, JWTDecryptOptions } from 'jose';
1
4
  export { JWTVerifyOptions } from 'jose';
2
- export { createJWS, signJWS, verifyJWS } from './index.js';
3
- import 'node:crypto';
5
+
6
+ /**
7
+ * Sign a standard JWT token with the following claims:
8
+ * - alg: algorithm used to sign the JWT
9
+ * - typ: type of the token
10
+ * - iat: time at which the JWT was issued
11
+ * - nbf: not before time of the JWT
12
+ * - exp: expiration time of the JWT
13
+ * - jti: unique identifier to avoid collisions
14
+ *
15
+ * @param payload - Payload data information to sign the JWT
16
+ * @param secret - Secret key to sign the JWT (CryptoKey, KeyObject, string or Uint8Array)
17
+ * @returns Signed JWT string
18
+ */
19
+ declare const signJWS: (payload: JWTPayload, secret: SecretInput) => Promise<string>;
20
+ /**
21
+ * Verify the integrity of a JWT token and return the payload if valid, rejecting
22
+ * tokens that use the "none" algorithm to prevent unsecured tokens.
23
+ *
24
+ * @see https://datatracker.ietf.org/doc/html/rfc7519#section-6 Unsecured JWTs
25
+ * @param token - JWT string to verify
26
+ * @param secret - CryptoKey or KeyObject used to verify the JWT
27
+ * @param options - Additional JWT verification options
28
+ * @returns verify and return the payload of the JWT
29
+ */
30
+ declare const verifyJWS: (token: string, secret: SecretInput, options?: JWTVerifyOptions) => Promise<JWTPayload>;
31
+ /**
32
+ * Create a JWS (JSON Web Signature) signer and verifier. It implements the `signJWS`
33
+ * and `verifyJWS` functions of the module.
34
+ *
35
+ * @param secret - Secret key used for signing and verifying the JWS
36
+ * @returns signJWS and verifyJWS functions
37
+ */
38
+ declare const createJWS: (secret: SecretInput) => {
39
+ signJWS: (payload: JWTPayload) => Promise<string>;
40
+ verifyJWS: (payload: string, options?: JWTVerifyOptions) => Promise<JWTPayload>;
41
+ };
42
+
43
+ interface EncryptedPayload {
44
+ payload: string;
45
+ }
46
+ interface EncryptOptions {
47
+ nbf?: string | number | Date;
48
+ exp?: string | number | Date;
49
+ }
50
+ /**
51
+ * Encrypt a standard JWT token with the following claims:
52
+ * - alg: algorithm used to encrypt the JWT
53
+ * - enc: encryption method used
54
+ * - typ: type of the token
55
+ * - cty: content type of the token
56
+ *
57
+ * @param payload - Payload data information to encrypt the JWT
58
+ * @param secret - Secret key to encrypt the JWT (CryptoKey, KeyObject, string or Uint8Array)
59
+ * @returns Encrypted JWT string
60
+ */
61
+ declare const encryptJWE: (payload: string, secret: SecretInput, options?: EncryptOptions) => Promise<string>;
62
+ /**
63
+ * Decrypt a JWE token and return the payload if valid.
64
+ *
65
+ * @param token - Encrypted JWT string to decrypt
66
+ * @param secret - Secret key to decrypt the JWT (CryptoKey, KeyObject, string or Uint8Array)
67
+ * @returns Decrypted JWT payload string
68
+ */
69
+ declare const decryptJWE: (token: string, secret: SecretInput, options?: JWTDecryptOptions) => Promise<string>;
70
+ /**
71
+ * Creates a `JWE (JSON Web Encryption)` encrypter and decrypter. It implements the `encryptJWE`
72
+ * and `decryptJWE` functions of the module.
73
+ *
74
+ * @param secret - Secret key used for encrypting and decrypting the JWE
75
+ * @returns encryptJWE and decryptJWE functions
76
+ */
77
+ declare const createJWE: (secret: SecretInput) => {
78
+ encryptJWE: (payload: string, options?: EncryptOptions) => Promise<string>;
79
+ decryptJWE: (payload: string, options?: JWTDecryptOptions) => Promise<string>;
80
+ };
81
+
82
+ declare const MIN_SECRET_ENTROPY_BITS = 4.5;
83
+ declare const getEntropy: (secret: string) => number;
84
+ /**
85
+ * Create a secret in Uint8Array format
86
+ *
87
+ * @param secret - The secret as a string or Uint8Array
88
+ * @returns The secret in Uint8Array format
89
+ */
90
+ declare const createSecret: (secret: SecretInput, length?: number) => crypto.KeyObject | Uint8Array<ArrayBufferLike>;
91
+ declare const getSecrets: (secret: SecretInput | DerivedKeyInput) => {
92
+ jwsSecret: SecretInput;
93
+ jweSecret: SecretInput;
94
+ };
95
+
96
+ /**
97
+ * @module @aura-stack/jose
98
+ */
99
+
100
+ type SecretInput = KeyObject | Uint8Array | string;
101
+ type DerivedKeyInput = {
102
+ jws: SecretInput;
103
+ jwe: SecretInput;
104
+ };
105
+ type DecodedJWTPayloadOptions = {
106
+ jws: JWTVerifyOptions;
107
+ jwt: JWTDecryptOptions;
108
+ };
109
+ /**
110
+ * Encode a JWT signed and encrypted token. The token first signed using JWS
111
+ * and then encrypted using JWE to ensure both integrity and confidentiality.
112
+ * It implements the `signJWS` and `encryptJWE` functions of the module.
113
+ *
114
+ * Based on the RFC 7519 standard
115
+ * - Official RFC: https://datatracker.ietf.org/doc/html/rfc7519
116
+ * - Nested JWTs should be signed and then encrypted: https://datatracker.ietf.org/doc/html/rfc7519#section-5.2
117
+ * - Ensuring the integrity and confidentiality of the claims: https://datatracker.ietf.org/doc/html/rfc7519#section-11.2
118
+ *
119
+ * @param token - Payload data to encode in the JWT
120
+ * @param secret - Secret key used for both signing and encrypting the JWT
121
+ * @returns Promise resolving to the signed and encrypted JWT string
122
+ */
123
+ declare const encodeJWT: (token: JWTPayload, secret: SecretInput | DerivedKeyInput) => Promise<string>;
124
+ /**
125
+ * Decode a JWT signed and encrypted token. The token is first decrypted using JWE
126
+ * and then verified using JWS to ensure both confidentiality and integrity. It
127
+ * implements the `decryptJWE` and `verifyJWS` functions of the module.
128
+ *
129
+ * Based on the RFC 7519 standard
130
+ * - Official RFC: https://datatracker.ietf.org/doc/html/rfc7519
131
+ * - Validating a JWT: https://datatracker.ietf.org/doc/html/rfc7519#section-7.2
132
+ * @param token
133
+ * @param secret
134
+ * @returns
135
+ */
136
+ declare const decodeJWT: (token: string, secret: SecretInput | DerivedKeyInput, options?: DecodedJWTPayloadOptions) => Promise<JWTPayload>;
137
+ /**
138
+ * Create a JWT handler with encode and decode methods to `signJWS/encryptJWE` and `verifyJWS/decryptJWE`
139
+ * JWT tokens. The JWTs are signed and verified using JWS and encrypted and decrypted using JWE. It
140
+ * implements the `signJWS`, `verifyJWS`, `encryptJWE` and `decryptJWE` functions of the module.
141
+ *
142
+ * @param secret - Secret key used for signing, verifying, encrypting and decrypting the JWT
143
+ * @returns JWT handler object with `signJWS/encryptJWE` and `verifyJWS/decryptJWE` methods
144
+ */
145
+ declare const createJWT: (secret: SecretInput | DerivedKeyInput) => {
146
+ encodeJWT: (payload: JWTPayload) => Promise<string>;
147
+ decodeJWT: (token: string) => Promise<JWTPayload>;
148
+ };
149
+
150
+ /**
151
+ * Generate a derived key using HKDF (HMAC-based Extract-and-Expand Key Derivation Function)
152
+ *
153
+ * @param secret Value used as the input keying material
154
+ * @param salt Cryptographic salt
155
+ * @param info Context and application specific information
156
+ * @param length Size of the derived key in bytes (default is 32 bytes)
157
+ * @returns Derived key as Uint8Array and base64 encoded string
158
+ */
159
+ declare const deriveKey: (secret: SecretInput, salt: BinaryLike, info: string, length?: number) => {
160
+ key: ArrayBuffer;
161
+ derivedKey: Buffer<ArrayBuffer>;
162
+ };
163
+ /**
164
+ * Create a derived key from a given secret.
165
+ *
166
+ * @param secret - The secret as a string or Uint8Array
167
+ * @returns The secret in Uint8Array format
168
+ */
169
+ declare const createDeriveKey: (secret: SecretInput, salt?: BinaryLike, info?: string, length?: number) => {
170
+ key: ArrayBuffer;
171
+ derivedKey: Buffer<ArrayBuffer>;
172
+ };
173
+
174
+ export { type DerivedKeyInput as D, type EncryptedPayload as E, MIN_SECRET_ENTROPY_BITS as M, type SecretInput as S, type EncryptOptions as a, createSecret as b, createJWE as c, createJWS, decryptJWE as d, encryptJWE as e, getSecrets as f, getEntropy as g, type DecodedJWTPayloadOptions as h, encodeJWT as i, decodeJWT as j, createJWT as k, deriveKey as l, createDeriveKey as m, signJWS, verifyJWS };
package/dist/sign.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  createJWS,
3
3
  signJWS,
4
4
  verifyJWS
5
- } from "./chunk-ZHDED44B.js";
6
- import "./chunk-GXM4P5MQ.js";
5
+ } from "./chunk-EX3NULRX.js";
6
+ import "./chunk-SES6WQL3.js";
7
7
  import "./chunk-ZHFHDRQH.js";
8
8
  import "./chunk-BMXFAB6Q.js";
9
9
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aura-stack/jose",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "JOSE utilities for @aura-stack/auth",
@@ -57,6 +57,8 @@
57
57
  "jose": "^6.1.2"
58
58
  },
59
59
  "devDependencies": {
60
+ "@types/node": "^24.9.2",
61
+ "typescript": "^5.9.2",
60
62
  "@aura-stack/tsconfig": "0.0.0",
61
63
  "@aura-stack/tsup-config": "0.0.0"
62
64
  },
@@ -1,31 +0,0 @@
1
- import {
2
- isObject
3
- } from "./chunk-ZHFHDRQH.js";
4
- import {
5
- InvalidSecretError
6
- } from "./chunk-BMXFAB6Q.js";
7
-
8
- // src/secret.ts
9
- var createSecret = (secret) => {
10
- if (secret === void 0) throw new InvalidSecretError("Secret is required");
11
- if (typeof secret === "string") {
12
- if (new TextEncoder().encode(secret).byteLength < 32) {
13
- throw new InvalidSecretError("Secret string must be at least 32 characters long");
14
- }
15
- return new Uint8Array(Buffer.from(secret, "utf-8"));
16
- }
17
- return secret;
18
- };
19
- var getSecrets = (secret) => {
20
- const jwsSecret = isObject(secret) && "jws" in secret ? secret.jws : secret;
21
- const jweSecret = isObject(secret) && "jwe" in secret ? secret.jwe : secret;
22
- return {
23
- jwsSecret,
24
- jweSecret
25
- };
26
- };
27
-
28
- export {
29
- createSecret,
30
- getSecrets
31
- };