@workos-inc/node 7.32.0 → 7.33.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.
Files changed (40) hide show
  1. package/lib/actions/actions.d.ts +19 -0
  2. package/lib/actions/actions.js +53 -0
  3. package/lib/actions/actions.spec.d.ts +1 -0
  4. package/lib/actions/actions.spec.js +78 -0
  5. package/lib/actions/fixtures/action-context.json +39 -0
  6. package/lib/actions/interfaces/response-payload.d.ts +23 -0
  7. package/lib/actions/interfaces/response-payload.js +2 -0
  8. package/lib/audit-logs/audit-logs.spec.js +58 -0
  9. package/lib/audit-logs/interfaces/create-audit-log-schema-options.interface.d.ts +1 -1
  10. package/lib/audit-logs/serializers/create-audit-log-schema-options.serializer.js +6 -4
  11. package/lib/audit-logs/serializers/create-audit-log-schema.serializer.js +3 -1
  12. package/lib/common/crypto/CryptoProvider.d.ts +32 -0
  13. package/lib/common/crypto/CryptoProvider.js +13 -0
  14. package/lib/common/crypto/CryptoProvider.spec.d.ts +1 -0
  15. package/lib/common/crypto/CryptoProvider.spec.js +57 -0
  16. package/lib/common/crypto/NodeCryptoProvider.d.ts +12 -0
  17. package/lib/common/crypto/NodeCryptoProvider.js +73 -0
  18. package/lib/common/crypto/SignatureProvider.d.ts +13 -0
  19. package/lib/common/crypto/SignatureProvider.js +53 -0
  20. package/lib/common/crypto/SignatureProvider.spec.d.ts +1 -0
  21. package/lib/common/crypto/SignatureProvider.spec.js +66 -0
  22. package/lib/common/crypto/SubtleCryptoProvider.d.ts +15 -0
  23. package/lib/common/crypto/SubtleCryptoProvider.js +75 -0
  24. package/lib/common/crypto/index.d.ts +4 -0
  25. package/lib/common/crypto/index.js +20 -0
  26. package/lib/common/net/index.d.ts +5 -0
  27. package/lib/common/net/index.js +31 -0
  28. package/lib/common/utils/unreachable.d.ts +10 -0
  29. package/lib/common/utils/unreachable.js +18 -0
  30. package/lib/index.d.ts +3 -0
  31. package/lib/index.js +12 -0
  32. package/lib/index.worker.d.ts +3 -0
  33. package/lib/index.worker.js +6 -0
  34. package/lib/webhooks/webhooks.d.ts +9 -9
  35. package/lib/webhooks/webhooks.js +11 -36
  36. package/lib/webhooks/webhooks.spec.js +23 -46
  37. package/lib/workos.d.ts +3 -0
  38. package/lib/workos.js +6 -1
  39. package/lib/workos.spec.js +5 -1
  40. package/package.json +2 -2
