@workos-inc/node 7.30.0 → 7.31.0-beta.actions1

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 (63) 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/common/crypto/CryptoProvider.d.ts +32 -0
  9. package/lib/common/crypto/CryptoProvider.js +13 -0
  10. package/lib/common/crypto/CryptoProvider.spec.d.ts +1 -0
  11. package/lib/common/crypto/CryptoProvider.spec.js +57 -0
  12. package/lib/common/crypto/NodeCryptoProvider.d.ts +12 -0
  13. package/lib/common/crypto/NodeCryptoProvider.js +73 -0
  14. package/lib/common/crypto/SignatureProvider.d.ts +13 -0
  15. package/lib/common/crypto/SignatureProvider.js +53 -0
  16. package/lib/common/crypto/SignatureProvider.spec.d.ts +1 -0
  17. package/lib/common/crypto/SignatureProvider.spec.js +66 -0
  18. package/lib/common/crypto/SubtleCryptoProvider.d.ts +15 -0
  19. package/lib/common/crypto/SubtleCryptoProvider.js +75 -0
  20. package/lib/common/crypto/index.d.ts +4 -0
  21. package/lib/common/crypto/index.js +20 -0
  22. package/lib/common/net/fetch-client.spec.d.ts +1 -0
  23. package/lib/common/net/fetch-client.spec.js +140 -0
  24. package/lib/common/net/index.d.ts +5 -0
  25. package/lib/common/net/index.js +31 -0
  26. package/lib/common/net/node-client.js +1 -0
  27. package/lib/common/net/node-client.spec.d.ts +1 -0
  28. package/lib/common/net/node-client.spec.js +140 -0
  29. package/lib/common/utils/unreachable.d.ts +10 -0
  30. package/lib/common/utils/unreachable.js +18 -0
  31. package/lib/fga/fga.spec.js +0 -609
  32. package/lib/index.d.ts +3 -0
  33. package/lib/index.js +12 -0
  34. package/lib/index.worker.d.ts +3 -0
  35. package/lib/index.worker.js +6 -0
  36. package/lib/organizations/fixtures/clear-stripe-customer-id.json +13 -0
  37. package/lib/organizations/fixtures/set-stripe-customer-id-disabled.json +4 -0
  38. package/lib/organizations/fixtures/set-stripe-customer-id.json +14 -0
  39. package/lib/organizations/interfaces/organization.interface.d.ts +2 -0
  40. package/lib/organizations/interfaces/update-organization-options.interface.d.ts +2 -0
  41. package/lib/organizations/organizations.spec.js +39 -0
  42. package/lib/organizations/serializers/organization.serializer.js +3 -9
  43. package/lib/organizations/serializers/update-organization-options.serializer.js +1 -0
  44. package/lib/user-management/interfaces/authenticate-with-session-cookie.interface.d.ts +2 -0
  45. package/lib/user-management/session.js +2 -1
  46. package/lib/user-management/session.spec.js +2 -1
  47. package/lib/user-management/user-management.js +2 -1
  48. package/lib/user-management/user-management.spec.js +2 -1
  49. package/lib/webhooks/webhooks.d.ts +9 -9
  50. package/lib/webhooks/webhooks.js +11 -36
  51. package/lib/webhooks/webhooks.spec.js +23 -46
  52. package/lib/widgets/fixtures/get-token-error.json +5 -0
  53. package/lib/widgets/fixtures/token.json +3 -0
  54. package/lib/widgets/interfaces/get-token.d.ts +19 -0
  55. package/lib/widgets/interfaces/get-token.js +13 -0
  56. package/lib/widgets/widgets.d.ts +7 -0
  57. package/lib/widgets/widgets.js +25 -0
  58. package/lib/widgets/widgets.spec.d.ts +1 -0
  59. package/lib/widgets/widgets.spec.js +49 -0
  60. package/lib/workos.d.ts +5 -0
  61. package/lib/workos.js +9 -2
  62. package/lib/workos.spec.js +5 -1
  63. package/package.json +2 -1
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
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "Test Organization 3",
3
+ "object": "organization",
4
+ "id": "org_01EHT88Z8J8795GZNQ4ZP1J81T",
5
+ "allow_profiles_outside_organization": false,
6
+ "domains": [
7
+ {
8
+ "domain": "example.com",
9
+ "object": "organization_domain",
10
+ "id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "message": "stripe_customer_id is not enabled for this environment",
3
+ "error": "Unprocessable Entity"
4
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "Test Organization 3",
3
+ "object": "organization",
4
+ "id": "org_01EHT88Z8J8795GZNQ4ZP1J81T",
5
+ "allow_profiles_outside_organization": false,
6
+ "domains": [
7
+ {
8
+ "domain": "example.com",
9
+ "object": "organization_domain",
10
+ "id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
11
+ }
12
+ ],
13
+ "stripe_customer_id": "cus_MX8J9nfK4lP2Yw"
14
+ }
@@ -5,6 +5,7 @@ export interface Organization {
5
5
  name: string;
6
6
  allowProfilesOutsideOrganization: boolean;
7
7
  domains: OrganizationDomain[];
8
+ stripeCustomerId?: string;
8
9
  createdAt: string;
9
10
  updatedAt: string;
10
11
  }
@@ -14,6 +15,7 @@ export interface OrganizationResponse {
14
15
  name: string;
15
16
  allow_profiles_outside_organization: boolean;
16
17
  domains: OrganizationDomainResponse[];
18
+ stripe_customer_id?: string;
17
19
  created_at: string;
18
20
  updated_at: string;
19
21
  }
@@ -3,6 +3,7 @@ export interface UpdateOrganizationOptions {
3
3
  organization: string;
4
4
  name?: string;
5
5
  domainData?: DomainData[];
6
+ stripeCustomerId?: string | null;
6
7
  /**
7
8
  * @deprecated If you need to allow sign-ins from any email domain, contact support@workos.com.
8
9
  */
@@ -15,6 +16,7 @@ export interface UpdateOrganizationOptions {
15
16
  export interface SerializedUpdateOrganizationOptions {
16
17
  name?: string;
17
18
  domain_data?: DomainData[];
19
+ stripe_customer_id?: string | null;
18
20
  /**
19
21
  * @deprecated If you need to allow sign-ins from any email domain, contact support@workos.com.
20
22
  */
@@ -15,11 +15,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const jest_fetch_mock_1 = __importDefault(require("jest-fetch-mock"));
16
16
  const test_utils_1 = require("../common/utils/test-utils");
17
17
  const workos_1 = require("../workos");
18
+ const clear_stripe_customer_id_json_1 = __importDefault(require("./fixtures/clear-stripe-customer-id.json"));
18
19
  const create_organization_invalid_json_1 = __importDefault(require("./fixtures/create-organization-invalid.json"));
19
20
  const create_organization_json_1 = __importDefault(require("./fixtures/create-organization.json"));
20
21
  const get_organization_json_1 = __importDefault(require("./fixtures/get-organization.json"));
21
22
  const list_organizations_json_1 = __importDefault(require("./fixtures/list-organizations.json"));
22
23
  const update_organization_json_1 = __importDefault(require("./fixtures/update-organization.json"));
24
+ const set_stripe_customer_id_json_1 = __importDefault(require("./fixtures/set-stripe-customer-id.json"));
25
+ const set_stripe_customer_id_disabled_json_1 = __importDefault(require("./fixtures/set-stripe-customer-id-disabled.json"));
23
26
  const interfaces_1 = require("./interfaces");
24
27
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
25
28
  describe('Organizations', () => {
@@ -235,5 +238,41 @@ describe('Organizations', () => {
235
238
  }));
236
239
  });
237
240
  });
241
+ describe('when given `stripeCustomerId`', () => {
242
+ it('updates the organization’s Stripe customer ID', () => __awaiter(void 0, void 0, void 0, function* () {
243
+ (0, test_utils_1.fetchOnce)(set_stripe_customer_id_json_1.default);
244
+ const subject = yield workos.organizations.updateOrganization({
245
+ organization: 'org_01EHT88Z8J8795GZNQ4ZP1J81T',
246
+ stripeCustomerId: 'cus_MX8J9nfK4lP2Yw',
247
+ });
248
+ expect((0, test_utils_1.fetchBody)()).toMatchObject({
249
+ stripe_customer_id: 'cus_MX8J9nfK4lP2Yw',
250
+ });
251
+ expect(subject.stripeCustomerId).toBe('cus_MX8J9nfK4lP2Yw');
252
+ }));
253
+ it('clears the organization’s Stripe customer ID with a `null` value', () => __awaiter(void 0, void 0, void 0, function* () {
254
+ (0, test_utils_1.fetchOnce)(clear_stripe_customer_id_json_1.default);
255
+ const subject = yield workos.organizations.updateOrganization({
256
+ organization: 'org_01EHT88Z8J8795GZNQ4ZP1J81T',
257
+ stripeCustomerId: null,
258
+ });
259
+ expect((0, test_utils_1.fetchBody)()).toEqual({
260
+ stripe_customer_id: null,
261
+ });
262
+ expect(subject.stripeCustomerId).toBeUndefined();
263
+ }));
264
+ describe('when the feature is not enabled', () => {
265
+ it('returns an error', () => __awaiter(void 0, void 0, void 0, function* () {
266
+ (0, test_utils_1.fetchOnce)(set_stripe_customer_id_disabled_json_1.default, { status: 422 });
267
+ yield expect(workos.organizations.updateOrganization({
268
+ organization: 'org_01EHT88Z8J8795GZNQ4ZP1J81T',
269
+ stripeCustomerId: 'cus_MX8J9nfK4lP2Yw',
270
+ })).rejects.toThrowError('stripe_customer_id is not enabled for this environment');
271
+ expect((0, test_utils_1.fetchBody)()).toEqual({
272
+ stripe_customer_id: 'cus_MX8J9nfK4lP2Yw',
273
+ });
274
+ }));
275
+ });
276
+ });
238
277
  });
