diff --git a/src/vault/vault.spec.ts b/src/vault/vault.spec.ts index 22468e94f..0cba6ba12 100644 --- a/src/vault/vault.spec.ts +++ b/src/vault/vault.spec.ts @@ -300,13 +300,30 @@ describe('Vault', () => { // Decrypt the data const decrypted = await workos.vault.decrypt(encrypted, associatedData); - // Verify decrypt API call expect(fetchURL()).toContain('/vault/v1/keys/decrypt'); expect(fetchMethod()).toBe('POST'); // Verify the decrypted text matches the original expect(decrypted).toBe(originalText); + + // Reset fetch + fetch.resetMocks(); + + // Decrypt with an already known key + const dataKey = { + key: validKey, + id: 'key123', + }; + const decryptedLocal = await workos.vault.decrypt( + encrypted, + associatedData, + dataKey, + ); + expect(decryptedLocal).toBe(originalText); + + // No API calls should be made + expect(fetch.mock.calls.length).toEqual(0); }); }); }); diff --git a/src/vault/vault.ts b/src/vault/vault.ts index 876d7504f..b202f8770 100644 --- a/src/vault/vault.ts +++ b/src/vault/vault.ts @@ -52,7 +52,7 @@ export class Vault { this.cryptoProvider = workos.getCryptoProvider(); } - private decode(payload: string): Decoded { + async decode(payload: string): Promise { const inputData = base64ToUint8Array(payload); // Use 12 bytes for IV (standard for AES-GCM) const iv = new Uint8Array(inputData.subarray(0, 12)); @@ -153,10 +153,13 @@ export class Vault { data: string, context: KeyContext, associatedData?: string, + keyPair?: DataKeyPair, ): Promise { - const keyPair = await this.createDataKey({ - context, - }); + if (keyPair === undefined) { + keyPair = await this.createDataKey({ + context, + }); + } // Convert base64 key to Uint8Array const encoder = new TextEncoder(); @@ -213,11 +216,18 @@ export class Vault { } async decrypt( - encryptedData: string, + encryptedData: string | Decoded, associatedData?: string, + dataKey?: DataKey, ): Promise { - const decoded = this.decode(encryptedData); - const dataKey = await this.decryptDataKey({ keys: decoded.keys }); + const decoded = + typeof encryptedData === 'string' + ? await this.decode(encryptedData) + : encryptedData; + + if (dataKey === undefined) { + dataKey = await this.decryptDataKey({ keys: decoded.keys }); + } // Convert base64 key to Uint8Array using our cross-runtime utility const key = base64ToUint8Array(dataKey.key);