@@ -0,0 +1,19 @@
1
+ import { CryptoProvider } from '../common/crypto/crypto-provider';
2
+ import { AuthenticationActionResponseData, ResponsePayload, UserRegistrationActionResponseData } from './interfaces/response-payload';
3
+ export declare class Actions {
4
+ private signatureProvider;
5
+ constructor(cryptoProvider: CryptoProvider);
6
+ private get computeSignature();
7
+ get verifyHeader(): ({ payload, sigHeader, secret, tolerance, }: {
8
+ payload: any;
9
+ sigHeader: string;
10
+ secret: string;
11
+ tolerance?: number | undefined;
12
+ }) => Promise<boolean>;
13
+ serializeType(type: AuthenticationActionResponseData['type'] | UserRegistrationActionResponseData['type']): "authentication_action_response" | "user_registration_action_response";
14
+ signResponse(data: AuthenticationActionResponseData | UserRegistrationActionResponseData, secret: string): Promise<{
15
+ object: string;
16
+ payload: ResponsePayload;
17
+ signature: string;
18
+ }>;
19
+ }
@@ -0,0 +1,53 @@
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.Actions = void 0;
13
+ const crypto_1 = require("../common/crypto");
14
+ const unreachable_1 = require("../common/utils/unreachable");
15
+ class Actions {
16
+ constructor(cryptoProvider) {
17
+ this.signatureProvider = new crypto_1.SignatureProvider(cryptoProvider);
18
+ }
19
+ get computeSignature() {
20
+ return this.signatureProvider.computeSignature.bind(this.signatureProvider);
21
+ }
22
+ get verifyHeader() {
23
+ return this.signatureProvider.verifyHeader.bind(this.signatureProvider);
24
+ }
25
+ serializeType(type) {
26
+ switch (type) {
27
+ case 'authentication':
28
+ return 'authentication_action_response';
29
+ case 'user_registration':
30
+ return 'user_registration_action_response';
31
+ default:
32
+ return (0, unreachable_1.unreachable)(type);
33
+ }
34
+ }
35
+ signResponse(data, secret) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ let errorMessage;
38
+ const { verdict, type } = data;
39
+ if (verdict === 'Deny' && data.errorMessage) {
40
+ errorMessage = data.errorMessage;
41
+ }
42
+ const responsePayload = Object.assign({ timestamp: Date.now(), verdict }, (verdict === 'Deny' &&
43
+ data.errorMessage && { error_message: errorMessage }));
44
+ const response = {
45
+ object: this.serializeType(type),
46
+ payload: responsePayload,
47
+ signature: yield this.computeSignature(responsePayload.timestamp, responsePayload, secret),
48
+ };
49
+ return response;
50
+ });
51
+ }
52
+ }
53
+ exports.Actions = Actions;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,78 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const crypto_1 = __importDefault(require("crypto"));
16
+ const workos_1 = require("../workos");
17
+ const action_context_json_1 = __importDefault(require("./fixtures/action-context.json"));
18
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
19
+ const crypto_2 = require("../common/crypto");
20
+ describe('Actions', () => {
21
+ let secret;
22
+ beforeEach(() => {
23
+ secret = 'secret';
24
+ });
25
+ describe('signResponse', () => {
26
+ describe('type: authentication', () => {
27
+ it('returns a signed response', () => __awaiter(void 0, void 0, void 0, function* () {
28
+ const nodeCryptoProvider = new crypto_2.NodeCryptoProvider();
29
+ const response = yield workos.actions.signResponse({
30
+ type: 'authentication',
31
+ verdict: 'Allow',
32
+ }, secret);
33
+ const signedPayload = `${response.payload.timestamp}.${JSON.stringify(response.payload)}`;
34
+ const expectedSig = yield nodeCryptoProvider.computeHMACSignatureAsync(signedPayload, secret);
35
+ expect(response.object).toEqual('authentication_action_response');
36
+ expect(response.payload.verdict).toEqual('Allow');
37
+ expect(response.payload.timestamp).toBeGreaterThan(0);
38
+ expect(response.signature).toEqual(expectedSig);
39
+ }));
40
+ });
41
+ describe('type: user_registration', () => {
42
+ it('returns a signed response', () => __awaiter(void 0, void 0, void 0, function* () {
43
+ const nodeCryptoProvider = new crypto_2.NodeCryptoProvider();
44
+ const response = yield workos.actions.signResponse({
45
+ type: 'user_registration',
46
+ verdict: 'Deny',
47
+ errorMessage: 'User already exists',
48
+ }, secret);
49
+ const signedPayload = `${response.payload.timestamp}.${JSON.stringify(response.payload)}`;
50
+ const expectedSig = yield nodeCryptoProvider.computeHMACSignatureAsync(signedPayload, secret);
51
+ expect(response.object).toEqual('user_registration_action_response');
52
+ expect(response.payload.verdict).toEqual('Deny');
53
+ expect(response.payload.timestamp).toBeGreaterThan(0);
54
+ expect(response.signature).toEqual(expectedSig);
55
+ }));
56
+ });
57
+ });
58
+ describe('verifyHeader', () => {
59
+ it('aliases to the signature provider', () => __awaiter(void 0, void 0, void 0, function* () {
60
+ const spy = jest.spyOn(
61
+ // tslint:disable-next-line
62
+ workos.actions['signatureProvider'], 'verifyHeader');
63
+ const timestamp = Date.now() * 1000;
64
+ const unhashedString = `${timestamp}.${JSON.stringify(action_context_json_1.default)}`;
65
+ const signatureHash = crypto_1.default
66
+ .createHmac('sha256', secret)
67
+ .update(unhashedString)
68
+ .digest()
69
+ .toString('hex');
70
+ yield workos.actions.verifyHeader({
71
+ payload: action_context_json_1.default,
72
+ sigHeader: `t=${timestamp}, v1=${signatureHash}`,
73
+ secret,
74
+ });
75
+ expect(spy).toHaveBeenCalled();
76
+ }));
77
+ });
78
+ });
@@ -0,0 +1,39 @@
1
+ {
2
+ "user": {
3
+ "object": "user",
4
+ "id": "01JATCHZVEC5EPANDPEZVM68Y9",
5
+ "email": "jane@foocorp.com",
6
+ "first_name": "Jane",
7
+ "last_name": "Doe",
8
+ "email_verified": true,
9
+ "profile_picture_url": "https://example.com/jane.jpg",
10
+ "created_at": "2024-10-22T17:12:50.746Z",
11
+ "updated_at": "2024-10-22T17:12:50.746Z"
12
+ },
13
+ "ip_address": "50.141.123.10",
14
+ "user_agent": "Mozilla/5.0",
15
+ "issuer": "test",
16
+ "object": "authentication_action_context",
17
+ "organization": {
18
+ "object": "organization",
19
+ "id": "01JATCMZJY26PQ59XT9BNT0FNN",
20
+ "name": "Foo Corp",
21
+ "allow_profiles_outside_organization": false,
22
+ "domains": [],
23
+ "lookup_key": "my-key",
24
+ "created_at": "2024-10-22T17:12:50.746Z",
25
+ "updated_at": "2024-10-22T17:12:50.746Z"
26
+ },
27
+ "organization_membership": {
28
+ "object": "organization_membership",
29
+ "id": "01JATCNVYCHT1SZGENR4QTXKRK",
30
+ "user_id": "01JATCHZVEC5EPANDPEZVM68Y9",
31
+ "organization_id": "01JATCMZJY26PQ59XT9BNT0FNN",
32
+ "role": {
33
+ "slug": "member"
34
+ },
35
+ "status": "active",
36
+ "created_at": "2024-10-22T17:12:50.746Z",
37
+ "updated_at": "2024-10-22T17:12:50.746Z"
38
+ }
39
+ }
@@ -0,0 +1,23 @@
1
+ export interface ResponsePayload {
2
+ timestamp: number;
3
+ verdict?: 'Allow' | 'Deny';
4
+ errorMessage?: string;
5
+ }
6
+ interface AllowResponseData {
7
+ verdict: 'Allow';
8
+ }
9
+ interface DenyResponseData {
10
+ verdict: 'Deny';
11
+ errorMessage?: string;
12
+ }
13
+ export type AuthenticationActionResponseData = (AllowResponseData & {
14
+ type: 'authentication';
15
+ }) | (DenyResponseData & {
16
+ type: 'authentication';
17
+ });
18
+ export type UserRegistrationActionResponseData = (AllowResponseData & {
19
+ type: 'user_registration';
20
+ }) | (DenyResponseData & {
21
+ type: 'user_registration';
22
+ });
23
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -61,6 +61,7 @@ const schema = {
61
61
  baz: 'boolean',
62
62
  },
