@absolutejs/auth 0.55.8 → 0.56.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,5 +1,11 @@
1
1
  export type SecretCipher = {
2
2
  decrypt: (ciphertext: string) => Promise<string>;
3
3
  encrypt: (plaintext: string) => Promise<string>;
4
+ needsReencryption?: (ciphertext: string) => boolean;
4
5
  };
5
6
  export declare const createSecretCipher: (keyMaterial: string) => SecretCipher;
7
+ export declare const createVersionedSecretCipher: ({ currentVersion, keys, legacyKey }: {
8
+ currentVersion: number;
9
+ keys: Readonly<Record<number, string>>;
10
+ legacyKey?: string;
11
+ }) => SecretCipher;
package/dist/index.js CHANGED
@@ -3214,6 +3214,7 @@ init_crypto();
3214
3214
  var ENCODER = new TextEncoder;
3215
3215
  var ES256 = { hash: "SHA-256", name: "ECDSA" };
3216
3216
  var KEY_PARAMS = { name: "ECDSA", namedCurve: "P-256" };
3217
+ var ES256_JOSE_SIGNATURE_BYTES = 64;
3217
3218
  var toBase64Url = (bytes) => Buffer.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)).toString("base64url");
3218
3219
  var fromBase64Url = (value) => new Uint8Array(Buffer.from(value, "base64url"));
3219
3220
  var encodeSegment = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
@@ -3247,9 +3248,18 @@ var jwkThumbprint = async (jwk) => {
3247
3248
  return toBase64Url(await crypto.subtle.digest("SHA-256", ENCODER.encode(canonical)));
3248
3249
  };
3249
3250
  var signJwt = async (payload, signing, typ = "JWT") => {
3250
- const key = await crypto.subtle.importKey("jwk", signing.privateJwk, KEY_PARAMS, false, ["sign"]);
3251
3251
  const input = `${encodeSegment({ alg: "ES256", kid: signing.kid, typ })}.${encodeSegment(payload)}`;
3252
- const signature = await crypto.subtle.sign(ES256, key, ENCODER.encode(input));
3252
+ const encoded = ENCODER.encode(input);
3253
+ let signature;
3254
+ if (signing.sign !== undefined) {
3255
+ signature = await signing.sign(encoded);
3256
+ } else {
3257
+ const key = await crypto.subtle.importKey("jwk", signing.privateJwk, KEY_PARAMS, false, ["sign"]);
3258
+ signature = await crypto.subtle.sign(ES256, key, encoded);
3259
+ }
3260
+ if (signature.byteLength !== ES256_JOSE_SIGNATURE_BYTES) {
3261
+ throw new Error("ES256 signer must return a 64-byte JOSE signature");
3262
+ }
3253
3263
  return `${input}.${toBase64Url(signature)}`;
3254
3264
  };
3255
3265
  var toPublicJwk = (key) => ({
@@ -12214,6 +12224,44 @@ var createSecretCipher = (keyMaterial) => ({
12214
12224
  decrypt: (ciphertext) => decryptSecret(ciphertext, keyMaterial),
12215
12225
  encrypt: (plaintext) => encryptSecret(plaintext, keyMaterial)
12216
12226
  });
12227
+ var VERSIONED_CIPHERTEXT_PREFIX = "absolute-vault";
12228
+ var VERSIONED_CIPHERTEXT_SEGMENTS = 3;
12229
+ var parseVersionedCiphertext = (value) => {
12230
+ const [prefix, encodedVersion, ciphertext] = value.split(":", VERSIONED_CIPHERTEXT_SEGMENTS);
12231
+ if (prefix !== VERSIONED_CIPHERTEXT_PREFIX || !encodedVersion || !ciphertext)
12232
+ return null;
12233
+ const version = Number(encodedVersion);
12234
+ if (!Number.isSafeInteger(version) || version < 1)
12235
+ return null;
12236
+ return { ciphertext, version };
12237
+ };
12238
+ var createVersionedSecretCipher = ({
12239
+ currentVersion,
12240
+ keys,
12241
+ legacyKey
12242
+ }) => {
12243
+ if (!Number.isSafeInteger(currentVersion) || currentVersion < 1)
12244
+ throw new Error("Vault key version must be a positive integer");
12245
+ const currentKey = keys[currentVersion];
12246
+ if (!currentKey)
12247
+ throw new Error(`Vault key version ${currentVersion} is unavailable`);
12248
+ return {
12249
+ decrypt: async (value) => {
12250
+ const envelope = parseVersionedCiphertext(value);
12251
+ if (!envelope) {
12252
+ if (!legacyKey)
12253
+ throw new Error("Legacy vault ciphertext key is unavailable");
12254
+ return decryptSecret(value, legacyKey);
12255
+ }
12256
+ const key = keys[envelope.version];
12257
+ if (!key)
12258
+ throw new Error(`Vault key version ${envelope.version} is unavailable`);
12259
+ return decryptSecret(envelope.ciphertext, key);
12260
+ },
12261
+ encrypt: async (plaintext) => `${VERSIONED_CIPHERTEXT_PREFIX}:${currentVersion}:${await encryptSecret(plaintext, currentKey)}`,
12262
+ needsReencryption: (value) => parseVersionedCiphertext(value)?.version !== currentVersion
12263
+ };
12264
+ };
12217
12265
 
12218
12266
  // src/vault/config.ts
12219
12267
  var createVault = ({
@@ -12225,7 +12273,15 @@ var createVault = ({
12225
12273
  const entry = await store.getEntry(ownerId, name);
12226
12274
  if (entry === undefined)
12227
12275
  return;
12228
- return cipher.decrypt(entry.encryptedValue);
12276
+ const plaintext = await cipher.decrypt(entry.encryptedValue);
12277
+ if (cipher.needsReencryption?.(entry.encryptedValue)) {
12278
+ await store.saveEntry({
12279
+ ...entry,
12280
+ encryptedValue: await cipher.encrypt(plaintext),
12281
+ updatedAt: Date.now()
12282
+ });
12283
+ }
12284
+ return plaintext;
12229
12285
  },
12230
12286
  list: async (ownerId) => (await store.listEntries(ownerId)).map((entry) => entry.name),
12231
12287
  put: async (ownerId, name, value) => {
@@ -12249,11 +12305,12 @@ var rotateVaultKey = async ({
12249
12305
  const newCipher = createSecretCipher(newKey);
12250
12306
  const entries = await store.listAllEntries();
12251
12307
  const now = Date.now();
12252
- for (const entry of entries) {
12308
+ await entries.reduce(async (pending, entry) => {
12309
+ await pending;
12253
12310
  const plaintext = await oldCipher.decrypt(entry.encryptedValue);
12254
12311
  const encryptedValue = await newCipher.encrypt(plaintext);
12255
12312
  await store.saveEntry({ ...entry, encryptedValue, updatedAt: now });
12256
- }
12313
+ }, Promise.resolve());
12257
12314
  return { rotated: entries.length };
12258
12315
  };
12259
12316
  // src/vault/inMemoryVaultStore.ts
@@ -30084,5 +30141,5 @@ export {
30084
30141
  AGENT_CLAIM_GRANT_TYPE
30085
30142
  };
30086
30143
 
30087
- //# debugId=DCA509B790E265BD64756E2164756E21
30144
+ //# debugId=C0233F9D8EBEC87464756E2164756E21
30088
30145
  //# sourceMappingURL=index.js.map