239
278
  });
@@ -2,13 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.deserializeOrganization = void 0;
4
4
  const organization_domain_serializer_1 = require("../../organization-domains/serializers/organization-domain.serializer");
5
- const deserializeOrganization = (organization) => ({
6
- object: organization.object,
7
- id: organization.id,
8
- name: organization.name,
9
- allowProfilesOutsideOrganization: organization.allow_profiles_outside_organization,
10
- domains: organization.domains.map(organization_domain_serializer_1.deserializeOrganizationDomain),
11
- createdAt: organization.created_at,
12
- updatedAt: organization.updated_at,
13
- });
5
+ const deserializeOrganization = (organization) => (Object.assign(Object.assign({ object: organization.object, id: organization.id, name: organization.name, allowProfilesOutsideOrganization: organization.allow_profiles_outside_organization, domains: organization.domains.map(organization_domain_serializer_1.deserializeOrganizationDomain) }, (typeof organization.stripe_customer_id === 'undefined'
6
+ ? undefined
7
+ : { stripeCustomerId: organization.stripe_customer_id })), { createdAt: organization.created_at, updatedAt: organization.updated_at }));
14
8
  exports.deserializeOrganization = deserializeOrganization;
@@ -6,5 +6,6 @@ const serializeUpdateOrganizationOptions = (options) => ({
6
6
  allow_profiles_outside_organization: options.allowProfilesOutsideOrganization,
7
7
  domain_data: options.domainData,
8
8
  domains: options.domains,
9
+ stripe_customer_id: options.stripeCustomerId,
9
10
  });
