@workos-inc/node 7.50.0 → 7.51.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.
- 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/common/utils/pagination.d.ts +1 -1
- package/lib/fga/fga.d.ts +2 -1
- package/lib/fga/fga.js +3 -1
- package/lib/fga/fga.spec.js +144 -0
- package/lib/fga/interfaces/check.interface.d.ts +4 -0
- package/lib/fga/interfaces/check.interface.js +1 -0
- package/lib/fga/interfaces/list.interface.d.ts +8 -0
- package/lib/fga/interfaces/list.interface.js +2 -0
- package/lib/fga/interfaces/warning.interface.d.ts +9 -0
- package/lib/fga/interfaces/warning.interface.js +2 -0
- package/lib/fga/serializers/index.d.ts +1 -0
- package/lib/fga/serializers/index.js +1 -0
- package/lib/fga/serializers/list.serializer.d.ts +5 -0
- package/lib/fga/serializers/list.serializer.js +10 -0
- package/lib/fga/serializers/query-result.serializer.d.ts +5 -0
- package/lib/fga/utils/fetch-and-deserialize-list.d.ts +5 -0
- package/lib/fga/utils/fetch-and-deserialize-list.js +18 -0
- package/lib/fga/utils/fga-paginatable.d.ts +9 -0
- package/lib/fga/utils/fga-paginatable.js +13 -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 +4 -2
- package/lib/user-management/serializers/update-user-options.serializer.js +2 -1
- 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/fga/fga.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { WorkOS } from '../workos';
|
|
2
2
|
import { Resource, CheckBatchOptions, CheckOptions, CheckRequestOptions, CheckResult, CreateResourceOptions, DeleteResourceOptions, ListResourcesOptions, ListWarrantsRequestOptions, ListWarrantsOptions, QueryOptions, QueryRequestOptions, QueryResult, ResourceInterface, ResourceOptions, UpdateResourceOptions, WriteWarrantOptions, Warrant, WarrantToken, BatchWriteResourcesOptions } from './interfaces';
|
|
3
3
|
import { AutoPaginatable } from '../common/utils/pagination';
|
|
4
|
+
import { FgaPaginatable } from './utils/fga-paginatable';
|
|
4
5
|
export declare class FGA {
|
|
5
6
|
private readonly workos;
|
|
6
7
|
constructor(workos: WorkOS);
|
|
@@ -15,5 +16,5 @@ export declare class FGA {
|
|
|
15
16
|
writeWarrant(options: WriteWarrantOptions): Promise<WarrantToken>;
|
|
16
17
|
batchWriteWarrants(options: WriteWarrantOptions[]): Promise<WarrantToken>;
|
|
17
18
|
listWarrants(options?: ListWarrantsOptions, requestOptions?: ListWarrantsRequestOptions): Promise<AutoPaginatable<Warrant>>;
|
|
18
|
-
query(options: QueryOptions, requestOptions?: QueryRequestOptions): Promise<
|
|
19
|
+
query(options: QueryOptions, requestOptions?: QueryRequestOptions): Promise<FgaPaginatable<QueryResult>>;
|
|
19
20
|
}
|
package/lib/fga/fga.js
CHANGED
|
@@ -15,6 +15,8 @@ const serializers_1 = require("./serializers");
|
|
|
15
15
|
const interface_check_1 = require("./utils/interface-check");
|
|
16
16
|
const pagination_1 = require("../common/utils/pagination");
|
|
17
17
|
const fetch_and_deserialize_1 = require("../common/utils/fetch-and-deserialize");
|
|
18
|
+
const fga_paginatable_1 = require("./utils/fga-paginatable");
|
|
19
|
+
const fetch_and_deserialize_list_1 = require("./utils/fetch-and-deserialize-list");
|
|
18
20
|
class FGA {
|
|
19
21
|
constructor(workos) {
|
|
20
22
|
this.workos = workos;
|
|
@@ -104,7 +106,7 @@ class FGA {
|
|
|
104
106
|
}
|
|
105
107
|
query(options, requestOptions = {}) {
|
|
106
108
|
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
-
return new
|
|
109
|
+
return new fga_paginatable_1.FgaPaginatable(yield (0, fetch_and_deserialize_list_1.fetchAndDeserializeFGAList)(this.workos, '/fga/v1/query', serializers_1.deserializeQueryResult, (0, serializers_1.serializeQueryOptions)(options), requestOptions), (params) => (0, fetch_and_deserialize_list_1.fetchAndDeserializeFGAList)(this.workos, '/fga/v1/query', serializers_1.deserializeQueryResult, params, requestOptions), (0, serializers_1.serializeQueryOptions)(options));
|
|
108
110
|
});
|
|
109
111
|
}
|
|
110
112
|
}
|
package/lib/fga/fga.spec.js
CHANGED
|
@@ -48,6 +48,56 @@ describe('FGA', () => {
|
|
|
48
48
|
warrantToken: 'abc',
|
|
49
49
|
});
|
|
50
50
|
}));
|
|
51
|
+
it('deserializes warnings in check response', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
|
+
(0, test_utils_1.fetchOnce)({
|
|
53
|
+
result: 'authorized',
|
|
54
|
+
is_implicit: false,
|
|
55
|
+
warrant_token: 'abc',
|
|
56
|
+
warnings: [
|
|
57
|
+
{
|
|
58
|
+
code: 'missing_context_keys',
|
|
59
|
+
message: 'Missing required context keys',
|
|
60
|
+
keys: ['tenant_id', 'region'],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: 'some_other_warning',
|
|
64
|
+
message: 'Some other warning message',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
const checkResult = yield workos.fga.check({
|
|
69
|
+
checks: [
|
|
70
|
+
{
|
|
71
|
+
resource: {
|
|
72
|
+
resourceType: 'role',
|
|
73
|
+
resourceId: 'admin',
|
|
74
|
+
},
|
|
75
|
+
relation: 'member',
|
|
76
|
+
subject: {
|
|
77
|
+
resourceType: 'user',
|
|
78
|
+
resourceId: 'user_123',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/check');
|
|
84
|
+
expect(checkResult).toMatchObject({
|
|
85
|
+
result: 'authorized',
|
|
86
|
+
isImplicit: false,
|
|
87
|
+
warrantToken: 'abc',
|
|
88
|
+
warnings: [
|
|
89
|
+
{
|
|
90
|
+
code: 'missing_context_keys',
|
|
91
|
+
message: 'Missing required context keys',
|
|
92
|
+
keys: ['tenant_id', 'region'],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
code: 'some_other_warning',
|
|
96
|
+
message: 'Some other warning message',
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
}));
|
|
51
101
|
});
|
|
52
102
|
describe('createResource', () => {
|
|
53
103
|
it('creates resource', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -606,6 +656,100 @@ describe('FGA', () => {
|
|
|
606
656
|
},
|
|
607
657
|
]);
|
|
608
658
|
}));
|
|
659
|
+
it('deserializes warnings in query response', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
660
|
+
(0, test_utils_1.fetchOnce)({
|
|
661
|
+
data: [
|
|
662
|
+
{
|
|
663
|
+
resource_type: 'role',
|
|
664
|
+
resource_id: 'admin',
|
|
665
|
+
warrant: {
|
|
666
|
+
resource_type: 'role',
|
|
667
|
+
resource_id: 'admin',
|
|
668
|
+
relation: 'member',
|
|
669
|
+
subject: {
|
|
670
|
+
resource_type: 'user',
|
|
671
|
+
resource_id: 'user_123',
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
is_implicit: false,
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
resource_type: 'role',
|
|
678
|
+
resource_id: 'manager',
|
|
679
|
+
warrant: {
|
|
680
|
+
resource_type: 'role',
|
|
681
|
+
resource_id: 'manager',
|
|
682
|
+
relation: 'member',
|
|
683
|
+
subject: {
|
|
684
|
+
resource_type: 'user',
|
|
685
|
+
resource_id: 'user_123',
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
is_implicit: true,
|
|
689
|
+
},
|
|
690
|
+
],
|
|
691
|
+
list_metadata: {
|
|
692
|
+
before: null,
|
|
693
|
+
after: null,
|
|
694
|
+
},
|
|
695
|
+
warnings: [
|
|
696
|
+
{
|
|
697
|
+
code: 'missing_context_keys',
|
|
698
|
+
message: 'Missing required context keys',
|
|
699
|
+
keys: ['tenant_id'],
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
code: 'some_other_warning',
|
|
703
|
+
message: 'Some other warning message',
|
|
704
|
+
},
|
|
705
|
+
],
|
|
706
|
+
});
|
|
707
|
+
const result = yield workos.fga.query({
|
|
708
|
+
q: 'select role where user:user_123 is member',
|
|
709
|
+
});
|
|
710
|
+
expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/query');
|
|
711
|
+
expect(result.data).toMatchObject([
|
|
712
|
+
{
|
|
713
|
+
resourceType: 'role',
|
|
714
|
+
resourceId: 'admin',
|
|
715
|
+
warrant: {
|
|
716
|
+
resourceType: 'role',
|
|
717
|
+
resourceId: 'admin',
|
|
718
|
+
relation: 'member',
|
|
719
|
+
subject: {
|
|
720
|
+
resourceType: 'user',
|
|
721
|
+
resourceId: 'user_123',
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
isImplicit: false,
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
resourceType: 'role',
|
|
728
|
+
resourceId: 'manager',
|
|
729
|
+
warrant: {
|
|
730
|
+
resourceType: 'role',
|
|
731
|
+
resourceId: 'manager',
|
|
732
|
+
relation: 'member',
|
|
733
|
+
subject: {
|
|
734
|
+
resourceType: 'user',
|
|
735
|
+
resourceId: 'user_123',
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
isImplicit: true,
|
|
739
|
+
},
|
|
740
|
+
]);
|
|
741
|
+
expect(result.warnings).toMatchObject([
|
|
742
|
+
{
|
|
743
|
+
code: 'missing_context_keys',
|
|
744
|
+
message: 'Missing required context keys',
|
|
745
|
+
keys: ['tenant_id'],
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
code: 'some_other_warning',
|
|
749
|
+
message: 'Some other warning message',
|
|
750
|
+
},
|
|
751
|
+
]);
|
|
752
|
+
}));
|
|
609
753
|
it('sends correct params and options', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
610
754
|
(0, test_utils_1.fetchOnce)({
|
|
611
755
|
data: [
|
|
@@ -2,6 +2,7 @@ import { ResourceInterface, ResourceOptions } from './resource.interface';
|
|
|
2
2
|
import { PolicyContext, SerializedSubject, Subject } from './warrant.interface';
|
|
3
3
|
import { CheckOp } from './check-op.enum';
|
|
4
4
|
import { PostOptions } from '../../common/interfaces';
|
|
5
|
+
import { Warning } from './warning.interface';
|
|
5
6
|
export interface CheckWarrantOptions {
|
|
6
7
|
resource: ResourceInterface | ResourceOptions;
|
|
7
8
|
relation: string;
|
|
@@ -39,6 +40,7 @@ export interface CheckResultResponse {
|
|
|
39
40
|
is_implicit: boolean;
|
|
40
41
|
warrant_token: string;
|
|
41
42
|
debug_info?: DebugInfoResponse;
|
|
43
|
+
warnings?: Warning[];
|
|
42
44
|
}
|
|
43
45
|
export interface DebugInfo {
|
|
44
46
|
processingTime: number;
|
|
@@ -67,12 +69,14 @@ export interface CheckResultInterface {
|
|
|
67
69
|
isImplicit: boolean;
|
|
68
70
|
warrantToken: string;
|
|
69
71
|
debugInfo?: DebugInfo;
|
|
72
|
+
warnings?: Warning[];
|
|
70
73
|
}
|
|
71
74
|
export declare class CheckResult implements CheckResultInterface {
|
|
72
75
|
result: string;
|
|
73
76
|
isImplicit: boolean;
|
|
74
77
|
warrantToken: string;
|
|
75
78
|
debugInfo?: DebugInfo;
|
|
79
|
+
warnings?: Warning[];
|
|
76
80
|
constructor(json: CheckResultResponse);
|
|
77
81
|
isAuthorized(): boolean;
|
|
78
82
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { List, ListResponse } from '../../common/interfaces';
|
|
2
|
+
import { Warning } from './warning.interface';
|
|
3
|
+
export interface FGAListResponse<T> extends ListResponse<T> {
|
|
4
|
+
warnings?: Warning[];
|
|
5
|
+
}
|
|
6
|
+
export interface FGAList<T> extends List<T> {
|
|
7
|
+
warnings?: Warning[];
|
|
8
|
+
}
|
|
@@ -26,3 +26,4 @@ __exportStar(require("./resource.serializer"), exports);
|
|
|
26
26
|
__exportStar(require("./warrant-token.serializer"), exports);
|
|
27
27
|
__exportStar(require("./warrant.serializer"), exports);
|
|
28
28
|
__exportStar(require("./write-warrant-options.serializer"), exports);
|
|
29
|
+
__exportStar(require("./list.serializer"), exports);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FGAList } from '../interfaces/list.interface';
|
|
2
|
+
import { ListResponse } from '../../common/interfaces';
|
|
3
|
+
export declare const deserializeFGAList: <T, U>(response: ListResponse<T> & {
|
|
4
|
+
warnings?: any[] | undefined;
|
|
5
|
+
}, deserializeFn: (data: T) => U) => FGAList<U>;
|