63
63
  };
64
+ const schemaWithoutMetadata = Object.assign(Object.assign({}, schema), { metadata: undefined });
64
65
  describe('AuditLogs', () => {
65
66
  beforeEach(() => jest_fetch_mock_1.default.resetMocks());
66
67
  describe('createEvent', () => {
@@ -328,6 +329,63 @@ describe('AuditLogs', () => {
328
329
  expect(workosSpy).toHaveBeenCalledWith('/audit_logs/actions/user.logged_in/schemas', (0, serializers_1.serializeCreateAuditLogSchemaOptions)(schema), { idempotencyKey: 'the-idempotency-key' });
329
330
  }));
330
331
  });
332
+ describe('without metadata', () => {
333
+ it('does not include metadata with the request', () => __awaiter(void 0, void 0, void 0, function* () {
334
+ const workosSpy = jest.spyOn(workos_1.WorkOS.prototype, 'post');
335
+ const time = new Date().toISOString();
336
+ const createSchemaResult = {
337
+ object: 'audit_log_schema',
338
+ version: 1,
339
+ targets: [
340
+ {
341
+ type: 'user',
342
+ metadata: {
343
+ user_id: 'string',
344
+ },
345
+ },
346
+ ],
347
+ actor: {
348
+ metadata: {
349
+ actor_id: 'string',
350
+ },
351
+ },
352
+ metadata: undefined,
353
+ createdAt: time,
354
+ };
355
+ const createSchemaResponse = {
356
+ object: 'audit_log_schema',
357
+ version: 1,
358
+ targets: [
359
+ {
360
+ type: 'user',
361
+ metadata: {
362
+ type: 'object',
363
+ properties: {
364
+ user_id: {
365
+ type: 'string',
366
+ },
367
+ },
368
+ },
369
+ },
370
+ ],
371
+ actor: {
372
+ metadata: {
373
+ type: 'object',
374
+ properties: {
375
+ actor_id: {
376
+ type: 'string',
377
+ },
378
+ },
379
+ },
380
+ },
381
+ created_at: time,
382
+ };
383
+ workosSpy.mockResolvedValueOnce((0, workos_mock_response_1.mockWorkOsResponse)(201, createSchemaResponse));
384
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
385
+ yield expect(workos.auditLogs.createSchema(schemaWithoutMetadata)).resolves.toEqual(createSchemaResult);
386
+ expect(workosSpy).toHaveBeenCalledWith('/audit_logs/actions/user.logged_in/schemas', (0, serializers_1.serializeCreateAuditLogSchemaOptions)(schemaWithoutMetadata), {});
387
+ }));
388
+ });
331
389
  describe('when the api responds with a 201', () => {
332
390
  it('returns `audit_log_schema`', () => __awaiter(void 0, void 0, void 0, function* () {
333
391
  const workosSpy = jest.spyOn(workos_1.WorkOS.prototype, 'post');
@@ -53,7 +53,7 @@ export interface CreateAuditLogSchemaResponse {
53
53
  properties: AuditLogSchemaMetadata;
54
54
  };
55
55
  };
56
- metadata: {
56
+ metadata?: {
57
57
  type: 'object';
58
58
  properties: AuditLogSchemaMetadata;
59
59
  };
@@ -33,10 +33,12 @@ const serializeCreateAuditLogSchemaOptions = (schema) => {
33
33
  : undefined,
34
34
  };
35
35
  }),
36
- metadata: {
37
- type: 'object',
38
- properties: serializeMetadata(schema.metadata),
39
- },
36
+ metadata: schema.metadata
37
+ ? {
38
+ type: 'object',
39
+ properties: serializeMetadata(schema.metadata),
40
+ }
41
+ : undefined,
40
42
  });
41
43
  };
42
44
  exports.serializeCreateAuditLogSchemaOptions = serializeCreateAuditLogSchemaOptions;
@@ -29,7 +29,9 @@ const deserializeAuditLogSchema = (auditLogSchema) => {
29
29
  actor: {
30
30
  metadata: deserializeMetadata((_a = auditLogSchema.actor) === null || _a === void 0 ? void 0 : _a.metadata),
31
31
  },
32
- metadata: deserializeMetadata(auditLogSchema.metadata),
32
+ metadata: auditLogSchema.metadata
33
+ ? deserializeMetadata(auditLogSchema.metadata)
34
+ : undefined,
33
35
  createdAt: auditLogSchema.created_at,
34
36
  });
35
37
  };