10
11
  exports.serializeUpdateOrganizationOptions = serializeUpdateOrganizationOptions;
@@ -10,6 +10,7 @@ export interface AccessToken {
10
10
  org_id?: string;
11
11
  role?: string;
12
12
  permissions?: string[];
13
+ entitlements?: string[];
13
14
  }
14
15
  export type SessionCookieData = Pick<AuthenticationResponse, 'accessToken' | 'impersonator' | 'organizationId' | 'refreshToken' | 'user'>;
15
16
  export declare enum AuthenticateWithSessionCookieFailureReason {
@@ -27,6 +28,7 @@ export type AuthenticateWithSessionCookieSuccessResponse = {
27
28
  organizationId?: string;
28
29
  role?: string;
29
30
  permissions?: string[];
31
+ entitlements?: string[];
30
32
  user: User;
31
33
  impersonator?: Impersonator;
32
34
  };
@@ -64,13 +64,14 @@ class Session {
64
64
  reason: interfaces_1.AuthenticateWithSessionCookieFailureReason.INVALID_JWT,
65
65
  };
66
66
  }
67
- const { sid: sessionId, org_id: organizationId, role, permissions, } = (0, jose_1.decodeJwt)(session.accessToken);
67
+ const { sid: sessionId, org_id: organizationId, role, permissions, entitlements, } = (0, jose_1.decodeJwt)(session.accessToken);
68
68
  return {
69
69
  authenticated: true,
70
70
  sessionId,
71
71
  organizationId,
72
72
  role,
73
73
  permissions,
74
+ entitlements,
74
75
  user: session.user,
75
76
  impersonator: session.impersonator,
76
77
  };
