@workos-inc/node 7.50.0 → 7.50.1
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/lib/actions/actions.js +2 -2
- package/lib/actions/actions.spec.js +3 -3
- package/lib/common/crypto/crypto-provider.d.ts +33 -0
- package/lib/common/crypto/{CryptoProvider.spec.js → crypto-provider.spec.js} +8 -8
- package/lib/common/crypto/node-crypto-provider.d.ts +7 -0
- package/lib/common/crypto/node-crypto-provider.js +38 -2
- package/lib/common/crypto/{SignatureProvider.d.ts → signature-provider.d.ts} +1 -1
- package/lib/common/crypto/{SignatureProvider.spec.js → signature-provider.spec.js} +4 -4
- package/lib/common/crypto/subtle-crypto-provider.d.ts +7 -0
- package/lib/common/crypto/subtle-crypto-provider.js +48 -0
- package/lib/common/utils/base64.d.ts +12 -0
- package/lib/common/utils/base64.js +52 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +5 -9
- package/lib/index.worker.d.ts +2 -0
- package/lib/index.worker.js +3 -0
- package/lib/user-management/interfaces/update-user-options.interface.d.ts +2 -2
- package/lib/user-management/user-management.spec.js +10 -0
- package/lib/vault/vault.d.ts +5 -3
- package/lib/vault/vault.js +60 -6
- package/lib/vault/vault.spec.js +39 -0
- package/lib/webhooks/webhooks.js +2 -2
- package/lib/workos.d.ts +2 -0
- package/lib/workos.js +6 -3
- package/package.json +1 -1
- package/lib/common/crypto/CryptoProvider.d.ts +0 -32
- package/lib/common/crypto/CryptoProvider.js +0 -13
- package/lib/common/crypto/NodeCryptoProvider.d.ts +0 -12
- package/lib/common/crypto/NodeCryptoProvider.js +0 -73
- package/lib/common/crypto/SubtleCryptoProvider.d.ts +0 -15
- package/lib/common/crypto/SubtleCryptoProvider.js +0 -75
- package/lib/vault/cryptography/decrypt.d.ts +0 -9
- package/lib/vault/cryptography/decrypt.js +0 -39
- package/lib/vault/cryptography/encrypt.d.ts +0 -1
- package/lib/vault/cryptography/encrypt.js +0 -33
- /package/lib/common/crypto/{CryptoProvider.spec.d.ts → crypto-provider.spec.d.ts} +0 -0
- /package/lib/common/crypto/{SignatureProvider.js → signature-provider.js} +0 -0
- /package/lib/common/crypto/{SignatureProvider.spec.d.ts → signature-provider.spec.d.ts} +0 -0
package/lib/actions/actions.js
CHANGED
|
@@ -10,12 +10,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Actions = void 0;
|
|
13
|
-
const
|
|
13
|
+
const signature_provider_1 = require("../common/crypto/signature-provider");
|
|
14
14
|
const unreachable_1 = require("../common/utils/unreachable");
|
|
15
15
|
const action_serializer_1 = require("./serializers/action.serializer");
|
|
16
16
|
class Actions {
|
|
17
17
|
constructor(cryptoProvider) {
|
|
18
|
-
this.signatureProvider = new
|
|
18
|
+
this.signatureProvider = new signature_provider_1.SignatureProvider(cryptoProvider);
|
|
19
19
|
}
|
|
20
20
|
get computeSignature() {
|
|
21
21
|
return this.signatureProvider.computeSignature.bind(this.signatureProvider);
|
|
@@ -16,8 +16,8 @@ const crypto_1 = __importDefault(require("crypto"));
|
|
|
16
16
|
const workos_1 = require("../workos");
|
|
17
17
|
const authentication_action_context_json_1 = __importDefault(require("./fixtures/authentication-action-context.json"));
|
|
18
18
|
const user_registration_action_context_json_1 = __importDefault(require("./fixtures/user-registration-action-context.json"));
|
|
19
|
+
const node_crypto_provider_1 = require("../common/crypto/node-crypto-provider");
|
|
19
20
|
const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
|
|
20
|
-
const NodeCryptoProvider_1 = require("../common/crypto/NodeCryptoProvider");
|
|
21
21
|
describe('Actions', () => {
|
|
22
22
|
let secret;
|
|
23
23
|
beforeEach(() => {
|
|
@@ -36,7 +36,7 @@ describe('Actions', () => {
|
|
|
36
36
|
describe('signResponse', () => {
|
|
37
37
|
describe('type: authentication', () => {
|
|
38
38
|
it('returns a signed response', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
-
const nodeCryptoProvider = new
|
|
39
|
+
const nodeCryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
40
40
|
const response = yield workos.actions.signResponse({
|
|
41
41
|
type: 'authentication',
|
|
42
42
|
verdict: 'Allow',
|
|
@@ -51,7 +51,7 @@ describe('Actions', () => {
|
|
|
51
51
|
});
|
|
52
52
|
describe('type: user_registration', () => {
|
|
53
53
|
it('returns a signed response', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
-
const nodeCryptoProvider = new
|
|
54
|
+
const nodeCryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
55
55
|
const response = yield workos.actions.signResponse({
|
|
56
56
|
type: 'user_registration',
|
|
57
57
|
verdict: 'Deny',
|
|
@@ -29,4 +29,37 @@ export declare abstract class CryptoProvider {
|
|
|
29
29
|
* Cryptographically determine whether two signatures are equal
|
|
30
30
|
*/
|
|
31
31
|
abstract secureCompare(stringA: string, stringB: string): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Encrypts data using AES-256-GCM algorithm.
|
|
34
|
+
*
|
|
35
|
+
* @param plaintext The data to encrypt
|
|
36
|
+
* @param key The encryption key (should be 32 bytes for AES-256)
|
|
37
|
+
* @param iv Optional initialization vector (if not provided, a random one will be generated)
|
|
38
|
+
* @param aad Optional additional authenticated data
|
|
39
|
+
* @returns Object containing the encrypted ciphertext, the IV used, and the authentication tag
|
|
40
|
+
*/
|
|
41
|
+
abstract encrypt(plaintext: Uint8Array, key: Uint8Array, iv?: Uint8Array, aad?: Uint8Array): Promise<{
|
|
42
|
+
ciphertext: Uint8Array;
|
|
43
|
+
iv: Uint8Array;
|
|
44
|
+
tag: Uint8Array;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Decrypts data that was encrypted using AES-256-GCM algorithm.
|
|
48
|
+
*
|
|
49
|
+
* @param ciphertext The encrypted data
|
|
50
|
+
* @param key The decryption key (must be the same key used for encryption)
|
|
51
|
+
* @param iv The initialization vector used during encryption
|
|
52
|
+
* @param tag The authentication tag produced during encryption
|
|
53
|
+
* @param aad Optional additional authenticated data (must match what was used during encryption)
|
|
54
|
+
* @returns The decrypted data
|
|
55
|
+
* @throws Will throw an error if authentication fails or the data has been tampered with
|
|
56
|
+
*/
|
|
57
|
+
abstract decrypt(ciphertext: Uint8Array, key: Uint8Array, iv: Uint8Array, tag: Uint8Array, aad?: Uint8Array): Promise<Uint8Array>;
|
|
58
|
+
/**
|
|
59
|
+
* Generates cryptographically secure random bytes.
|
|
60
|
+
*
|
|
61
|
+
* @param length The number of random bytes to generate
|
|
62
|
+
* @returns A Uint8Array containing the random bytes
|
|
63
|
+
*/
|
|
64
|
+
abstract randomBytes(length: number): Uint8Array;
|
|
32
65
|
}
|
|
@@ -13,10 +13,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
const crypto_1 = __importDefault(require("crypto"));
|
|
16
|
-
const
|
|
17
|
-
const
|
|
16
|
+
const node_crypto_provider_1 = require("./node-crypto-provider");
|
|
17
|
+
const subtle_crypto_provider_1 = require("./subtle-crypto-provider");
|
|
18
18
|
const webhook_json_1 = __importDefault(require("../../webhooks/fixtures/webhook.json"));
|
|
19
|
-
const
|
|
19
|
+
const signature_provider_1 = require("./signature-provider");
|
|
20
20
|
describe('CryptoProvider', () => {
|
|
21
21
|
let payload;
|
|
22
22
|
let secret;
|
|
@@ -35,8 +35,8 @@ describe('CryptoProvider', () => {
|
|
|
35
35
|
});
|
|
36
36
|
describe('when computing HMAC signature', () => {
|
|
37
37
|
it('returns the same for the Node crypto and Web Crypto versions', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
-
const nodeCryptoProvider = new
|
|
39
|
-
const subtleCryptoProvider = new
|
|
38
|
+
const nodeCryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
39
|
+
const subtleCryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
40
40
|
const stringifiedPayload = JSON.stringify(payload);
|
|
41
41
|
const payloadHMAC = `${timestamp}.${stringifiedPayload}`;
|
|
42
42
|
const nodeCompare = yield nodeCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
|
|
@@ -46,9 +46,9 @@ describe('CryptoProvider', () => {
|
|
|
46
46
|
});
|
|
47
47
|
describe('when securely comparing', () => {
|
|
48
48
|
it('returns the same for the Node crypto and Web Crypto versions', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
-
const nodeCryptoProvider = new
|
|
50
|
-
const subtleCryptoProvider = new
|
|
51
|
-
const signatureProvider = new
|
|
49
|
+
const nodeCryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
50
|
+
const subtleCryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
51
|
+
const signatureProvider = new signature_provider_1.SignatureProvider(subtleCryptoProvider);
|
|
52
52
|
const signature = yield signatureProvider.computeSignature(timestamp, payload, secret);
|
|
53
53
|
expect(nodeCryptoProvider.secureCompare(signature, signatureHash)).toEqual(subtleCryptoProvider.secureCompare(signature, signatureHash));
|
|
54
54
|
expect(nodeCryptoProvider.secureCompare(signature, 'foo')).toEqual(subtleCryptoProvider.secureCompare(signature, 'foo'));
|
|
@@ -9,4 +9,11 @@ export declare class NodeCryptoProvider extends CryptoProvider {
|
|
|
9
9
|
computeHMACSignatureAsync(payload: string, secret: string): Promise<string>;
|
|
10
10
|
/** @override */
|
|
11
11
|
secureCompare(stringA: string, stringB: string): Promise<boolean>;
|
|
12
|
+
encrypt(plaintext: Uint8Array, key: Uint8Array, iv?: Uint8Array, aad?: Uint8Array): Promise<{
|
|
13
|
+
ciphertext: Uint8Array;
|
|
14
|
+
iv: Uint8Array;
|
|
15
|
+
tag: Uint8Array;
|
|
16
|
+
}>;
|
|
17
|
+
decrypt(ciphertext: Uint8Array, key: Uint8Array, iv: Uint8Array, tag: Uint8Array, aad?: Uint8Array): Promise<Uint8Array>;
|
|
18
|
+
randomBytes(length: number): Uint8Array;
|
|
12
19
|
}
|
|
@@ -33,7 +33,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
35
|
exports.NodeCryptoProvider = void 0;
|
|
36
|
-
const crypto = __importStar(require("
|
|
36
|
+
const crypto = __importStar(require("crypto"));
|
|
37
37
|
const crypto_provider_1 = require("./crypto-provider");
|
|
38
38
|
/**
|
|
39
39
|
* `CryptoProvider which uses the Node `crypto` package for its computations.
|
|
@@ -49,7 +49,7 @@ class NodeCryptoProvider extends crypto_provider_1.CryptoProvider {
|
|
|
49
49
|
/** @override */
|
|
50
50
|
computeHMACSignatureAsync(payload, secret) {
|
|
51
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
const signature =
|
|
52
|
+
const signature = this.computeHMACSignature(payload, secret);
|
|
53
53
|
return signature;
|
|
54
54
|
});
|
|
55
55
|
}
|
|
@@ -69,5 +69,41 @@ class NodeCryptoProvider extends crypto_provider_1.CryptoProvider {
|
|
|
69
69
|
return crypto.timingSafeEqual(hmacA, hmacB);
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
|
+
encrypt(plaintext, key, iv, aad) {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
const actualIv = iv || crypto.randomBytes(32);
|
|
75
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, actualIv);
|
|
76
|
+
if (aad) {
|
|
77
|
+
cipher.setAAD(Buffer.from(aad));
|
|
78
|
+
}
|
|
79
|
+
const ciphertext = Buffer.concat([
|
|
80
|
+
cipher.update(Buffer.from(plaintext)),
|
|
81
|
+
cipher.final(),
|
|
82
|
+
]);
|
|
83
|
+
const tag = cipher.getAuthTag();
|
|
84
|
+
return {
|
|
85
|
+
ciphertext: new Uint8Array(ciphertext),
|
|
86
|
+
iv: new Uint8Array(actualIv),
|
|
87
|
+
tag: new Uint8Array(tag),
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
decrypt(ciphertext, key, iv, tag, aad) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
94
|
+
decipher.setAuthTag(Buffer.from(tag));
|
|
95
|
+
if (aad) {
|
|
96
|
+
decipher.setAAD(Buffer.from(aad));
|
|
97
|
+
}
|
|
98
|
+
const decrypted = Buffer.concat([
|
|
99
|
+
decipher.update(Buffer.from(ciphertext)),
|
|
100
|
+
decipher.final(),
|
|
101
|
+
]);
|
|
102
|
+
return new Uint8Array(decrypted);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
randomBytes(length) {
|
|
106
|
+
return new Uint8Array(crypto.randomBytes(length));
|
|
107
|
+
}
|
|
72
108
|
}
|
|
73
109
|
exports.NodeCryptoProvider = NodeCryptoProvider;
|
|
@@ -13,15 +13,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
const crypto_1 = __importDefault(require("crypto"));
|
|
16
|
-
const
|
|
16
|
+
const subtle_crypto_provider_1 = require("./subtle-crypto-provider");
|
|
17
17
|
const webhook_json_1 = __importDefault(require("../../webhooks/fixtures/webhook.json"));
|
|
18
|
-
const
|
|
18
|
+
const signature_provider_1 = require("./signature-provider");
|
|
19
19
|
describe('SignatureProvider', () => {
|
|
20
20
|
let payload;
|
|
21
21
|
let secret;
|
|
22
22
|
let timestamp;
|
|
23
23
|
let signatureHash;
|
|
24
|
-
const signatureProvider = new
|
|
24
|
+
const signatureProvider = new signature_provider_1.SignatureProvider(new subtle_crypto_provider_1.SubtleCryptoProvider());
|
|
25
25
|
beforeEach(() => {
|
|
26
26
|
payload = webhook_json_1.default;
|
|
27
27
|
secret = 'secret';
|
|
@@ -60,7 +60,7 @@ describe('SignatureProvider', () => {
|
|
|
60
60
|
describe('when in an environment that supports SubtleCrypto', () => {
|
|
61
61
|
it('automatically uses the subtle crypto library', () => {
|
|
62
62
|
// tslint:disable-next-line
|
|
63
|
-
expect(signatureProvider['cryptoProvider']).toBeInstanceOf(
|
|
63
|
+
expect(signatureProvider['cryptoProvider']).toBeInstanceOf(subtle_crypto_provider_1.SubtleCryptoProvider);
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
});
|
|
@@ -12,4 +12,11 @@ export declare class SubtleCryptoProvider extends CryptoProvider {
|
|
|
12
12
|
computeHMACSignatureAsync(payload: string, secret: string): Promise<string>;
|
|
13
13
|
/** @override */
|
|
14
14
|
secureCompare(stringA: string, stringB: string): Promise<boolean>;
|
|
15
|
+
encrypt(plaintext: Uint8Array, key: Uint8Array, iv?: Uint8Array, aad?: Uint8Array): Promise<{
|
|
16
|
+
ciphertext: Uint8Array;
|
|
17
|
+
iv: Uint8Array;
|
|
18
|
+
tag: Uint8Array;
|
|
19
|
+
}>;
|
|
20
|
+
decrypt(ciphertext: Uint8Array, key: Uint8Array, iv: Uint8Array, tag: Uint8Array, aad?: Uint8Array): Promise<Uint8Array>;
|
|
21
|
+
randomBytes(length: number): Uint8Array;
|
|
15
22
|
}
|
|
@@ -65,6 +65,54 @@ class SubtleCryptoProvider extends crypto_provider_1.CryptoProvider {
|
|
|
65
65
|
return equal;
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
+
encrypt(plaintext, key, iv, aad) {
|
|
69
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
+
const actualIv = iv || crypto.getRandomValues(new Uint8Array(32));
|
|
71
|
+
const cryptoKey = yield this.subtleCrypto.importKey('raw', key, { name: 'AES-GCM' }, false, ['encrypt']);
|
|
72
|
+
const encryptParams = {
|
|
73
|
+
name: 'AES-GCM',
|
|
74
|
+
iv: actualIv,
|
|
75
|
+
};
|
|
76
|
+
if (aad) {
|
|
77
|
+
encryptParams.additionalData = aad;
|
|
78
|
+
}
|
|
79
|
+
const encryptedData = yield this.subtleCrypto.encrypt(encryptParams, cryptoKey, plaintext);
|
|
80
|
+
const encryptedBytes = new Uint8Array(encryptedData);
|
|
81
|
+
// Extract tag (last 16 bytes)
|
|
82
|
+
const tagSize = 16;
|
|
83
|
+
const tagStart = encryptedBytes.length - tagSize;
|
|
84
|
+
const tag = encryptedBytes.slice(tagStart);
|
|
85
|
+
const ciphertext = encryptedBytes.slice(0, tagStart);
|
|
86
|
+
return {
|
|
87
|
+
ciphertext,
|
|
88
|
+
iv: actualIv,
|
|
89
|
+
tag,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
decrypt(ciphertext, key, iv, tag, aad) {
|
|
94
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
// SubtleCrypto expects tag to be appended to ciphertext for AES-GCM
|
|
96
|
+
const combinedData = new Uint8Array(ciphertext.length + tag.length);
|
|
97
|
+
combinedData.set(ciphertext, 0);
|
|
98
|
+
combinedData.set(tag, ciphertext.length);
|
|
99
|
+
const cryptoKey = yield this.subtleCrypto.importKey('raw', key, { name: 'AES-GCM' }, false, ['decrypt']);
|
|
100
|
+
const decryptParams = {
|
|
101
|
+
name: 'AES-GCM',
|
|
102
|
+
iv,
|
|
103
|
+
};
|
|
104
|
+
if (aad) {
|
|
105
|
+
decryptParams.additionalData = aad;
|
|
106
|
+
}
|
|
107
|
+
const decryptedData = yield this.subtleCrypto.decrypt(decryptParams, cryptoKey, combinedData);
|
|
108
|
+
return new Uint8Array(decryptedData);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
randomBytes(length) {
|
|
112
|
+
const bytes = new Uint8Array(length);
|
|
113
|
+
crypto.getRandomValues(bytes);
|
|
114
|
+
return bytes;
|
|
115
|
+
}
|
|
68
116
|
}
|
|
69
117
|
exports.SubtleCryptoProvider = SubtleCryptoProvider;
|
|
70
118
|
// Cached mapping of byte to hex representation. We do this once to avoid re-
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-runtime compatible base64 encoding/decoding utilities
|
|
3
|
+
* that work in both Node.js and browser environments
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Converts a base64 string to a Uint8Array
|
|
7
|
+
*/
|
|
8
|
+
export declare function base64ToUint8Array(base64: string): Uint8Array;
|
|
9
|
+
/**
|
|
10
|
+
* Converts a Uint8Array to a base64 string
|
|
11
|
+
*/
|
|
12
|
+
export declare function uint8ArrayToBase64(bytes: Uint8Array): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-runtime compatible base64 encoding/decoding utilities
|
|
4
|
+
* that work in both Node.js and browser environments
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.uint8ArrayToBase64 = exports.base64ToUint8Array = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Converts a base64 string to a Uint8Array
|
|
10
|
+
*/
|
|
11
|
+
function base64ToUint8Array(base64) {
|
|
12
|
+
// In browsers and modern Node.js
|
|
13
|
+
if (typeof atob === 'function') {
|
|
14
|
+
const binary = atob(base64);
|
|
15
|
+
const bytes = new Uint8Array(binary.length);
|
|
16
|
+
for (let i = 0; i < binary.length; i++) {
|
|
17
|
+
bytes[i] = binary.charCodeAt(i);
|
|
18
|
+
}
|
|
19
|
+
return bytes;
|
|
20
|
+
}
|
|
21
|
+
// Node.js fallback using Buffer
|
|
22
|
+
else if (typeof Buffer !== 'undefined') {
|
|
23
|
+
return new Uint8Array(Buffer.from(base64, 'base64'));
|
|
24
|
+
}
|
|
25
|
+
// Fallback implementation if neither is available
|
|
26
|
+
else {
|
|
27
|
+
throw new Error('No base64 decoding implementation available');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.base64ToUint8Array = base64ToUint8Array;
|
|
31
|
+
/**
|
|
32
|
+
* Converts a Uint8Array to a base64 string
|
|
33
|
+
*/
|
|
34
|
+
function uint8ArrayToBase64(bytes) {
|
|
35
|
+
// In browsers and modern Node.js
|
|
36
|
+
if (typeof btoa === 'function') {
|
|
37
|
+
let binary = '';
|
|
38
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
39
|
+
binary += String.fromCharCode(bytes[i]);
|
|
40
|
+
}
|
|
41
|
+
return btoa(binary);
|
|
42
|
+
}
|
|
43
|
+
// Node.js fallback using Buffer
|
|
44
|
+
else if (typeof Buffer !== 'undefined') {
|
|
45
|
+
return Buffer.from(bytes).toString('base64');
|
|
46
|
+
}
|
|
47
|
+
// Fallback implementation if neither is available
|
|
48
|
+
else {
|
|
49
|
+
throw new Error('No base64 encoding implementation available');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.uint8ArrayToBase64 = uint8ArrayToBase64;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CryptoProvider } from './common/crypto/crypto-provider';
|
|
1
2
|
import { HttpClient } from './common/net/http-client';
|
|
2
3
|
import { Actions } from './actions/actions';
|
|
3
4
|
import { Webhooks } from './webhooks/webhooks';
|
|
@@ -25,6 +26,7 @@ declare class WorkOSNode extends WorkOS {
|
|
|
25
26
|
createHttpClient(options: WorkOSOptions, userAgent: string): HttpClient;
|
|
26
27
|
/** @override */
|
|
27
28
|
createWebhookClient(): Webhooks;
|
|
29
|
+
getCryptoProvider(): CryptoProvider;
|
|
28
30
|
/** @override */
|
|
29
31
|
createActionsClient(): Actions;
|
|
30
32
|
/** @override */
|
package/lib/index.js
CHANGED
|
@@ -54,6 +54,9 @@ class WorkOSNode extends workos_1.WorkOS {
|
|
|
54
54
|
}
|
|
55
55
|
/** @override */
|
|
56
56
|
createWebhookClient() {
|
|
57
|
+
return new webhooks_1.Webhooks(this.getCryptoProvider());
|
|
58
|
+
}
|
|
59
|
+
getCryptoProvider() {
|
|
57
60
|
let cryptoProvider;
|
|
58
61
|
if (typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined') {
|
|
59
62
|
cryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
@@ -61,18 +64,11 @@ class WorkOSNode extends workos_1.WorkOS {
|
|
|
61
64
|
else {
|
|
62
65
|
cryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
63
66
|
}
|
|
64
|
-
return
|
|
67
|
+
return cryptoProvider;
|
|
65
68
|
}
|
|
66
69
|
/** @override */
|
|
67
70
|
createActionsClient() {
|
|
68
|
-
|
|
69
|
-
if (typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined') {
|
|
70
|
-
cryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
cryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
74
|
-
}
|
|
75
|
-
return new actions_1.Actions(cryptoProvider);
|
|
71
|
+
return new actions_1.Actions(this.getCryptoProvider());
|
|
76
72
|
}
|
|
77
73
|
/** @override */
|
|
78
74
|
createIronSessionProvider() {
|
package/lib/index.worker.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Actions } from './actions/actions';
|
|
2
|
+
import { CryptoProvider } from './common/crypto/crypto-provider';
|
|
2
3
|
import { IronSessionProvider } from './common/iron-session/iron-session-provider';
|
|
3
4
|
import { HttpClient } from './common/net/http-client';
|
|
4
5
|
import { WorkOSOptions } from './index.worker';
|
|
@@ -25,6 +26,7 @@ declare class WorkOSWorker extends WorkOS {
|
|
|
25
26
|
createHttpClient(options: WorkOSOptions, userAgent: string): HttpClient;
|
|
26
27
|
/** @override */
|
|
27
28
|
createWebhookClient(): Webhooks;
|
|
29
|
+
getCryptoProvider(): CryptoProvider;
|
|
28
30
|
/** @override */
|
|
29
31
|
createActionsClient(): Actions;
|
|
30
32
|
/** @override */
|
package/lib/index.worker.js
CHANGED
|
@@ -48,6 +48,9 @@ class WorkOSWorker extends workos_1.WorkOS {
|
|
|
48
48
|
const cryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
49
49
|
return new webhooks_1.Webhooks(cryptoProvider);
|
|
50
50
|
}
|
|
51
|
+
getCryptoProvider() {
|
|
52
|
+
return new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
53
|
+
}
|
|
51
54
|
/** @override */
|
|
52
55
|
createActionsClient() {
|
|
53
56
|
const cryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
@@ -8,7 +8,7 @@ export interface UpdateUserOptions {
|
|
|
8
8
|
passwordHash?: string;
|
|
9
9
|
passwordHashType?: PasswordHashType;
|
|
10
10
|
externalId?: string;
|
|
11
|
-
metadata?: Record<string, string>;
|
|
11
|
+
metadata?: Record<string, string | null>;
|
|
12
12
|
}
|
|
13
13
|
export interface SerializedUpdateUserOptions {
|
|
14
14
|
first_name?: string;
|
|
@@ -18,5 +18,5 @@ export interface SerializedUpdateUserOptions {
|
|
|
18
18
|
password_hash?: string;
|
|
19
19
|
password_hash_type?: PasswordHashType;
|
|
20
20
|
external_id?: string;
|
|
21
|
-
metadata?: Record<string, string>;
|
|
21
|
+
metadata?: Record<string, string | null>;
|
|
22
22
|
}
|
|
@@ -1112,6 +1112,16 @@ describe('UserManagement', () => {
|
|
|
1112
1112
|
metadata: { key: 'value' },
|
|
1113
1113
|
});
|
|
1114
1114
|
}));
|
|
1115
|
+
it('removes metadata from the request', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1116
|
+
(0, test_utils_1.fetchOnce)(user_json_1.default);
|
|
1117
|
+
yield workos.userManagement.updateUser({
|
|
1118
|
+
userId,
|
|
1119
|
+
metadata: { key: null },
|
|
1120
|
+
});
|
|
1121
|
+
expect((0, test_utils_1.fetchBody)()).toMatchObject({
|
|
1122
|
+
metadata: {},
|
|
1123
|
+
});
|
|
1124
|
+
}));
|
|
1115
1125
|
});
|
|
1116
1126
|
describe('enrollAuthFactor', () => {
|
|
1117
1127
|
it('sends an enrollAuthFactor request', () => __awaiter(void 0, void 0, void 0, function* () {
|
package/lib/vault/vault.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { PaginationOptions } from '../index.worker';
|
|
2
|
-
import { WorkOS } from '../workos';
|
|
3
|
-
import { CreateDataKeyOptions, CreateObjectOptions, DataKey, DataKeyPair, DecryptDataKeyOptions, DeleteObjectOptions, ReadObjectOptions, KeyContext, ObjectDigest, ObjectMetadata, ObjectVersion, UpdateObjectOptions, VaultObject } from './interfaces';
|
|
4
1
|
import { List } from '../common/interfaces';
|
|
2
|
+
import { PaginationOptions } from '../index.worker';
|
|
3
|
+
import type { WorkOS } from '../workos';
|
|
4
|
+
import { CreateDataKeyOptions, CreateObjectOptions, DataKey, DataKeyPair, DecryptDataKeyOptions, DeleteObjectOptions, KeyContext, ObjectDigest, ObjectMetadata, ObjectVersion, ReadObjectOptions, UpdateObjectOptions, VaultObject } from './interfaces';
|
|
5
5
|
export declare class Vault {
|
|
6
6
|
private readonly workos;
|
|
7
|
+
private cryptoProvider;
|
|
7
8
|
constructor(workos: WorkOS);
|
|
9
|
+
private decode;
|
|
8
10
|
createObject(options: CreateObjectOptions): Promise<ObjectMetadata>;
|
|
9
11
|
listObjects(options?: PaginationOptions | undefined): Promise<List<ObjectDigest>>;
|
|
10
12
|
listObjectVersions(options: ReadObjectOptions): Promise<ObjectVersion[]>;
|
package/lib/vault/vault.js
CHANGED
|
@@ -10,8 +10,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Vault = void 0;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const leb_1 = require("leb");
|
|
14
|
+
const base64_1 = require("../common/utils/base64");
|
|
15
15
|
const vault_key_serializer_1 = require("./serializers/vault-key.serializer");
|
|
16
16
|
const vault_object_serializer_1 = require("./serializers/vault-object.serializer");
|
|
17
17
|
class Vault {
|
|
@@ -45,6 +45,24 @@ class Vault {
|
|
|
45
45
|
* @deprecated Use `deleteObject` instead.
|
|
46
46
|
*/
|
|
47
47
|
this.deleteSecret = this.deleteObject;
|
|
48
|
+
this.cryptoProvider = workos.getCryptoProvider();
|
|
49
|
+
}
|
|
50
|
+
decode(payload) {
|
|
51
|
+
const inputData = (0, base64_1.base64ToUint8Array)(payload);
|
|
52
|
+
// Use 12 bytes for IV (standard for AES-GCM)
|
|
53
|
+
const iv = new Uint8Array(inputData.subarray(0, 12));
|
|
54
|
+
const tag = new Uint8Array(inputData.subarray(12, 28));
|
|
55
|
+
const { value: keyLen, nextIndex } = (0, leb_1.decodeUInt32)(inputData, 28);
|
|
56
|
+
// Use subarray instead of slice and convert directly to base64
|
|
57
|
+
const keysBuffer = inputData.subarray(nextIndex, nextIndex + keyLen);
|
|
58
|
+
const keys = (0, base64_1.uint8ArrayToBase64)(keysBuffer);
|
|
59
|
+
const ciphertext = new Uint8Array(inputData.subarray(nextIndex + keyLen));
|
|
60
|
+
return {
|
|
61
|
+
iv,
|
|
62
|
+
tag,
|
|
63
|
+
keys,
|
|
64
|
+
ciphertext,
|
|
65
|
+
};
|
|
48
66
|
}
|
|
49
67
|
createObject(options) {
|
|
50
68
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -108,17 +126,53 @@ class Vault {
|
|
|
108
126
|
}
|
|
109
127
|
encrypt(data, context, associatedData) {
|
|
110
128
|
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
-
const
|
|
129
|
+
const keyPair = yield this.createDataKey({
|
|
112
130
|
context,
|
|
113
131
|
});
|
|
114
|
-
|
|
132
|
+
// Convert base64 key to Uint8Array
|
|
133
|
+
const encoder = new TextEncoder();
|
|
134
|
+
// Use our cross-runtime base64 utility
|
|
135
|
+
const key = (0, base64_1.base64ToUint8Array)(keyPair.dataKey.key);
|
|
136
|
+
const keyBlob = (0, base64_1.base64ToUint8Array)(keyPair.encryptedKeys);
|
|
137
|
+
const prefixLenBuffer = (0, leb_1.encodeUInt32)(keyBlob.length);
|
|
138
|
+
const aadBuffer = associatedData
|
|
139
|
+
? encoder.encode(associatedData)
|
|
140
|
+
: undefined;
|
|
141
|
+
// Use a 12-byte IV for AES-GCM (industry standard)
|
|
142
|
+
const iv = this.cryptoProvider.randomBytes(12);
|
|
143
|
+
const { ciphertext, iv: resultIv, tag, } = yield this.cryptoProvider.encrypt(encoder.encode(data), key, iv, aadBuffer);
|
|
144
|
+
// Concatenate all parts into a single array
|
|
145
|
+
const resultArray = new Uint8Array(resultIv.length +
|
|
146
|
+
tag.length +
|
|
147
|
+
prefixLenBuffer.length +
|
|
148
|
+
keyBlob.length +
|
|
149
|
+
ciphertext.length);
|
|
150
|
+
let offset = 0;
|
|
151
|
+
resultArray.set(resultIv, offset);
|
|
152
|
+
offset += resultIv.length;
|
|
153
|
+
resultArray.set(tag, offset);
|
|
154
|
+
offset += tag.length;
|
|
155
|
+
resultArray.set(new Uint8Array(prefixLenBuffer), offset);
|
|
156
|
+
offset += prefixLenBuffer.length;
|
|
157
|
+
resultArray.set(keyBlob, offset);
|
|
158
|
+
offset += keyBlob.length;
|
|
159
|
+
resultArray.set(ciphertext, offset);
|
|
160
|
+
// Convert to base64 using our cross-runtime utility
|
|
161
|
+
return (0, base64_1.uint8ArrayToBase64)(resultArray);
|
|
115
162
|
});
|
|
116
163
|
}
|
|
117
164
|
decrypt(encryptedData, associatedData) {
|
|
118
165
|
return __awaiter(this, void 0, void 0, function* () {
|
|
119
|
-
const decoded =
|
|
166
|
+
const decoded = this.decode(encryptedData);
|
|
120
167
|
const dataKey = yield this.decryptDataKey({ keys: decoded.keys });
|
|
121
|
-
|
|
168
|
+
// Convert base64 key to Uint8Array using our cross-runtime utility
|
|
169
|
+
const key = (0, base64_1.base64ToUint8Array)(dataKey.key);
|
|
170
|
+
const encoder = new TextEncoder();
|
|
171
|
+
const aadBuffer = associatedData
|
|
172
|
+
? encoder.encode(associatedData)
|
|
173
|
+
: undefined;
|
|
174
|
+
const decrypted = yield this.cryptoProvider.decrypt(decoded.ciphertext, key, decoded.iv, decoded.tag, aadBuffer);
|
|
175
|
+
return new TextDecoder().decode(decrypted);
|
|
122
176
|
});
|
|
123
177
|
}
|
|
124
178
|
}
|
package/lib/vault/vault.spec.js
CHANGED
|
@@ -244,4 +244,43 @@ describe('Vault', () => {
|
|
|
244
244
|
expect((0, test_utils_1.fetchMethod)()).toBe('PUT');
|
|
245
245
|
}));
|
|
246
246
|
});
|
|
247
|
+
describe('encrypt and decrypt', () => {
|
|
248
|
+
it('correctly encrypts and decrypts data', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
249
|
+
// Generate a valid 32-byte (256-bit) key for AES-256-GCM
|
|
250
|
+
const validKey = Buffer.alloc(32).fill('A').toString('base64');
|
|
251
|
+
// Mock createDataKey to return a valid key for testing
|
|
252
|
+
(0, test_utils_1.fetchOnce)({
|
|
253
|
+
data_key: validKey,
|
|
254
|
+
encrypted_keys: 'ZW5jcnlwdGVkX2tleXM=',
|
|
255
|
+
id: 'key123',
|
|
256
|
+
context: { type: 'test' },
|
|
257
|
+
});
|
|
258
|
+
// Mock decryptDataKey to return same key
|
|
259
|
+
(0, test_utils_1.fetchOnce)({
|
|
260
|
+
data_key: validKey,
|
|
261
|
+
id: 'key123',
|
|
262
|
+
});
|
|
263
|
+
const originalText = 'This is a secret message';
|
|
264
|
+
const context = { type: 'test' };
|
|
265
|
+
const associatedData = 'additional-auth-data';
|
|
266
|
+
// Encrypt the data
|
|
267
|
+
const encrypted = yield workos.vault.encrypt(originalText, context, associatedData);
|
|
268
|
+
// Verify encrypt API call
|
|
269
|
+
expect((0, test_utils_1.fetchURL)()).toContain('/vault/v1/keys/data-key');
|
|
270
|
+
expect((0, test_utils_1.fetchMethod)()).toBe('POST');
|
|
271
|
+
// Reset fetch for the decrypt call
|
|
272
|
+
jest_fetch_mock_1.default.resetMocks();
|
|
273
|
+
(0, test_utils_1.fetchOnce)({
|
|
274
|
+
data_key: validKey,
|
|
275
|
+
id: 'key123',
|
|
276
|
+
});
|
|
277
|
+
// Decrypt the data
|
|
278
|
+
const decrypted = yield workos.vault.decrypt(encrypted, associatedData);
|
|
279
|
+
// Verify decrypt API call
|
|
280
|
+
expect((0, test_utils_1.fetchURL)()).toContain('/vault/v1/keys/decrypt');
|
|
281
|
+
expect((0, test_utils_1.fetchMethod)()).toBe('POST');
|
|
282
|
+
// Verify the decrypted text matches the original
|
|
283
|
+
expect(decrypted).toBe(originalText);
|
|
284
|
+
}));
|
|
285
|
+
});
|
|
247
286
|
});
|
package/lib/webhooks/webhooks.js
CHANGED
|
@@ -11,10 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Webhooks = void 0;
|
|
13
13
|
const serializers_1 = require("../common/serializers");
|
|
14
|
-
const
|
|
14
|
+
const signature_provider_1 = require("../common/crypto/signature-provider");
|
|
15
15
|
class Webhooks {
|
|
16
16
|
constructor(cryptoProvider) {
|
|
17
|
-
this.signatureProvider = new
|
|
17
|
+
this.signatureProvider = new signature_provider_1.SignatureProvider(cryptoProvider);
|
|
18
18
|
}
|
|
19
19
|
get verifyHeader() {
|
|
20
20
|
return this.signatureProvider.verifyHeader.bind(this.signatureProvider);
|
package/lib/workos.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { IronSessionProvider } from './common/iron-session/iron-session-provider
|
|
|
16
16
|
import { Widgets } from './widgets/widgets';
|
|
17
17
|
import { Actions } from './actions/actions';
|
|
18
18
|
import { Vault } from './vault/vault';
|
|
19
|
+
import { CryptoProvider } from './common/crypto/crypto-provider';
|
|
19
20
|
export declare class WorkOS {
|
|
20
21
|
readonly key?: string | undefined;
|
|
21
22
|
readonly options: WorkOSOptions;
|
|
@@ -40,6 +41,7 @@ export declare class WorkOS {
|
|
|
40
41
|
constructor(key?: string | undefined, options?: WorkOSOptions);
|
|
41
42
|
createWebhookClient(): Webhooks;
|
|
42
43
|
createActionsClient(): Actions;
|
|
44
|
+
getCryptoProvider(): CryptoProvider;
|
|
43
45
|
createHttpClient(options: WorkOSOptions, userAgent: string): HttpClient;
|
|
44
46
|
createIronSessionProvider(): IronSessionProvider;
|
|
45
47
|
get version(): string;
|
package/lib/workos.js
CHANGED
|
@@ -31,7 +31,7 @@ const widgets_1 = require("./widgets/widgets");
|
|
|
31
31
|
const actions_1 = require("./actions/actions");
|
|
32
32
|
const vault_1 = require("./vault/vault");
|
|
33
33
|
const conflict_exception_1 = require("./common/exceptions/conflict.exception");
|
|
34
|
-
const VERSION = '7.50.
|
|
34
|
+
const VERSION = '7.50.1';
|
|
35
35
|
const DEFAULT_HOSTNAME = 'api.workos.com';
|
|
36
36
|
const HEADER_AUTHORIZATION = 'Authorization';
|
|
37
37
|
const HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
|
|
@@ -88,10 +88,13 @@ class WorkOS {
|
|
|
88
88
|
this.client = this.createHttpClient(options, userAgent);
|
|
89
89
|
}
|
|
90
90
|
createWebhookClient() {
|
|
91
|
-
return new webhooks_1.Webhooks(
|
|
91
|
+
return new webhooks_1.Webhooks(this.getCryptoProvider());
|
|
92
92
|
}
|
|
93
93
|
createActionsClient() {
|
|
94
|
-
return new actions_1.Actions(
|
|
94
|
+
return new actions_1.Actions(this.getCryptoProvider());
|
|
95
|
+
}
|
|
96
|
+
getCryptoProvider() {
|
|
97
|
+
return new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
95
98
|
}
|
|
96
99
|
createHttpClient(options, userAgent) {
|
|
97
100
|
var _a;
|
package/package.json
CHANGED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interface encapsulating the various crypto computations used by the library,
|
|
3
|
-
* allowing pluggable underlying crypto implementations.
|
|
4
|
-
*/
|
|
5
|
-
export declare abstract class CryptoProvider {
|
|
6
|
-
encoder: TextEncoder;
|
|
7
|
-
/**
|
|
8
|
-
* Computes a SHA-256 HMAC given a secret and a payload (encoded in UTF-8).
|
|
9
|
-
* The output HMAC should be encoded in hexadecimal.
|
|
10
|
-
*
|
|
11
|
-
* Sample values for implementations:
|
|
12
|
-
* - computeHMACSignature('', 'test_secret') => 'f7f9bd47fb987337b5796fdc1fdb9ba221d0d5396814bfcaf9521f43fd8927fd'
|
|
13
|
-
* - computeHMACSignature('\ud83d\ude00', 'test_secret') => '837da296d05c4fe31f61d5d7ead035099d9585a5bcde87de952012a78f0b0c43
|
|
14
|
-
*/
|
|
15
|
-
abstract computeHMACSignature(payload: string, secret: string): string;
|
|
16
|
-
/**
|
|
17
|
-
* Asynchronous version of `computeHMACSignature`. Some implementations may
|
|
18
|
-
* only allow support async signature computation.
|
|
19
|
-
*
|
|
20
|
-
* Computes a SHA-256 HMAC given a secret and a payload (encoded in UTF-8).
|
|
21
|
-
* The output HMAC should be encoded in hexadecimal.
|
|
22
|
-
*
|
|
23
|
-
* Sample values for implementations:
|
|
24
|
-
* - computeHMACSignature('', 'test_secret') => 'f7f9bd47fb987337b5796fdc1fdb9ba221d0d5396814bfcaf9521f43fd8927fd'
|
|
25
|
-
* - computeHMACSignature('\ud83d\ude00', 'test_secret') => '837da296d05c4fe31f61d5d7ead035099d9585a5bcde87de952012a78f0b0c43
|
|
26
|
-
*/
|
|
27
|
-
abstract computeHMACSignatureAsync(payload: string, secret: string): Promise<string>;
|
|
28
|
-
/**
|
|
29
|
-
* Cryptographically determine whether two signatures are equal
|
|
30
|
-
*/
|
|
31
|
-
abstract secureCompare(stringA: string, stringB: string): Promise<boolean>;
|
|
32
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CryptoProvider = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Interface encapsulating the various crypto computations used by the library,
|
|
6
|
-
* allowing pluggable underlying crypto implementations.
|
|
7
|
-
*/
|
|
8
|
-
class CryptoProvider {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.encoder = new TextEncoder();
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
exports.CryptoProvider = CryptoProvider;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { CryptoProvider } from './CryptoProvider';
|
|
2
|
-
/**
|
|
3
|
-
* `CryptoProvider which uses the Node `crypto` package for its computations.
|
|
4
|
-
*/
|
|
5
|
-
export declare class NodeCryptoProvider extends CryptoProvider {
|
|
6
|
-
/** @override */
|
|
7
|
-
computeHMACSignature(payload: string, secret: string): string;
|
|
8
|
-
/** @override */
|
|
9
|
-
computeHMACSignatureAsync(payload: string, secret: string): Promise<string>;
|
|
10
|
-
/** @override */
|
|
11
|
-
secureCompare(stringA: string, stringB: string): Promise<boolean>;
|
|
12
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.NodeCryptoProvider = void 0;
|
|
36
|
-
const crypto = __importStar(require("crypto"));
|
|
37
|
-
const CryptoProvider_1 = require("./CryptoProvider");
|
|
38
|
-
/**
|
|
39
|
-
* `CryptoProvider which uses the Node `crypto` package for its computations.
|
|
40
|
-
*/
|
|
41
|
-
class NodeCryptoProvider extends CryptoProvider_1.CryptoProvider {
|
|
42
|
-
/** @override */
|
|
43
|
-
computeHMACSignature(payload, secret) {
|
|
44
|
-
return crypto
|
|
45
|
-
.createHmac('sha256', secret)
|
|
46
|
-
.update(payload, 'utf8')
|
|
47
|
-
.digest('hex');
|
|
48
|
-
}
|
|
49
|
-
/** @override */
|
|
50
|
-
computeHMACSignatureAsync(payload, secret) {
|
|
51
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
const signature = yield this.computeHMACSignature(payload, secret);
|
|
53
|
-
return signature;
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
/** @override */
|
|
57
|
-
secureCompare(stringA, stringB) {
|
|
58
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
-
const bufferA = this.encoder.encode(stringA);
|
|
60
|
-
const bufferB = this.encoder.encode(stringB);
|
|
61
|
-
if (bufferA.length !== bufferB.length) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
// Generate a random key for HMAC
|
|
65
|
-
const key = crypto.randomBytes(32); // Generates a 256-bit key
|
|
66
|
-
const hmacA = crypto.createHmac('sha256', key).update(bufferA).digest();
|
|
67
|
-
const hmacB = crypto.createHmac('sha256', key).update(bufferB).digest();
|
|
68
|
-
// Perform a constant time comparison
|
|
69
|
-
return crypto.timingSafeEqual(hmacA, hmacB);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
exports.NodeCryptoProvider = NodeCryptoProvider;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { CryptoProvider } from './CryptoProvider';
|
|
2
|
-
/**
|
|
3
|
-
* `CryptoProvider which uses the SubtleCrypto interface of the Web Crypto API.
|
|
4
|
-
*
|
|
5
|
-
* This only supports asynchronous operations.
|
|
6
|
-
*/
|
|
7
|
-
export declare class SubtleCryptoProvider extends CryptoProvider {
|
|
8
|
-
subtleCrypto: SubtleCrypto;
|
|
9
|
-
constructor(subtleCrypto?: SubtleCrypto);
|
|
10
|
-
computeHMACSignature(_payload: string, _secret: string): string;
|
|
11
|
-
/** @override */
|
|
12
|
-
computeHMACSignatureAsync(payload: string, secret: string): Promise<string>;
|
|
13
|
-
/** @override */
|
|
14
|
-
secureCompare(stringA: string, stringB: string): Promise<boolean>;
|
|
15
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.SubtleCryptoProvider = void 0;
|
|
13
|
-
const CryptoProvider_1 = require("./CryptoProvider");
|
|
14
|
-
/**
|
|
15
|
-
* `CryptoProvider which uses the SubtleCrypto interface of the Web Crypto API.
|
|
16
|
-
*
|
|
17
|
-
* This only supports asynchronous operations.
|
|
18
|
-
*/
|
|
19
|
-
class SubtleCryptoProvider extends CryptoProvider_1.CryptoProvider {
|
|
20
|
-
constructor(subtleCrypto) {
|
|
21
|
-
super();
|
|
22
|
-
// If no subtle crypto is interface, default to the global namespace. This
|
|
23
|
-
// is to allow custom interfaces (eg. using the Node webcrypto interface in
|
|
24
|
-
// tests).
|
|
25
|
-
this.subtleCrypto = subtleCrypto || crypto.subtle;
|
|
26
|
-
}
|
|
27
|
-
computeHMACSignature(_payload, _secret) {
|
|
28
|
-
throw new Error('SubleCryptoProvider cannot be used in a synchronous context.');
|
|
29
|
-
}
|
|
30
|
-
/** @override */
|
|
31
|
-
computeHMACSignatureAsync(payload, secret) {
|
|
32
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
-
const encoder = new TextEncoder();
|
|
34
|
-
const key = yield this.subtleCrypto.importKey('raw', encoder.encode(secret), {
|
|
35
|
-
name: 'HMAC',
|
|
36
|
-
hash: { name: 'SHA-256' },
|
|
37
|
-
}, false, ['sign']);
|
|
38
|
-
const signatureBuffer = yield this.subtleCrypto.sign('hmac', key, encoder.encode(payload));
|
|
39
|
-
// crypto.subtle returns the signature in base64 format. This must be
|
|
40
|
-
// encoded in hex to match the CryptoProvider contract. We map each byte in
|
|
41
|
-
// the buffer to its corresponding hex octet and then combine into a string.
|
|
42
|
-
const signatureBytes = new Uint8Array(signatureBuffer);
|
|
43
|
-
const signatureHexCodes = new Array(signatureBytes.length);
|
|
44
|
-
for (let i = 0; i < signatureBytes.length; i++) {
|
|
45
|
-
signatureHexCodes[i] = byteHexMapping[signatureBytes[i]];
|
|
46
|
-
}
|
|
47
|
-
return signatureHexCodes.join('');
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
/** @override */
|
|
51
|
-
secureCompare(stringA, stringB) {
|
|
52
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
-
const bufferA = this.encoder.encode(stringA);
|
|
54
|
-
const bufferB = this.encoder.encode(stringB);
|
|
55
|
-
if (bufferA.length !== bufferB.length) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
const algorithm = { name: 'HMAC', hash: 'SHA-256' };
|
|
59
|
-
const key = (yield crypto.subtle.generateKey(algorithm, false, [
|
|
60
|
-
'sign',
|
|
61
|
-
'verify',
|
|
62
|
-
]));
|
|
63
|
-
const hmac = yield crypto.subtle.sign(algorithm, key, bufferA);
|
|
64
|
-
const equal = yield crypto.subtle.verify(algorithm, key, hmac, bufferB);
|
|
65
|
-
return equal;
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
exports.SubtleCryptoProvider = SubtleCryptoProvider;
|
|
70
|
-
// Cached mapping of byte to hex representation. We do this once to avoid re-
|
|
71
|
-
// computing every time we need to convert the result of a signature to hex.
|
|
72
|
-
const byteHexMapping = new Array(256);
|
|
73
|
-
for (let i = 0; i < byteHexMapping.length; i++) {
|
|
74
|
-
byteHexMapping[i] = i.toString(16).padStart(2, '0');
|
|
75
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
export interface Decoded {
|
|
3
|
-
iv: Buffer;
|
|
4
|
-
tag: Buffer;
|
|
5
|
-
keys: string;
|
|
6
|
-
ciphertext: Buffer;
|
|
7
|
-
}
|
|
8
|
-
export declare const decrypt: (payload: string | Decoded, dataKey: string, aad: string) => string;
|
|
9
|
-
export declare const decode: (payload: string) => Decoded;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.decode = exports.decrypt = void 0;
|
|
7
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
-
const leb_1 = require("leb");
|
|
9
|
-
const decrypt = (payload, dataKey, aad) => {
|
|
10
|
-
if (typeof payload === 'string') {
|
|
11
|
-
payload = (0, exports.decode)(payload);
|
|
12
|
-
}
|
|
13
|
-
const { iv, tag, ciphertext } = payload;
|
|
14
|
-
const key = Buffer.from(dataKey, 'base64');
|
|
15
|
-
const decipher = crypto_1.default
|
|
16
|
-
.createDecipheriv('aes-256-gcm', key, iv)
|
|
17
|
-
.setAAD(Buffer.from(aad))
|
|
18
|
-
.setAuthTag(tag);
|
|
19
|
-
const decrypted = decipher.update(ciphertext, undefined, 'utf-8') + decipher.final('utf-8');
|
|
20
|
-
return decrypted;
|
|
21
|
-
};
|
|
22
|
-
exports.decrypt = decrypt;
|
|
23
|
-
const decode = (payload) => {
|
|
24
|
-
const inputData = Buffer.from(payload, 'base64');
|
|
25
|
-
const iv = inputData.slice(0, 32);
|
|
26
|
-
const tag = inputData.slice(32, 48);
|
|
27
|
-
const { value: keyLen, nextIndex } = (0, leb_1.decodeUInt32)(inputData, 48);
|
|
28
|
-
const keys = inputData
|
|
29
|
-
.slice(nextIndex, nextIndex + keyLen)
|
|
30
|
-
.toString('base64');
|
|
31
|
-
const ciphertext = inputData.slice(nextIndex + keyLen);
|
|
32
|
-
return {
|
|
33
|
-
iv,
|
|
34
|
-
tag,
|
|
35
|
-
keys,
|
|
36
|
-
ciphertext,
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
exports.decode = decode;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const encrypt: (data: string, dataKey: string, encryptedKeys: string, aad: string) => string;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.encrypt = void 0;
|
|
7
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
-
const leb_1 = require("leb");
|
|
9
|
-
const encrypt = (data, dataKey, encryptedKeys, aad) => {
|
|
10
|
-
// encrypt using the returned data key
|
|
11
|
-
const key = Buffer.from(dataKey, 'base64');
|
|
12
|
-
const keyBlob = Buffer.from(encryptedKeys, 'base64');
|
|
13
|
-
const prefixLen = (0, leb_1.encodeUInt32)(keyBlob.length);
|
|
14
|
-
const iv = crypto_1.default.randomBytes(32);
|
|
15
|
-
const cipher = crypto_1.default
|
|
16
|
-
.createCipheriv('aes-256-gcm', key, iv)
|
|
17
|
-
.setAAD(Buffer.from(aad));
|
|
18
|
-
const ciphertext = Buffer.concat([
|
|
19
|
-
cipher.update(data, 'utf8'),
|
|
20
|
-
cipher.final(),
|
|
21
|
-
]);
|
|
22
|
-
const tag = cipher.getAuthTag();
|
|
23
|
-
// store the encrypted keys with the ciphertext
|
|
24
|
-
const payload = Buffer.concat([
|
|
25
|
-
iv,
|
|
26
|
-
tag,
|
|
27
|
-
prefixLen,
|
|
28
|
-
keyBlob,
|
|
29
|
-
ciphertext,
|
|
30
|
-
]).toString('base64');
|
|
31
|
-
return payload;
|
|
32
|
-
};
|
|
33
|
-
exports.encrypt = encrypt;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|