@@ -0,0 +1,32 @@
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
+ }
@@ -0,0 +1,13 @@
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;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,57 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const crypto_1 = __importDefault(require("crypto"));
16
+ const NodeCryptoProvider_1 = require("./NodeCryptoProvider");
17
+ const SubtleCryptoProvider_1 = require("./SubtleCryptoProvider");
18
+ const webhook_json_1 = __importDefault(require("../../webhooks/fixtures/webhook.json"));
19
+ const SignatureProvider_1 = require("./SignatureProvider");
20
+ describe('CryptoProvider', () => {
21
+ let payload;
22
+ let secret;
23
+ let timestamp;
24
+ let signatureHash;
25
+ beforeEach(() => {
26
+ payload = webhook_json_1.default;
27
+ secret = 'secret';
28
+ timestamp = Date.now() * 1000;
29
+ const unhashedString = `${timestamp}.${JSON.stringify(payload)}`;
30
+ signatureHash = crypto_1.default
31
+ .createHmac('sha256', secret)
32
+ .update(unhashedString)
33
+ .digest()
34
+ .toString('hex');
35
+ });
36
+ describe('when computing HMAC signature', () => {
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 NodeCryptoProvider_1.NodeCryptoProvider();
39
+ const subtleCryptoProvider = new SubtleCryptoProvider_1.SubtleCryptoProvider();
40
+ const stringifiedPayload = JSON.stringify(payload);
41
+ const payloadHMAC = `${timestamp}.${stringifiedPayload}`;
42
+ const nodeCompare = yield nodeCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
43
+ const subtleCompare = yield subtleCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
44
+ expect(nodeCompare).toEqual(subtleCompare);
45
+ }));
46
+ });
47
+ describe('when securely comparing', () => {
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 NodeCryptoProvider_1.NodeCryptoProvider();
50
+ const subtleCryptoProvider = new SubtleCryptoProvider_1.SubtleCryptoProvider();
51
+ const signatureProvider = new SignatureProvider_1.SignatureProvider(subtleCryptoProvider);
52
+ const signature = yield signatureProvider.computeSignature(timestamp, payload, secret);
53
+ expect(nodeCryptoProvider.secureCompare(signature, signatureHash)).toEqual(subtleCryptoProvider.secureCompare(signature, signatureHash));
54
+ expect(nodeCryptoProvider.secureCompare(signature, 'foo')).toEqual(subtleCryptoProvider.secureCompare(signature, 'foo'));
55
+ }));
56
+ });
57
+ });
@@ -0,0 +1,12 @@
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
+ }
@@ -0,0 +1,73 @@
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;
@@ -0,0 +1,13 @@
1
+ import { CryptoProvider } from './CryptoProvider';
2
+ export declare class SignatureProvider {
3
+ private cryptoProvider;
4
+ constructor(cryptoProvider: CryptoProvider);
5
+ verifyHeader({ payload, sigHeader, secret, tolerance, }: {
6
+ payload: any;
7
+ sigHeader: string;
8
+ secret: string;
9
+ tolerance?: number;
10
+ }): Promise<boolean>;
11
+ getTimestampAndSignatureHash(sigHeader: string): [string, string];
12
+ computeSignature(timestamp: any, payload: any, secret: string): Promise<string>;
13
+ }
@@ -0,0 +1,53 @@
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.SignatureProvider = void 0;
13
+ const exceptions_1 = require("../exceptions");
14
+ class SignatureProvider {
15
+ constructor(cryptoProvider) {
16
+ this.cryptoProvider = cryptoProvider;
17
+ }
18
+ verifyHeader({ payload, sigHeader, secret, tolerance = 180000, }) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const [timestamp, signatureHash] = this.getTimestampAndSignatureHash(sigHeader);
21
+ if (!signatureHash || Object.keys(signatureHash).length === 0) {
22
+ throw new exceptions_1.SignatureVerificationException('No signature hash found with expected scheme v1');
23
+ }
24
+ if (parseInt(timestamp, 10) < Date.now() - tolerance) {
25
+ throw new exceptions_1.SignatureVerificationException('Timestamp outside the tolerance zone');
26
+ }
27
+ const expectedSig = yield this.computeSignature(timestamp, payload, secret);
28
+ if ((yield this.cryptoProvider.secureCompare(expectedSig, signatureHash)) ===
29
+ false) {
30
+ throw new exceptions_1.SignatureVerificationException('Signature hash does not match the expected signature hash for payload');
31
+ }
32
+ return true;
33
+ });
34
+ }
35
+ getTimestampAndSignatureHash(sigHeader) {
36
+ const signature = sigHeader;
37
+ const [t, v1] = signature.split(',');
38
+ if (typeof t === 'undefined' || typeof v1 === 'undefined') {
39
+ throw new exceptions_1.SignatureVerificationException('Signature or timestamp missing');
40
+ }
41
+ const { 1: timestamp } = t.split('=');
42
+ const { 1: signatureHash } = v1.split('=');
43
+ return [timestamp, signatureHash];
44
+ }
45
+ computeSignature(timestamp, payload, secret) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ payload = JSON.stringify(payload);
48
+ const signedPayload = `${timestamp}.${payload}`;
49
+ return yield this.cryptoProvider.computeHMACSignatureAsync(signedPayload, secret);
50
+ });
51
+ }
52
+ }
53
+ exports.SignatureProvider = SignatureProvider;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,66 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const crypto_1 = __importDefault(require("crypto"));
16
+ const SubtleCryptoProvider_1 = require("./SubtleCryptoProvider");
17
+ const webhook_json_1 = __importDefault(require("../../webhooks/fixtures/webhook.json"));
18
+ const SignatureProvider_1 = require("./SignatureProvider");
19
+ describe('SignatureProvider', () => {
20
+ let payload;
21
+ let secret;
22
+ let timestamp;
23
+ let signatureHash;
24
+ const signatureProvider = new SignatureProvider_1.SignatureProvider(new SubtleCryptoProvider_1.SubtleCryptoProvider());
25
+ beforeEach(() => {
26
+ payload = webhook_json_1.default;
27
+ secret = 'secret';
28
+ timestamp = Date.now() * 1000;
29
+ const unhashedString = `${timestamp}.${JSON.stringify(payload)}`;
30
+ signatureHash = crypto_1.default
31
+ .createHmac('sha256', secret)
32
+ .update(unhashedString)
33
+ .digest()
34
+ .toString('hex');
35
+ });
36
+ describe('verifyHeader', () => {
37
+ it('returns true when the signature is valid', () => __awaiter(void 0, void 0, void 0, function* () {
38
+ const sigHeader = `t=${timestamp}, v1=${signatureHash}`;
39
+ const options = { payload, sigHeader, secret };
40
+ const result = yield signatureProvider.verifyHeader(options);
41
+ expect(result).toBeTruthy();
42
+ }));
43
+ });
44
+ describe('getTimestampAndSignatureHash', () => {
45
+ it('returns the timestamp and signature when the signature is valid', () => {
46
+ const sigHeader = `t=${timestamp}, v1=${signatureHash}`;
47
+ const timestampAndSignature = signatureProvider.getTimestampAndSignatureHash(sigHeader);
48
+ expect(timestampAndSignature).toEqual([
49
+ timestamp.toString(),
50
+ signatureHash,
51
+ ]);
52
+ });
53
+ });
54
+ describe('computeSignature', () => {
55
+ it('returns the computed signature', () => __awaiter(void 0, void 0, void 0, function* () {
56
+ const signature = yield signatureProvider.computeSignature(timestamp, payload, secret);
57
+ expect(signature).toEqual(signatureHash);
58
+ }));
59
+ });
60
+ describe('when in an environment that supports SubtleCrypto', () => {
61
+ it('automatically uses the subtle crypto library', () => {
62
+ // tslint:disable-next-line
63
+ expect(signatureProvider['cryptoProvider']).toBeInstanceOf(SubtleCryptoProvider_1.SubtleCryptoProvider);
64
+ });
65
+ });
66
+ });
@@ -0,0 +1,15 @@
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
+ }
@@ -0,0 +1,75 @@
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
+ }
@@ -0,0 +1,4 @@
1
+ export * from './NodeCryptoProvider';
2
+ export * from './SubtleCryptoProvider';
3
+ export * from './CryptoProvider';
4
+ export * from './SignatureProvider';
@@ -0,0 +1,20 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./NodeCryptoProvider"), exports);
18
+ __exportStar(require("./SubtleCryptoProvider"), exports);
19
+ __exportStar(require("./CryptoProvider"), exports);
20
+ __exportStar(require("./SignatureProvider"), exports);
@@ -0,0 +1,5 @@
1
+ import { HttpClient } from './http-client';
2
+ export declare function createHttpClient(baseURL: string, options: RequestInit, fetchFn?: typeof fetch): HttpClient;
3
+ export * from './fetch-client';
4
+ export * from './node-client';
5
+ export * from './http-client';
@@ -0,0 +1,31 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createHttpClient = void 0;
18
+ const fetch_client_1 = require("./fetch-client");
19
+ const node_client_1 = require("./node-client");
20
+ function createHttpClient(baseURL, options, fetchFn) {
21
+ if (typeof fetch !== 'undefined' || typeof fetchFn !== 'undefined') {
22
+ return new fetch_client_1.FetchHttpClient(baseURL, options, fetchFn);
23
+ }
24
+ else {
25
+ return new node_client_1.NodeHttpClient(baseURL, options);
26
+ }
27
+ }
28
+ exports.createHttpClient = createHttpClient;
29
+ __exportStar(require("./fetch-client"), exports);
30
+ __exportStar(require("./node-client"), exports);
31
+ __exportStar(require("./http-client"), exports);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Indicates that code is unreachable.
3
+ *
4
+ * This can be used for exhaustiveness checks in situations where the compiler
5
+ * would not otherwise check for exhaustiveness.
6
+ *
7
+ * If the determination that the code is unreachable proves incorrect, an
8
+ * exception is thrown.
9
+ */
10
+ export declare const unreachable: (condition: never, message?: string) => never;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unreachable = void 0;
4
+ /**
5
+ * Indicates that code is unreachable.
6
+ *
7
+ * This can be used for exhaustiveness checks in situations where the compiler
8
+ * would not otherwise check for exhaustiveness.
9
+ *
10
+ * If the determination that the code is unreachable proves incorrect, an
11
+ * exception is thrown.
12
+ */
13
+ const unreachable = (condition,
14
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
15
+ message = `Entered unreachable code. Received '${condition}'.`) => {
16
+ throw new TypeError(message);
17
+ };
18
+ exports.unreachable = unreachable;
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { HttpClient } from './common/net/http-client';
2
+ import { Actions } from './actions/actions';
2
3
  import { Webhooks } from './webhooks/webhooks';