@@ -120,7 +120,7 @@ describe('Session', () => {
120
120
  .mockResolvedValue({});
121
121
  const cookiePassword = 'alongcookiesecretmadefortestingsessions';
122
122
  const sessionData = yield (0, iron_session_1.sealData)({
123
- accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJpYXQiOiAxNTE2MjM5MDIyLAogICJzaWQiOiAic2Vzc2lvbl8xMjMiLAogICJvcmdfaWQiOiAib3JnXzEyMyIsCiAgInJvbGUiOiAibWVtYmVyIiwKICAicGVybWlzc2lvbnMiOiBbInBvc3RzOmNyZWF0ZSIsICJwb3N0czpkZWxldGUiXQp9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
123
+ accessToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkIjp0cnVlLCJpbXBlcnNvbmF0b3IiOnsiZW1haWwiOiJhZG1pbkBleGFtcGxlLmNvbSIsInJlYXNvbiI6InRlc3QifSwic2lkIjoic2Vzc2lvbl8xMjMiLCJvcmdfaWQiOiJvcmdfMTIzIiwicm9sZSI6Im1lbWJlciIsInBlcm1pc3Npb25zIjpbInBvc3RzOmNyZWF0ZSIsInBvc3RzOmRlbGV0ZSJdLCJlbnRpdGxlbWVudHMiOlsiYXVkaXQtbG9ncyJdLCJ1c2VyIjp7Im9iamVjdCI6InVzZXIiLCJpZCI6InVzZXJfMDFINUpRRFY3UjdBVEVZWkRFRzBXNVBSWVMiLCJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifX0.A8mDST4wtq_0vId6ALg7k2Ukr7FXrszZtdJ_6dfXeAc',
124
124
  refreshToken: 'def456',
125
125
  impersonator: {
126
126
  email: 'admin@example.com',
@@ -146,6 +146,7 @@ describe('Session', () => {
146
146
  organizationId: 'org_123',
147
147
  role: 'member',
148
148
  permissions: ['posts:create', 'posts:delete'],
149
+ entitlements: ['audit-logs'],
149
150
  user: {
150
151
  object: 'user',
151
152
  id: 'user_01H5JQDV7R7ATEYZDEG0W5PRYS',
@@ -192,7 +192,7 @@ class UserManagement {
192
192
  reason: authenticate_with_session_cookie_interface_1.AuthenticateWithSessionCookieFailureReason.INVALID_JWT,
193
193
  };
194
194
  }
195
- const { sid: sessionId, org_id: organizationId, role, permissions, } = (0, jose_1.decodeJwt)(session.accessToken);
195
+ const { sid: sessionId, org_id: organizationId, role, permissions, entitlements, } = (0, jose_1.decodeJwt)(session.accessToken);
196
196
  return {
197
197
  authenticated: true,
198
198
  sessionId,
@@ -200,6 +200,7 @@ class UserManagement {
200
200
  role,
201
201
  user: session.user,
202
202
  permissions,
203
+ entitlements,
203
204
  };
204
205
  });
205
206
  }
@@ -720,7 +720,7 @@ describe('UserManagement', () => {
720
720
  .mockResolvedValue({});
721
721
  const cookiePassword = 'alongcookiesecretmadefortestingsessions';
722
722
  const sessionData = yield (0, iron_session_1.sealData)({
723
- accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJpYXQiOiAxNTE2MjM5MDIyLAogICJzaWQiOiAic2Vzc2lvbl8xMjMiLAogICJvcmdfaWQiOiAib3JnXzEyMyIsCiAgInJvbGUiOiAibWVtYmVyIiwKICAicGVybWlzc2lvbnMiOiBbInBvc3RzOmNyZWF0ZSIsICJwb3N0czpkZWxldGUiXQp9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
723
+ accessToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkIjp0cnVlLCJpbXBlcnNvbmF0b3IiOnsiZW1haWwiOiJhZG1pbkBleGFtcGxlLmNvbSIsInJlYXNvbiI6InRlc3QifSwic2lkIjoic2Vzc2lvbl8xMjMiLCJvcmdfaWQiOiJvcmdfMTIzIiwicm9sZSI6Im1lbWJlciIsInBlcm1pc3Npb25zIjpbInBvc3RzOmNyZWF0ZSIsInBvc3RzOmRlbGV0ZSJdLCJlbnRpdGxlbWVudHMiOlsiYXVkaXQtbG9ncyJdLCJ1c2VyIjp7Im9iamVjdCI6InVzZXIiLCJpZCI6InVzZXJfMDFINUpRRFY3UjdBVEVZWkRFRzBXNVBSWVMiLCJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifX0.A8mDST4wtq_0vId6ALg7k2Ukr7FXrszZtdJ_6dfXeAc',
724
724
  refreshToken: 'def456',
725
725
  user: {
726
726
  object: 'user',
@@ -737,6 +737,7 @@ describe('UserManagement', () => {
737
737
  organizationId: 'org_123',
738
738
  role: 'member',
739
739
  permissions: ['posts:create', 'posts:delete'],
740
+ entitlements: ['audit-logs'],
740
741
  user: expect.objectContaining({
741
742
  email: 'test@example.com',
742
743
  }),
@@ -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
  });
@@ -0,0 +1,5 @@
1
+ {
2
+ "message": "User not found 'user_123'",
3
+ "code": "entity_not_found",
4
+ "entity_id": "user_123"
5
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "token": "this.is.a.token"
3
+ }
@@ -0,0 +1,19 @@
1
+ export type WidgetScope = 'widgets:users-table:manage';
2
+ export interface GetTokenOptions {
3
+ organizationId: string;
4
+ userId: string;
5
+ scopes: [WidgetScope];
6
+ }
7
+ export interface SerializedGetTokenOptions {
8
+ organization_id: string;
9
+ user_id: string;
10
+ scopes: [WidgetScope];
11
+ }
12
+ export declare const serializeGetTokenOptions: (options: GetTokenOptions) => SerializedGetTokenOptions;
13
+ export interface GetTokenResponse {
14
+ token: string;
15
+ }
16
+ export interface GetTokenResponseResponse {
17
+ token: string;
18
+ }
19
+ export declare const deserializeGetTokenResponse: (data: GetTokenResponseResponse) => GetTokenResponse;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deserializeGetTokenResponse = exports.serializeGetTokenOptions = void 0;
4
+ const serializeGetTokenOptions = (options) => ({
5
+ organization_id: options.organizationId,
6
+ user_id: options.userId,
7
+ scopes: options.scopes,
8
+ });
9
+ exports.serializeGetTokenOptions = serializeGetTokenOptions;
10
+ const deserializeGetTokenResponse = (data) => ({
11
+ token: data.token,
12
+ });
13
+ exports.deserializeGetTokenResponse = deserializeGetTokenResponse;
@@ -0,0 +1,7 @@
1
+ import { WorkOS } from '../workos';
2
+ import { GetTokenOptions } from './interfaces/get-token';
3
+ export declare class Widgets {
4
+ private readonly workos;
5
+ constructor(workos: WorkOS);
6
+ getToken(payload: GetTokenOptions): Promise<string>;
7
+ }
@@ -0,0 +1,25 @@
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.Widgets = void 0;
13
+ const get_token_1 = require("./interfaces/get-token");
14
+ class Widgets {
15
+ constructor(workos) {
16
+ this.workos = workos;
17
+ }
18
+ getToken(payload) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const { data } = yield this.workos.post('/widgets/token', (0, get_token_1.serializeGetTokenOptions)(payload));
21
+ return (0, get_token_1.deserializeGetTokenResponse)(data).token;
22
+ });
23
+ }
24
+ }
25
+ exports.Widgets = Widgets;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
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 jest_fetch_mock_1 = __importDefault(require("jest-fetch-mock"));
16
+ const workos_1 = require("../workos");
17
+ const test_utils_1 = require("../common/utils/test-utils");
18
+ const token_json_1 = __importDefault(require("./fixtures/token.json"));
19
+ const get_token_error_json_1 = __importDefault(require("./fixtures/get-token-error.json"));
20
+ describe('Widgets', () => {
21
+ let workos;
22
+ beforeAll(() => {
23
+ workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
24
+ apiHostname: 'api.workos.test',
25
+ clientId: 'proj_123',
26
+ });
27
+ });
28
+ beforeEach(() => jest_fetch_mock_1.default.resetMocks());
29
+ describe('getToken', () => {
30
+ it('sends a Get Token request', () => __awaiter(void 0, void 0, void 0, function* () {
31
+ (0, test_utils_1.fetchOnce)(token_json_1.default);
32
+ const token = yield workos.widgets.getToken({
33
+ organizationId: 'org_123',
34
+ userId: 'user_123',
35
+ scopes: ['widgets:users-table:manage'],
36
+ });
37
+ expect((0, test_utils_1.fetchURL)()).toContain('/widgets/token');
38
+ expect(token).toEqual('this.is.a.token');
39
+ }));
40
+ it('returns an error if the API returns an error', () => __awaiter(void 0, void 0, void 0, function* () {
41
+ (0, test_utils_1.fetchOnce)(get_token_error_json_1.default, { status: 404 });
42
+ yield expect(workos.widgets.getToken({
43
+ organizationId: 'org_123',
44
+ userId: 'user_123',
45
+ scopes: ['widgets:users-table:manage'],
46
+ })).rejects.toThrow("User not found 'user_123'");
47
+ }));
48
+ });
49
+ });