3
4
  import { WorkOS } from './workos';
4
5
  import { WorkOSOptions } from './common/interfaces';
@@ -23,6 +24,8 @@ declare class WorkOSNode extends WorkOS {
23
24
  /** @override */
24
25
  createWebhookClient(): Webhooks;
25
26
  /** @override */
27
+ createActionsClient(): Actions;
28
+ /** @override */
26
29
  createIronSessionProvider(): IronSessionProvider;
27
30
  /** @override */
28
31
  emitWarning(warning: string): void;
package/lib/index.js CHANGED
@@ -19,6 +19,7 @@ const node_crypto_provider_1 = require("./common/crypto/node-crypto-provider");
19
19
  const subtle_crypto_provider_1 = require("./common/crypto/subtle-crypto-provider");
20
20
  const fetch_client_1 = require("./common/net/fetch-client");
21
21
  const node_client_1 = require("./common/net/node-client");
22
+ const actions_1 = require("./actions/actions");
22
23
  const webhooks_1 = require("./webhooks/webhooks");
23
24
  const workos_1 = require("./workos");
24
25
  const web_iron_session_provider_1 = require("./common/iron-session/web-iron-session-provider");
@@ -61,6 +62,17 @@ class WorkOSNode extends workos_1.WorkOS {
61
62
  return new webhooks_1.Webhooks(cryptoProvider);
62
63
  }
63
64
  /** @override */
65
+ createActionsClient() {
66
+ let cryptoProvider;
67
+ if (typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined') {
68
+ cryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
69
+ }
70
+ else {
71
+ cryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
72
+ }
73
+ return new actions_1.Actions(cryptoProvider);
74
+ }
75
+ /** @override */
64
76
  createIronSessionProvider() {
65
77
  return new web_iron_session_provider_1.WebIronSessionProvider();
66
78
  }
@@ -1,3 +1,4 @@
1
+ import { Actions } from './actions/actions';
1
2
  import { IronSessionProvider } from './common/iron-session/iron-session-provider';
2
3
  import { HttpClient } from './common/net/http-client';
3
4
  import { WorkOSOptions } from './index.worker';
@@ -21,6 +22,8 @@ declare class WorkOSWorker extends WorkOS {
21
22
  /** @override */
22
23
  createWebhookClient(): Webhooks;
23
24
  /** @override */
25
+ createActionsClient(): Actions;
26
+ /** @override */
24
27
  createIronSessionProvider(): IronSessionProvider;
25
28
  /** @override */
26
29
  emitWarning(warning: string): void;
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.WorkOS = void 0;
18
+ const actions_1 = require("./actions/actions");
18
19
  const subtle_crypto_provider_1 = require("./common/crypto/subtle-crypto-provider");
19
20
  const edge_iron_session_provider_1 = require("./common/iron-session/edge-iron-session-provider");
20
21
  const fetch_client_1 = require("./common/net/fetch-client");
@@ -44,6 +45,11 @@ class WorkOSWorker extends workos_1.WorkOS {
44
45
  return new webhooks_1.Webhooks(cryptoProvider);
45
46
  }
46
47
  /** @override */
48
+ createActionsClient() {
49
+ const cryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
50
+ return new actions_1.Actions(cryptoProvider);
51
+ }
52
+ /** @override */
47
53
  createIronSessionProvider() {
48
54
  return new edge_iron_session_provider_1.EdgeIronSessionProvider();
49
55
  }
@@ -1,20 +1,20 @@
1
1
  import { Event } from '../common/interfaces';
2
2
  import { CryptoProvider } from '../common/crypto/crypto-provider';
3
3
  export declare class Webhooks {
4
- private cryptoProvider;
4
+ private signatureProvider;
5
5
  constructor(cryptoProvider: CryptoProvider);
6
+ get verifyHeader(): ({ payload, sigHeader, secret, tolerance, }: {
7
+ payload: any;
8
+ sigHeader: string;
9
+ secret: string;
10
+ tolerance?: number | undefined;
11
+ }) => Promise<boolean>;
12
+ get computeSignature(): (timestamp: any, payload: any, secret: string) => Promise<string>;
13
+ get getTimestampAndSignatureHash(): (sigHeader: string) => [string, string];
6
14
  constructEvent({ payload, sigHeader, secret, tolerance, }: {
7
15
  payload: unknown;
8
16
  sigHeader: string;
9
17
  secret: string;
10
18
  tolerance?: number;
11
19
  }): Promise<Event>;
12
- verifyHeader({ payload, sigHeader, secret, tolerance, }: {
13
- payload: any;
14
- sigHeader: string;
15
- secret: string;
16
- tolerance?: number;
17
- }): Promise<boolean>;
18
- getTimestampAndSignatureHash(sigHeader: string): [string, string];
19
- computeSignature(timestamp: any, payload: any, secret: string): Promise<string>;
20
20
  }
@@ -10,11 +10,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.Webhooks = void 0;
13
- const exceptions_1 = require("../common/exceptions");
14
13
  const serializers_1 = require("../common/serializers");
14
+ const SignatureProvider_1 = require("../common/crypto/SignatureProvider");
15
15
  class Webhooks {
16
16
  constructor(cryptoProvider) {
17
- this.cryptoProvider = cryptoProvider;
17
+ this.signatureProvider = new SignatureProvider_1.SignatureProvider(cryptoProvider);
18
+ }
19
+ get verifyHeader() {
20
+ return this.signatureProvider.verifyHeader.bind(this.signatureProvider);
21
+ }
22
+ get computeSignature() {
23
+ return this.signatureProvider.computeSignature.bind(this.signatureProvider);
24
+ }
25
+ get getTimestampAndSignatureHash() {
26
+ return this.signatureProvider.getTimestampAndSignatureHash.bind(this.signatureProvider);
18
27
  }
19
28
  constructEvent({ payload, sigHeader, secret, tolerance = 180000, }) {
20
29
  return __awaiter(this, void 0, void 0, function* () {
@@ -24,39 +33,5 @@ class Webhooks {
24
33
  return (0, serializers_1.deserializeEvent)(webhookPayload);
25
34
  });
26
35
  }
27
- verifyHeader({ payload, sigHeader, secret, tolerance = 180000, }) {
28
- return __awaiter(this, void 0, void 0, function* () {
29
- const [timestamp, signatureHash] = this.getTimestampAndSignatureHash(sigHeader);
30
- if (!signatureHash || Object.keys(signatureHash).length === 0) {
31
- throw new exceptions_1.SignatureVerificationException('No signature hash found with expected scheme v1');
32
- }
33
- if (parseInt(timestamp, 10) < Date.now() - tolerance) {
34
- throw new exceptions_1.SignatureVerificationException('Timestamp outside the tolerance zone');
35
- }
36
- const expectedSig = yield this.computeSignature(timestamp, payload, secret);
37
- if ((yield this.cryptoProvider.secureCompare(expectedSig, signatureHash)) ===
38
- false) {
39
- throw new exceptions_1.SignatureVerificationException('Signature hash does not match the expected signature hash for payload');
40
- }
41
- return true;
42
- });
43
- }
44
- getTimestampAndSignatureHash(sigHeader) {
45
- const signature = sigHeader;
46
- const [t, v1] = signature.split(',');
47
- if (typeof t === 'undefined' || typeof v1 === 'undefined') {
48
- throw new exceptions_1.SignatureVerificationException('Signature or timestamp missing');
49
- }
50
- const { 1: timestamp } = t.split('=');
51
- const { 1: signatureHash } = v1.split('=');
52
- return [timestamp, signatureHash];
53
- }
54
- computeSignature(timestamp, payload, secret) {
55
- return __awaiter(this, void 0, void 0, function* () {
56
- payload = JSON.stringify(payload);
57
- const signedPayload = `${timestamp}.${payload}`;
58
- return yield this.cryptoProvider.computeHMACSignatureAsync(signedPayload, secret);
59
- });
60
- }
61
36
  }
62
37
  exports.Webhooks = Webhooks;
@@ -17,8 +17,6 @@ const workos_1 = require("../workos");
17
17
  const webhook_json_1 = __importDefault(require("./fixtures/webhook.json"));
18
18
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
19
19
  const exceptions_1 = require("../common/exceptions");
20
- const node_crypto_provider_1 = require("../common/crypto/node-crypto-provider");
21
- const subtle_crypto_provider_1 = require("../common/crypto/subtle-crypto-provider");
22
20
  describe('Webhooks', () => {
23
21
  let payload;
24
22
  let secret;
@@ -166,55 +164,34 @@ describe('Webhooks', () => {
166
164
  });
167
165
  });
168
166
  describe('verifyHeader', () => {
169
- it('returns true when the signature is valid', () => __awaiter(void 0, void 0, void 0, function* () {
170
- const sigHeader = `t=${timestamp}, v1=${signatureHash}`;
171
- const options = { payload, sigHeader, secret };
172
- const result = yield workos.webhooks.verifyHeader(options);
173
- expect(result).toBeTruthy();
167
+ it('aliases to the signature provider', () => __awaiter(void 0, void 0, void 0, function* () {
168
+ const spy = jest.spyOn(
169
+ // tslint:disable-next-line
170
+ workos.webhooks['signatureProvider'], 'verifyHeader');
171
+ yield workos.webhooks.verifyHeader({
172
+ payload,
173
+ sigHeader: `t=${timestamp}, v1=${signatureHash}`,
174
+ secret,
175
+ });
176
+ expect(spy).toHaveBeenCalled();
174
177
  }));
175
178
  });
176
- describe('getTimestampAndSignatureHash', () => {
177
- it('returns the timestamp and signature when the signature is valid', () => {
178
- const sigHeader = `t=${timestamp}, v1=${signatureHash}`;
179
- const timestampAndSignature = workos.webhooks.getTimestampAndSignatureHash(sigHeader);
180
- expect(timestampAndSignature).toEqual([
181
- timestamp.toString(),
182
- signatureHash,
183
- ]);
184
- });
185
- });
186
179
  describe('computeSignature', () => {
187
- it('returns the computed signature', () => __awaiter(void 0, void 0, void 0, function* () {
188
- const signature = yield workos.webhooks.computeSignature(timestamp, payload, secret);
189
- expect(signature).toEqual(signatureHash);
180
+ it('aliases to the signature provider', () => __awaiter(void 0, void 0, void 0, function* () {
181
+ const spy = jest.spyOn(
182
+ // tslint:disable-next-line
183
+ workos.webhooks['signatureProvider'], 'computeSignature');
184
+ yield workos.webhooks.computeSignature(timestamp, payload, secret);
185
+ expect(spy).toHaveBeenCalled();
190
186
  }));
191
187
  });
192
- describe('when in an environment that supports SubtleCrypto', () => {
193
- it('automatically uses the subtle crypto library', () => {
188
+ describe('getTimestampAndSignatureHash', () => {
189
+ it('aliases to the signature provider', () => __awaiter(void 0, void 0, void 0, function* () {
190
+ const spy = jest.spyOn(
194
191
  // tslint:disable-next-line
195
- expect(workos.webhooks['cryptoProvider']).toBeInstanceOf(subtle_crypto_provider_1.SubtleCryptoProvider);
196
- });
197
- });
198
- describe('CryptoProvider', () => {
199
- describe('when computing HMAC signature', () => {
200
- it('returns the same for the Node crypto and Web Crypto versions', () => __awaiter(void 0, void 0, void 0, function* () {
201
- const nodeCryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
202
- const subtleCryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
203
- const stringifiedPayload = JSON.stringify(payload);
204
- const payloadHMAC = `${timestamp}.${stringifiedPayload}`;
205
- const nodeCompare = yield nodeCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
206
- const subtleCompare = yield subtleCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
207
- expect(nodeCompare).toEqual(subtleCompare);
208
- }));
209
- });
210
- describe('when securely comparing', () => {
211
- it('returns the same for the Node crypto and Web Crypto versions', () => __awaiter(void 0, void 0, void 0, function* () {
212
- const nodeCryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
213
- const subtleCryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
214
- const signature = yield workos.webhooks.computeSignature(timestamp, payload, secret);
215
- expect(nodeCryptoProvider.secureCompare(signature, signatureHash)).toEqual(subtleCryptoProvider.secureCompare(signature, signatureHash));
216
- expect(nodeCryptoProvider.secureCompare(signature, 'foo')).toEqual(subtleCryptoProvider.secureCompare(signature, 'foo'));
217
- }));
218
- });
192
+ workos.webhooks['signatureProvider'], 'getTimestampAndSignatureHash');
193
+ workos.webhooks.getTimestampAndSignatureHash(`t=${timestamp}, v1=${signatureHash}`);
194
+ expect(spy).toHaveBeenCalled();
195
+ }));
219
196
  });
220
197
  });
package/lib/workos.d.ts CHANGED
@@ -14,12 +14,14 @@ import { FGA } from './fga/fga';
14
14
  import { HttpClient } from './common/net/http-client';
15
15
  import { IronSessionProvider } from './common/iron-session/iron-session-provider';
16
16
  import { Widgets } from './widgets/widgets';
17
+ import { Actions } from './actions/actions';
17
18
  export declare class WorkOS {
18
19
  readonly key?: string | undefined;
19
20
  readonly options: WorkOSOptions;
20
21
  readonly baseURL: string;
21
22
  readonly client: HttpClient;
22
23
  readonly clientId?: string;
24
+ readonly actions: Actions;
23
25
  readonly auditLogs: AuditLogs;
24
26
  readonly directorySync: DirectorySync;
25
27
  readonly organizations: Organizations;
@@ -35,6 +37,7 @@ export declare class WorkOS {
35
37
  readonly widgets: Widgets;
36
38
  constructor(key?: string | undefined, options?: WorkOSOptions);
37
39
  createWebhookClient(): Webhooks;
40
+ createActionsClient(): Actions;
38
41
  createHttpClient(options: WorkOSOptions, userAgent: string): HttpClient;
39
42
  createIronSessionProvider(): IronSessionProvider;
40
43
  get version(): string;
package/lib/workos.js CHANGED
@@ -28,7 +28,8 @@ const http_client_1 = require("./common/net/http-client");
28
28
  const subtle_crypto_provider_1 = require("./common/crypto/subtle-crypto-provider");
29
29
  const fetch_client_1 = require("./common/net/fetch-client");
30
30
  const widgets_1 = require("./widgets/widgets");
31
- const VERSION = '7.32.0';
31
+ const actions_1 = require("./actions/actions");
32
+ const VERSION = '7.33.1';
32
33
  const DEFAULT_HOSTNAME = 'api.workos.com';
33
34
  const HEADER_AUTHORIZATION = 'Authorization';
34
35
  const HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
@@ -78,6 +79,7 @@ class WorkOS {
78
79
  userAgent += ` ${name}: ${version}`;
79
80
  }
80
81
  this.webhooks = this.createWebhookClient();
82
+ this.actions = this.createActionsClient();
81
83
  // Must initialize UserManagement after baseURL is configured
82
84
  this.userManagement = new user_management_1.UserManagement(this, this.createIronSessionProvider());
83
85
  this.client = this.createHttpClient(options, userAgent);
@@ -85,6 +87,9 @@ class WorkOS {
85
87
  createWebhookClient() {
86
88
  return new webhooks_1.Webhooks(new subtle_crypto_provider_1.SubtleCryptoProvider());
87
89
  }
90
+ createActionsClient() {
91
+ return new actions_1.Actions(new subtle_crypto_provider_1.SubtleCryptoProvider());
92
+ }
88
93
  createHttpClient(options, userAgent) {
89
94
  var _a;
90
95
  return new fetch_client_1.FetchHttpClient(this.baseURL, Object.assign(Object.assign({}, options.config), { headers: Object.assign(Object.assign({}, (_a = options.config) === null || _a === void 0 ? void 0 : _a.headers), { Authorization: `Bearer ${this.key}`, 'User-Agent': userAgent }) }));
@@ -246,8 +246,12 @@ describe('WorkOS', () => {
246
246
  const workos = new index_worker_1.WorkOS('sk_test_key');
247
247
  // tslint:disable-next-line
248
248
  expect(workos['client']).toBeInstanceOf(fetch_client_1.FetchHttpClient);
249
+ expect(
249
250
  // tslint:disable-next-line
250
- expect(workos.webhooks['cryptoProvider']).toBeInstanceOf(subtle_crypto_provider_1.SubtleCryptoProvider);
251
+ workos.webhooks['signatureProvider']['cryptoProvider']).toBeInstanceOf(subtle_crypto_provider_1.SubtleCryptoProvider);
252
+ expect(
253
+ // tslint:disable-next-line
254
+ workos.actions['signatureProvider']['cryptoProvider']).toBeInstanceOf(subtle_crypto_provider_1.SubtleCryptoProvider);
251
255
  });
252
256
  it('uses console.warn to emit warnings', () => {
253
257
  const workos = new index_worker_1.WorkOS('sk_test_key');
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "7.32.0",
2
+ "version": "7.33.1",
3
3
  "name": "@workos-inc/node",
4
4
  "author": "WorkOS",
5
5
  "description": "A Node wrapper for the WorkOS API",
@@ -72,4 +72,4 @@
72
72
  "default": "./lib/index.js"
73
73
  }
74
74
  }
75
- }
75
+ }