@workos-inc/node 2.5.1 → 2.7.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.
Files changed (30) hide show
  1. package/README.md +5 -1
  2. package/lib/audit-trail/audit-trail.spec.js +15 -6
  3. package/lib/audit-trail/interfaces/event.interface.d.ts +0 -1
  4. package/lib/common/exceptions/unprocessable-entity.exception.d.ts +8 -2
  5. package/lib/common/exceptions/unprocessable-entity.exception.js +15 -6
  6. package/lib/common/interfaces/pagination-options.interface.d.ts +1 -0
  7. package/lib/directory-sync/directory-sync.spec.js +113 -50
  8. package/lib/directory-sync/interfaces/directory.interface.d.ts +0 -1
  9. package/lib/directory-sync/interfaces/group.interface.d.ts +3 -0
  10. package/lib/directory-sync/interfaces/user.interface.d.ts +2 -2
  11. package/lib/mfa/interfaces/challenge.interface.d.ts +1 -1
  12. package/lib/mfa/interfaces/factor.interface.d.ts +0 -1
  13. package/lib/mfa/interfaces/verify-factor-response.d.ts +1 -6
  14. package/lib/mfa/mfa.spec.js +93 -6
  15. package/lib/organizations/organizations.spec.js +45 -10
  16. package/lib/passwordless/passwordless.spec.js +9 -3
  17. package/lib/portal/portal.spec.js +21 -3
  18. package/lib/sso/interfaces/connection-type.enum.d.ts +18 -0
  19. package/lib/sso/interfaces/connection-type.enum.js +18 -0
  20. package/lib/sso/sso.spec.js +42 -21
  21. package/lib/webhooks/interfaces/index.d.ts +2 -0
  22. package/lib/webhooks/interfaces/index.js +2 -0
  23. package/lib/webhooks/interfaces/webhook-directory-group.interface.d.ts +9 -0
  24. package/lib/webhooks/interfaces/webhook-directory-group.interface.js +2 -0
  25. package/lib/webhooks/interfaces/webhook-directory-user.interface.d.ts +5 -0
  26. package/lib/webhooks/interfaces/webhook-directory-user.interface.js +2 -0
  27. package/lib/webhooks/interfaces/webhook-directory.interface.d.ts +19 -3
  28. package/lib/webhooks/interfaces/webhook.interface.d.ts +7 -6
  29. package/lib/workos.js +29 -9
  30. package/package.json +9 -9
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # WorkOS Node.js Library
2
2
 
3
3
  ![npm](https://img.shields.io/npm/v/@workos-inc/node)
4
- [![Build Status](https://workos.semaphoreci.com/badges/workos-node/branches/master.svg?style=shields)](https://workos.semaphoreci.com/projects/workos-node)
4
+ [![Build Status](https://workos.semaphoreci.com/badges/workos-node/branches/main.svg?style=shields)](https://workos.semaphoreci.com/projects/workos-node)
5
5
 
6
6
  The WorkOS library for Node.js provides convenient access to the WorkOS API from applications written in server-side JavaScript.
7
7
 
@@ -33,6 +33,10 @@ import WorkOS from '@workos-inc/node';
33
33
  const workos = new WorkOS('sk_1234');
34
34
  ```
35
35
 
36
+ ## SDK Versioning
37
+
38
+ For our SDKs WorkOS follows a Semantic Versioning ([SemVer](https://semver.org/)) process where all releases will have a version X.Y.Z (like 1.0.0) pattern wherein Z would be a bug fix (e.g., 1.0.1), Y would be a minor release (1.1.0) and X would be a major release (2.0.0). We permit any breaking changes to only be released in major versions and strongly recommend reading changelogs before making any major version upgrades.
39
+
36
40
  ## More Information
37
41
 
38
42
  - [Single Sign-On Guide](https://workos.com/docs/sso/guide)
@@ -28,12 +28,15 @@ const event = {
28
28
  action_type: 'U',
29
29
  action: 'document.updated',
30
30
  };
31
+ const serializeEventOptions = (options) => (Object.assign(Object.assign({}, options), { occurred_at: options.occurred_at.toISOString() }));
31
32
  describe('AuditTrail', () => {
32
33
  describe('createEvent', () => {
33
34
  describe('when the api responds with a 201 CREATED', () => {
34
35
  describe('with an idempotency key', () => {
35
36
  it('includes an idempotency key with request', () => __awaiter(void 0, void 0, void 0, function* () {
36
- mock.onPost().reply(201, { success: true });
37
+ mock
38
+ .onPost('/events', serializeEventOptions(event))
39
+ .replyOnce(201, { success: true });
37
40
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
38
41
  yield expect(workos.auditTrail.createEvent(event, {
39
42
  idempotencyKey: 'the-idempotency-key',
@@ -42,14 +45,16 @@ describe('AuditTrail', () => {
42
45
  }));
43
46
  });
44
47
  it('posts Event successfully', () => __awaiter(void 0, void 0, void 0, function* () {
45
- mock.onPost().reply(201, { success: true });
48
+ mock
49
+ .onPost('/events', serializeEventOptions(event))
50
+ .replyOnce(201, { success: true });
46
51
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
47
52
  yield expect(workos.auditTrail.createEvent(event)).resolves.toBeUndefined();
48
53
  }));
49
54
  });
50
55
  describe('when the api responds with a 401', () => {
51
56
  it('throws an UnauthorizedException', () => __awaiter(void 0, void 0, void 0, function* () {
52
- mock.onPost().reply(401, {
57
+ mock.onPost('/events', serializeEventOptions(event)).replyOnce(401, {
53
58
  message: 'Unauthorized',
54
59
  }, { 'X-Request-ID': 'a-request-id' });
55
60
  const workos = new workos_1.WorkOS('invalid apikey');
@@ -68,7 +73,7 @@ describe('AuditTrail', () => {
68
73
  code: 'occurred_at must be an ISO 8601 date string',
69
74
  },
70
75
  ];
71
- mock.onPost().reply(422, {
76
+ mock.onPost('/events', serializeEventOptions(event)).replyOnce(422, {
72
77
  message: 'Validation failed',
73
78
  errors,
74
79
  }, { 'X-Request-ID': 'a-request-id' });
@@ -80,7 +85,7 @@ describe('AuditTrail', () => {
80
85
  describe('listEvents', () => {
81
86
  describe('With no filters', () => {
82
87
  it('Returns all events', () => __awaiter(void 0, void 0, void 0, function* () {
83
- mock.onGet().reply(200, {
88
+ mock.onGet('/events').replyOnce(200, {
84
89
  data: [
85
90
  {
86
91
  object: 'event',
@@ -136,7 +141,11 @@ describe('AuditTrail', () => {
136
141
  });
137
142
  describe('With a filter', () => {
138
143
  it('Returns events that match the filter', () => __awaiter(void 0, void 0, void 0, function* () {
139
- mock.onGet().reply(200, {
144
+ mock
145
+ .onGet('/events', {
146
+ action: ['user.searched_directories'],
147
+ })
148
+ .replyOnce(200, {
140
149
  data: [
141
150
  {
142
151
  object: 'event',
@@ -15,7 +15,6 @@ export interface Event {
15
15
  actor_id: string;
16
16
  target_name: string;
17
17
  target_id: string;
18
- environment_id: string;
19
18
  occurred_at: Date;
20
19
  action: EventAction;
21
20
  metadata?: {
@@ -1,8 +1,14 @@
1
1
  import { UnprocessableEntityError } from '../interfaces';
2
2
  export declare class UnprocessableEntityException extends Error {
3
- readonly requestID: string;
4
3
  readonly status: number;
5
4
  readonly name: string;
6
5
  readonly message: string;
7
- constructor(errors: UnprocessableEntityError[], requestID: string);
6
+ readonly code?: string;
7
+ readonly requestID: string;
8
+ constructor({ code, errors, message, requestID, }: {
9
+ code?: string;
10
+ errors?: UnprocessableEntityError[];
11
+ message?: string;
12
+ requestID: string;
13
+ });
8
14
  }
@@ -6,15 +6,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.UnprocessableEntityException = void 0;
7
7
  const pluralize_1 = __importDefault(require("pluralize"));
8
8
  class UnprocessableEntityException extends Error {
9
- constructor(errors, requestID) {
9
+ constructor({ code, errors, message, requestID, }) {
10
10
  super();
11
- this.requestID = requestID;
12
11
  this.status = 422;
13
12
  this.name = 'UnprocessableEntityException';
14
- const requirement = (0, pluralize_1.default)('requirement', errors.length);
15
- this.message = `The following ${requirement} must be met:\n`;
16
- for (const { code } of errors) {
17
- this.message = this.message.concat(`\t${code}\n`);
13
+ this.message = 'Unprocessable entity';
14
+ this.requestID = requestID;
15
+ if (message) {
16
+ this.message = message;
17
+ }
18
+ if (code) {
19
+ this.code = code;
20
+ }
21
+ if (errors) {
22
+ const requirement = (0, pluralize_1.default)('requirement', errors.length);
23
+ this.message = `The following ${requirement} must be met:\n`;
24
+ for (const { code } of errors) {
25
+ this.message = this.message.concat(`\t${code}\n`);
26
+ }
18
27
  }
19
28
  }
20
29
  }
@@ -2,4 +2,5 @@ export interface PaginationOptions {
2
2
  limit?: number;
3
3
  before?: string;
4
4
  after?: string;
5
+ order?: 'asc' | 'desc';
5
6
  }
@@ -18,81 +18,152 @@ const workos_1 = require("../workos");
18
18
  const mock = new axios_mock_adapter_1.default(axios_1.default);
19
19
  describe('DirectorySync', () => {
20
20
  afterEach(() => mock.resetHistory());
21
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
22
+ const directoryResponse = {
23
+ id: 'directory_123',
24
+ created_at: '2020-05-06 04:21:48.649164',
25
+ domain: 'foo-corp.com',
26
+ external_key: '9asBRBVHz2ASEkgg',
27
+ name: 'Foo',
28
+ object: 'directory',
29
+ organization_id: 'org_01EXSR7M9QTKCC5D531SMCWMYG',
30
+ state: 'linked',
31
+ type: 'okta scim v1.1',
32
+ updated_at: '2021-10-27 15:21:50.640958',
33
+ };
34
+ const groupResponse = {
35
+ id: 'dir_grp_123',
36
+ idp_id: '123',
37
+ directory_id: 'dir_123',
38
+ name: 'Foo Group',
39
+ created_at: `2021-10-27 15:21:50.640958`,
40
+ updated_at: '2021-10-27 15:21:50.640959',
41
+ raw_attributes: {
42
+ foo: 'bar',
43
+ },
44
+ };
45
+ const userWithGroupResponse = {
46
+ id: 'user_123',
47
+ custom_attributes: {
48
+ custom: true,
49
+ },
50
+ directory_id: 'dir_123',
51
+ emails: [
52
+ {
53
+ primary: true,
54
+ type: 'type',
55
+ value: 'jonsnow@workos.com',
56
+ },
57
+ ],
58
+ first_name: 'Jon',
59
+ groups: [groupResponse],
60
+ idp_id: 'idp_foo',
61
+ last_name: 'Snow',
62
+ raw_attributes: {},
63
+ state: 'active',
64
+ username: 'jonsnow',
65
+ };
21
66
  describe('listDirectories', () => {
22
67
  describe('with options', () => {
23
68
  it('requests Directories with query parameters', () => __awaiter(void 0, void 0, void 0, function* () {
24
- mock.onGet().reply(200, {});
25
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
26
- yield workos.directorySync.listDirectories({
69
+ const directoryListResponse = {
70
+ object: 'list',
71
+ data: [directoryResponse],
72
+ list_metadata: {},
73
+ };
74
+ mock
75
+ .onGet('/directories', {
27
76
  domain: 'google.com',
28
- });
29
- expect(mock.history.get[0].params).toEqual({
77
+ })
78
+ .replyOnce(200, directoryListResponse);
79
+ const directories = yield workos.directorySync.listDirectories({
30
80
  domain: 'google.com',
31
81
  });
32
- expect(mock.history.get[0].url).toEqual('/directories');
82
+ expect(directories).toEqual(directoryListResponse);
33
83
  }));
34
84
  });
35
85
  });
36
86
  describe('getDirectory', () => {
37
87
  it(`requests a Directory`, () => __awaiter(void 0, void 0, void 0, function* () {
38
- mock.onGet().reply(200, {});
39
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
40
- yield workos.directorySync.getDirectory('directory_123');
41
- expect(mock.history.get[0].url).toEqual('/directories/directory_123');
88
+ mock
89
+ .onGet('/directories/directory_123')
90
+ .replyOnce(200, directoryResponse);
91
+ const directory = yield workos.directorySync.getDirectory('directory_123');
92
+ expect(directory).toEqual(directoryResponse);
42
93
  }));
43
94
  });
44
95
  describe('deleteDirectory', () => {
45
96
  it('sends a request to delete the directory', () => __awaiter(void 0, void 0, void 0, function* () {
46
- mock.onDelete().reply(202, {});
47
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
97
+ mock.onDelete('/directories/directory_123').replyOnce(202, {});
48
98
  yield workos.directorySync.deleteDirectory('directory_123');
49
99
  expect(mock.history.delete[0].url).toEqual('/directories/directory_123');
50
100
  }));
51
101
  });
102
+ describe('getGroup', () => {
103
+ it(`requests a Directory Group`, () => __awaiter(void 0, void 0, void 0, function* () {
104
+ mock.onGet('/directory_groups/dir_grp_123').replyOnce(200, groupResponse);
105
+ const group = yield workos.directorySync.getGroup('dir_grp_123');
106
+ expect(group).toEqual(groupResponse);
107
+ }));
108
+ });
52
109
  describe('listGroups', () => {
110
+ const groupListResponse = {
111
+ object: 'list',
112
+ data: [groupResponse],
113
+ list_metadata: {},
114
+ };
53
115
  describe('with a Directory', () => {
54
116
  it(`requests a Directory's Groups`, () => __awaiter(void 0, void 0, void 0, function* () {
55
- mock.onGet().reply(200, {});
56
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
57
- yield workos.directorySync.listGroups({
117
+ mock
118
+ .onGet('/directory_groups', {
58
119
  directory: 'directory_123',
59
- });
60
- expect(mock.history.get[0].params).toEqual({
120
+ })
121
+ .replyOnce(200, groupListResponse);
122
+ const list = yield workos.directorySync.listGroups({
61
123
  directory: 'directory_123',
62
124
  });
63
- expect(mock.history.get[0].url).toEqual('/directory_groups');
125
+ expect(list).toEqual(groupListResponse);
64
126
  }));
65
127
  });
66
128
  describe('with a User', () => {
67
129
  it(`requests a Directory's Groups`, () => __awaiter(void 0, void 0, void 0, function* () {
68
- mock.onGet().reply(200, {});
69
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
70
- yield workos.directorySync.listGroups({
130
+ mock
131
+ .onGet('/directory_groups', {
71
132
  user: 'directory_usr_123',
72
- });
73
- expect(mock.history.get[0].params).toEqual({
133
+ })
134
+ .replyOnce(200, groupListResponse);
135
+ const list = yield workos.directorySync.listGroups({
74
136
  user: 'directory_usr_123',
75
137
  });
76
- expect(mock.history.get[0].url).toEqual('/directory_groups');
138
+ expect(list).toEqual(groupListResponse);
77
139
  }));
78
140
  });
79
141
  });
80
142
  describe('listUsers', () => {
143
+ const userWithGroupListResponse = {
144
+ object: 'list',
145
+ data: [userWithGroupResponse],
146
+ list_metadata: {},
147
+ };
81
148
  describe('with a Directory', () => {
82
149
  it(`requests a Directory's Users`, () => __awaiter(void 0, void 0, void 0, function* () {
83
- mock.onGet().reply(200, {});
84
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
85
- yield workos.directorySync.listUsers({
150
+ mock
151
+ .onGet('/directory_users', {
86
152
  directory: 'directory_123',
87
- });
88
- expect(mock.history.get[0].params).toEqual({
153
+ })
154
+ .replyOnce(200, userWithGroupListResponse);
155
+ const list = yield workos.directorySync.listUsers({
89
156
  directory: 'directory_123',
90
157
  });
91
- expect(mock.history.get[0].url).toEqual('/directory_users');
158
+ expect(list).toEqual(userWithGroupListResponse);
92
159
  }));
93
160
  describe('with custom attributes', () => {
94
161
  it('returns the custom attributes, using the provided type', () => __awaiter(void 0, void 0, void 0, function* () {
95
- mock.onGet().reply(200, {
162
+ mock
163
+ .onGet('/directory_users', {
164
+ directory: 'directory_123',
165
+ })
166
+ .replyOnce(200, {
96
167
  data: [
97
168
  {
98
169
  object: 'directory_user',
@@ -144,7 +215,6 @@ describe('DirectorySync', () => {
144
215
  },
145
216
  ],
146
217
  });
147
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
148
218
  const users = yield workos.directorySync.listUsers({
149
219
  directory: 'directory_123',
150
220
  });
@@ -158,32 +228,25 @@ describe('DirectorySync', () => {
158
228
  });
159
229
  describe('with a Group', () => {
160
230
  it(`requests a Directory's Users`, () => __awaiter(void 0, void 0, void 0, function* () {
161
- mock.onGet().reply(200, {});
162
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
163
- yield workos.directorySync.listUsers({
231
+ mock
232
+ .onGet('/directory_users', {
164
233
  group: 'directory_grp_123',
165
- });
166
- expect(mock.history.get[0].params).toEqual({
234
+ })
235
+ .replyOnce(200, userWithGroupListResponse);
236
+ const list = yield workos.directorySync.listUsers({
167
237
  group: 'directory_grp_123',
168
238
  });
169
- expect(mock.history.get[0].url).toEqual('/directory_users');
239
+ expect(list).toEqual(userWithGroupListResponse);
170
240
  }));
171
241
  });
172
242
  });
173
243
  describe('getUser', () => {
174
244
  it(`requests a Directory User`, () => __awaiter(void 0, void 0, void 0, function* () {
175
- mock.onGet().reply(200, {});
176
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
177
- yield workos.directorySync.getUser('dir_usr_123');
178
- expect(mock.history.get[0].url).toEqual('/directory_users/dir_usr_123');
179
- }));
180
- });
181
- describe('getGroup', () => {
182
- it(`requests a Directory Group`, () => __awaiter(void 0, void 0, void 0, function* () {
183
- mock.onGet().reply(200, {});
184
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
185
- yield workos.directorySync.getGroup('dir_grp_123');
186
- expect(mock.history.get[0].url).toEqual('/directory_groups/dir_grp_123');
245
+ mock
246
+ .onGet('/directory_users/dir_usr_123')
247
+ .replyOnce(200, userWithGroupResponse);
248
+ const user = yield workos.directorySync.getUser('dir_usr_123');
249
+ expect(user).toEqual(userWithGroupResponse);
187
250
  }));
188
251
  });
189
252
  });
@@ -4,7 +4,6 @@ export interface Directory {
4
4
  domain: string;
5
5
  external_key: string;
6
6
  name: string;
7
- environment_id: string;
8
7
  organization_id?: string;
9
8
  state: 'unlinked' | 'linked' | 'invalid_credentials';
10
9
  type: string;
@@ -1,6 +1,9 @@
1
1
  export interface Group {
2
2
  id: string;
3
+ idp_id: string;
3
4
  directory_id: string;
4
5
  name: string;
6
+ created_at: string;
7
+ updated_at: string;
5
8
  raw_attributes: any;
6
9
  }
@@ -1,9 +1,9 @@
1
1
  import { Group } from './group.interface';
2
2
  export declare type DefaultCustomAttributes = Record<string, unknown>;
3
- export interface User<TCustomAttributes extends object = DefaultCustomAttributes> {
3
+ export interface User<TCustomAttributes extends object = DefaultCustomAttributes, TRawAttributes = any> {
4
4
  id: string;
5
5
  directory_id: string;
6
- raw_attributes: any;
6
+ raw_attributes: TRawAttributes;
7
7
  custom_attributes: TCustomAttributes;
8
8
  idp_id: string;
9
9
  first_name: string;
@@ -3,7 +3,7 @@ export interface Challenge {
3
3
  id: string;
4
4
  created_at: string;
5
5
  updated_at: string;
6
- expires_at: string;
6
+ expires_at?: string;
7
7
  code: string;
8
8
  authentication_factor_id: string;
9
9
  }
@@ -6,7 +6,6 @@ export interface Factor {
6
6
  created_at: string;
7
7
  updated_at: string;
8
8
  type: string;
9
- environment_id: string;
10
9
  sms: Sms;
11
10
  totp: Totp;
12
11
  }
@@ -1,10 +1,5 @@
1
1
  import { Challenge } from './challenge.interface';
2
- export interface VerifyResponseSuccess {
2
+ export interface VerifyResponse {
3
3
  challenge: Challenge;
4
4
  valid: boolean;
5
5
  }
6
- export interface VerifyResponseError {
7
- code: string;
8
- message: string;
9
- }
10
- export declare type VerifyResponse = VerifyResponseSuccess | VerifyResponseError;
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const axios_1 = __importDefault(require("axios"));
16
16
  const axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
17
+ const exceptions_1 = require("../common/exceptions");
17
18
  const workos_1 = require("../workos");
18
19
  describe('MFA', () => {
19
20
  describe('getFactor', () => {
@@ -44,7 +45,6 @@ describe('MFA', () => {
44
45
  created_at: '2022-03-15T20:39:19.892Z',
45
46
  updated_at: '2022-03-15T20:39:19.892Z',
46
47
  type: 'generic_otp',
47
- environment_id: 'environment_1234',
48
48
  });
49
49
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
50
50
  apiHostname: 'api.workos.dev',
@@ -55,7 +55,6 @@ describe('MFA', () => {
55
55
  expect(enrollResponse).toMatchInlineSnapshot(`
56
56
  Object {
57
57
  "created_at": "2022-03-15T20:39:19.892Z",
58
- "environment_id": "environment_1234",
59
58
  "id": "auth_factor_1234",
60
59
  "object": "authentication_factor",
61
60
  "type": "generic_otp",
@@ -73,7 +72,6 @@ describe('MFA', () => {
73
72
  created_at: '2022-03-15T20:39:19.892Z',
74
73
  updated_at: '2022-03-15T20:39:19.892Z',
75
74
  type: 'totp',
76
- environment_id: 'environment_1234',
77
75
  totp: {
78
76
  qr_code: 'qr-code-test',
79
77
  secret: 'secret-test',
@@ -90,7 +88,6 @@ describe('MFA', () => {
90
88
  expect(enrollResponse).toMatchInlineSnapshot(`
91
89
  Object {
92
90
  "created_at": "2022-03-15T20:39:19.892Z",
93
- "environment_id": "environment_1234",
94
91
  "id": "auth_factor_1234",
95
92
  "object": "authentication_factor",
96
93
  "totp": Object {
@@ -112,7 +109,6 @@ describe('MFA', () => {
112
109
  created_at: '2022-03-15T20:39:19.892Z',
113
110
  updated_at: '2022-03-15T20:39:19.892Z',
114
111
  type: 'sms',
115
- environment_id: 'environment_1234',
116
112
  sms: {
117
113
  phone_number: '+15555555555',
118
114
  },
@@ -127,7 +123,6 @@ describe('MFA', () => {
127
123
  expect(enrollResponse).toMatchInlineSnapshot(`
128
124
  Object {
129
125
  "created_at": "2022-03-15T20:39:19.892Z",
130
- "environment_id": "environment_1234",
131
126
  "id": "auth_factor_1234",
132
127
  "object": "authentication_factor",
133
128
  "sms": Object {
@@ -138,6 +133,24 @@ describe('MFA', () => {
138
133
  }
139
134
  `);
140
135
  }));
136
+ describe('when phone number is invalid', () => {
137
+ it('throws an exception', () => __awaiter(void 0, void 0, void 0, function* () {
138
+ const mock = new axios_mock_adapter_1.default(axios_1.default);
139
+ mock.onPost('/auth/factors/enroll').reply(422, {
140
+ message: `Phone number is invalid: 'foo'`,
141
+ code: 'invalid_phone_number',
142
+ }, {
143
+ 'X-Request-ID': 'req_123',
144
+ });
145
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
146
+ apiHostname: 'api.workos.dev',
147
+ });
148
+ yield expect(workos.mfa.enrollFactor({
149
+ type: 'sms',
150
+ phoneNumber: 'foo',
151
+ })).rejects.toThrow(exceptions_1.UnprocessableEntityException);
152
+ }));
153
+ });
141
154
  });
142
155
  });
143
156
  describe('challengeFactor', () => {
@@ -258,5 +271,79 @@ describe('MFA', () => {
258
271
  `);
259
272
  }));
260
273
  });
274
+ describe('when the challenge has been previously verified', () => {
275
+ it('throws an exception', () => __awaiter(void 0, void 0, void 0, function* () {
276
+ const mock = new axios_mock_adapter_1.default(axios_1.default);
277
+ mock
278
+ .onPost('/auth/factors/verify', {
279
+ authentication_challenge_id: 'auth_challenge_1234',
280
+ code: '12345',
281
+ })
282
+ .reply(422, {
283
+ message: `The authentication challenge '12345' has already been verified.`,
284
+ code: 'authentication_challenge_previously_verified',
285
+ }, {
286
+ 'X-Request-ID': 'req_123',
287
+ });
288
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
289
+ apiHostname: 'api.workos.dev',
290
+ });
291
+ yield expect(workos.mfa.verifyFactor({
292
+ authenticationChallengeId: 'auth_challenge_1234',
293
+ code: '12345',
294
+ })).rejects.toThrow(exceptions_1.UnprocessableEntityException);
295
+ }));
296
+ });
297
+ describe('when the challenge has expired', () => {
298
+ it('throws an exception', () => __awaiter(void 0, void 0, void 0, function* () {
299
+ const mock = new axios_mock_adapter_1.default(axios_1.default);
300
+ mock
301
+ .onPost('/auth/factors/verify', {
302
+ authentication_challenge_id: 'auth_challenge_1234',
303
+ code: '12345',
304
+ })
305
+ .reply(422, {
306
+ message: `The authentication challenge '12345' has expired.`,
307
+ code: 'authentication_challenge_expired',
308
+ }, {
309
+ 'X-Request-ID': 'req_123',
310
+ });
311
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
312
+ apiHostname: 'api.workos.dev',
313
+ });
314
+ yield expect(workos.mfa.verifyFactor({
315
+ authenticationChallengeId: 'auth_challenge_1234',
316
+ code: '12345',
317
+ })).rejects.toThrow(exceptions_1.UnprocessableEntityException);
318
+ }));
319
+ it('exception has code', () => __awaiter(void 0, void 0, void 0, function* () {
320
+ const mock = new axios_mock_adapter_1.default(axios_1.default);
321
+ mock
322
+ .onPost('/auth/factors/verify', {
323
+ authentication_challenge_id: 'auth_challenge_1234',
324
+ code: '12345',
325
+ })
326
+ .reply(422, {
327
+ message: `The authentication challenge '12345' has expired.`,
328
+ code: 'authentication_challenge_expired',
329
+ }, {
330
+ 'X-Request-ID': 'req_123',
331
+ });
332
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
333
+ apiHostname: 'api.workos.dev',
334
+ });
335
+ try {
336
+ yield workos.mfa.verifyFactor({
337
+ authenticationChallengeId: 'auth_challenge_1234',
338
+ code: '12345',
339
+ });
340
+ }
341
+ catch (error) {
342
+ expect(error).toMatchObject({
343
+ code: 'authentication_challenge_expired',
344
+ });
345
+ }
346
+ }));
347
+ });
261
348
  });
262
349
  });
@@ -27,7 +27,7 @@ describe('Organizations', () => {
27
27
  describe('listOrganizations', () => {
28
28
  describe('without any options', () => {
29
29
  it('returns organizations and metadata', () => __awaiter(void 0, void 0, void 0, function* () {
30
- mock.onGet().reply(200, list_organizations_json_1.default);
30
+ mock.onGet('/organizations').replyOnce(200, list_organizations_json_1.default);
31
31
  const { data, list_metadata: listMetadata } = yield workos.organizations.listOrganizations();
32
32
  expect(mock.history.get[0].params).toBeUndefined();
33
33
  expect(mock.history.get[0].url).toEqual('/organizations');
@@ -40,7 +40,11 @@ describe('Organizations', () => {
40
40
  });
41
41
  describe('with the domain option', () => {
42
42
  it('forms the proper request to the API', () => __awaiter(void 0, void 0, void 0, function* () {
43
- mock.onGet().reply(200, list_organizations_json_1.default);
43
+ mock
44
+ .onGet('/organizations', {
45
+ domains: ['example.com'],
46
+ })
47
+ .replyOnce(200, list_organizations_json_1.default);
44
48
  const { data } = yield workos.organizations.listOrganizations({
45
49
  domains: ['example.com'],
46
50
  });
@@ -53,7 +57,11 @@ describe('Organizations', () => {
53
57
  });
54
58
  describe('with the before option', () => {
55
59
  it('forms the proper request to the API', () => __awaiter(void 0, void 0, void 0, function* () {
56
- mock.onGet().reply(200, list_organizations_json_1.default);
60
+ mock
61
+ .onGet('/organizations', {
62
+ before: 'before-id',
63
+ })
64
+ .replyOnce(200, list_organizations_json_1.default);
57
65
  const { data } = yield workos.organizations.listOrganizations({
58
66
  before: 'before-id',
59
67
  });
@@ -66,7 +74,11 @@ describe('Organizations', () => {
66
74
  });
67
75
  describe('with the after option', () => {
68
76
  it('forms the proper request to the API', () => __awaiter(void 0, void 0, void 0, function* () {
69
- mock.onGet().reply(200, list_organizations_json_1.default);
77
+ mock
78
+ .onGet('/organizations', {
79
+ after: 'after-id',
80
+ })
81
+ .replyOnce(200, list_organizations_json_1.default);
70
82
  const { data } = yield workos.organizations.listOrganizations({
71
83
  after: 'after-id',
72
84
  });
@@ -79,7 +91,11 @@ describe('Organizations', () => {
79
91
  });
80
92
  describe('with the limit option', () => {
81
93
  it('forms the proper request to the API', () => __awaiter(void 0, void 0, void 0, function* () {
82
- mock.onGet().reply(200, list_organizations_json_1.default);
94
+ mock
95
+ .onGet('/organizations', {
96
+ limit: 10,
97
+ })
98
+ .replyOnce(200, list_organizations_json_1.default);
83
99
  const { data } = yield workos.organizations.listOrganizations({
84
100
  limit: 10,
85
101
  });
@@ -94,7 +110,12 @@ describe('Organizations', () => {
94
110
  describe('createOrganization', () => {
95
111
  describe('with a valid payload', () => {
96
112
  it('creates an organization', () => __awaiter(void 0, void 0, void 0, function* () {
97
- mock.onPost().reply(201, create_organization_json_1.default);
113
+ mock
114
+ .onPost('/organizations', {
115
+ domains: ['example.com'],
116
+ name: 'Test Organization',
117
+ })
118
+ .replyOnce(201, create_organization_json_1.default);
98
119
  const subject = yield workos.organizations.createOrganization({
99
120
  domains: ['example.com'],
100
121
  name: 'Test Organization',
@@ -106,7 +127,12 @@ describe('Organizations', () => {
106
127
  });
107
128
  describe('with an invalid payload', () => {
108
129
  it('returns an error', () => __awaiter(void 0, void 0, void 0, function* () {
109
- mock.onPost().reply(409, create_organization_invalid_json_1.default, {
130
+ mock
131
+ .onPost('/organizations', {
132
+ domains: ['example.com'],
133
+ name: 'Test Organization',
134
+ })
135
+ .replyOnce(409, create_organization_invalid_json_1.default, {
110
136
  'X-Request-ID': 'a-request-id',
111
137
  });
112
138
  yield expect(workos.organizations.createOrganization({
@@ -119,7 +145,9 @@ describe('Organizations', () => {
119
145
  describe('getOrganization', () => {
120
146
  it(`requests an Organization`, () => __awaiter(void 0, void 0, void 0, function* () {
121
147
  const mock = new axios_mock_adapter_1.default(axios_1.default);
122
- mock.onGet().reply(200, get_organization_json_1.default);
148
+ mock
149
+ .onGet('/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T')
150
+ .replyOnce(200, get_organization_json_1.default);
123
151
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
124
152
  const subject = yield workos.organizations.getOrganization('org_01EHT88Z8J8795GZNQ4ZP1J81T');
125
153
  expect(mock.history.get[0].url).toEqual('/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T');
@@ -132,7 +160,9 @@ describe('Organizations', () => {
132
160
  describe('deleteOrganization', () => {
133
161
  it('sends request to delete an Organization', () => __awaiter(void 0, void 0, void 0, function* () {
134
162
  const mock = new axios_mock_adapter_1.default(axios_1.default);
135
- mock.onDelete().reply(200, {});
163
+ mock
164
+ .onDelete('/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T')
165
+ .replyOnce(200, {});
136
166
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
137
167
  yield workos.organizations.deleteOrganization('org_01EHT88Z8J8795GZNQ4ZP1J81T');
138
168
  expect(mock.history.delete[0].url).toEqual('/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T');
@@ -141,7 +171,12 @@ describe('Organizations', () => {
141
171
  describe('updateOrganization', () => {
142
172
  describe('with a valid payload', () => {
143
173
  it('updates an organization', () => __awaiter(void 0, void 0, void 0, function* () {
144
- mock.onPut().reply(201, update_organization_json_1.default);
174
+ mock
175
+ .onPut('/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T', {
176
+ domains: ['example.com'],
177
+ name: 'Test Organization 2',
178
+ })
179
+ .replyOnce(201, update_organization_json_1.default);
145
180
  const subject = yield workos.organizations.updateOrganization({
146
181
  organization: 'org_01EHT88Z8J8795GZNQ4ZP1J81T',
147
182
  domains: ['example.com'],
@@ -22,10 +22,16 @@ describe('Passwordless', () => {
22
22
  describe('createSession', () => {
23
23
  describe('with valid options', () => {
24
24
  it('creates a passwordless session', () => __awaiter(void 0, void 0, void 0, function* () {
25
- mock.onPost().reply(201, create_session_json_1.default);
26
- const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
27
25
  const email = 'passwordless-session-email@workos.com';
28
26
  const redirectURI = 'https://example.com/passwordless/callback';
27
+ mock
28
+ .onPost('/passwordless/sessions', {
29
+ type: 'MagicLink',
30
+ email,
31
+ redirect_uri: redirectURI,
32
+ })
33
+ .replyOnce(201, create_session_json_1.default);
34
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
29
35
  const session = yield workos.passwordless.createSession({
30
36
  type: 'MagicLink',
31
37
  email,
@@ -42,7 +48,7 @@ describe('Passwordless', () => {
42
48
  describe('sendEmail', () => {
43
49
  describe('with a valid session id', () => {
44
50
  it(`sends a request to send a magic link email`, () => __awaiter(void 0, void 0, void 0, function* () {
45
- mock.onPost().reply(200);
51
+ mock.onPost('/passwordless/sessions/session_123/send').replyOnce(200);
46
52
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
47
53
  const sessionId = 'session_123';
48
54
  yield workos.passwordless.sendSession(sessionId);
@@ -26,7 +26,13 @@ describe('Portal', () => {
26
26
  describe('with a valid organization', () => {
27
27
  describe('with the sso intent', () => {
28
28
  it('returns an Admin Portal link', () => __awaiter(void 0, void 0, void 0, function* () {
29
- mock.onPost().reply(201, generate_link_json_1.default);
29
+ mock
30
+ .onPost('/portal/generate_link', {
31
+ intent: generate_portal_link_intent_interface_1.GeneratePortalLinkIntent.SSO,
32
+ organization: 'org_01EHQMYV6MBK39QC5PZXHY59C3',
33
+ return_url: 'https://www.example.com',
34
+ })
35
+ .replyOnce(201, generate_link_json_1.default);
30
36
  const { link } = yield workos.portal.generateLink({
31
37
  intent: generate_portal_link_intent_interface_1.GeneratePortalLinkIntent.SSO,
32
38
  organization: 'org_01EHQMYV6MBK39QC5PZXHY59C3',
@@ -37,7 +43,13 @@ describe('Portal', () => {
37
43
  });
38
44
  describe('with the dsync intent', () => {
39
45
  it('returns an Admin Portal link', () => __awaiter(void 0, void 0, void 0, function* () {
40
- mock.onPost().reply(201, generate_link_json_1.default);
46
+ mock
47
+ .onPost('/portal/generate_link', {
48
+ intent: generate_portal_link_intent_interface_1.GeneratePortalLinkIntent.DSync,
49
+ organization: 'org_01EHQMYV6MBK39QC5PZXHY59C3',
50
+ return_url: 'https://www.example.com',
51
+ })
52
+ .reply(201, generate_link_json_1.default);
41
53
  const { link } = yield workos.portal.generateLink({
42
54
  intent: generate_portal_link_intent_interface_1.GeneratePortalLinkIntent.DSync,
43
55
  organization: 'org_01EHQMYV6MBK39QC5PZXHY59C3',
@@ -49,7 +61,13 @@ describe('Portal', () => {
49
61
  });
50
62
  describe('with an invalid organization', () => {
51
63
  it('throws an error', () => __awaiter(void 0, void 0, void 0, function* () {
52
- mock.onPost().reply(400, generate_link_invalid_json_1.default, {
64
+ mock
65
+ .onPost('/portal/generate_link', {
66
+ intent: generate_portal_link_intent_interface_1.GeneratePortalLinkIntent.SSO,
67
+ organization: 'bogus-id',
68
+ return_url: 'https://www.example.com',
69
+ })
70
+ .reply(400, generate_link_invalid_json_1.default, {
53
71
  'X-Request-ID': 'a-request-id',
54
72
  });
55
73
  yield expect(workos.portal.generateLink({
@@ -1,15 +1,33 @@
1
1
  export declare enum ConnectionType {
2
2
  ADFSSAML = "ADFSSAML",
3
+ AdpOidc = "AdpOidc",
3
4
  Auth0SAML = "Auth0SAML",
4
5
  AzureSAML = "AzureSAML",
6
+ CasSAML = "CasSAML",
7
+ ClassLinkSAML = "ClassLinkSAML",
8
+ CloudflareSAML = "CloudflareSAML",
9
+ CyberArkSAML = "CyberArkSAML",
10
+ DuoSAML = "DuoSAML",
5
11
  GenericOIDC = "GenericOIDC",
6
12
  GenericSAML = "GenericSAML",
7
13
  GoogleOAuth = "GoogleOAuth",
8
14
  GoogleSAML = "GoogleSAML",
15
+ JumpCloudSAML = "JumpCloudSAML",
16
+ KeycloakSAML = "KeycloakSAML",
17
+ LastPassSAML = "LastPassSAML",
9
18
  MagicLink = "MagicLink",
19
+ MicrosoftOAuth = "MicrosoftOAuth",
20
+ MiniOrangeSAML = "MiniOrangeSAML",
21
+ NetIqSAML = "NetIqSAML",
10
22
  OktaSAML = "OktaSAML",
11
23
  OneLoginSAML = "OneLoginSAML",
24
+ OracleSAML = "OracleSAML",
12
25
  PingFederateSAML = "PingFederateSAML",
13
26
  PingOneSAML = "PingOneSAML",
27
+ RipplingSAML = "RipplingSAML",
28
+ SalesforceSAML = "SalesforceSAML",
29
+ ShibbolethGenericSAML = "ShibbolethGenericSAML",
30
+ ShibbolethSAML = "ShibbolethSAML",
31
+ SimpleSamlPhpSAML = "SimpleSamlPhpSAML",
14
32
  VMwareSAML = "VMwareSAML"
15
33
  }
@@ -4,16 +4,34 @@ exports.ConnectionType = void 0;
4
4
  var ConnectionType;
5
5
  (function (ConnectionType) {
6
6
  ConnectionType["ADFSSAML"] = "ADFSSAML";
7
+ ConnectionType["AdpOidc"] = "AdpOidc";
7
8
  ConnectionType["Auth0SAML"] = "Auth0SAML";
8
9
  ConnectionType["AzureSAML"] = "AzureSAML";
10
+ ConnectionType["CasSAML"] = "CasSAML";
11
+ ConnectionType["ClassLinkSAML"] = "ClassLinkSAML";
12
+ ConnectionType["CloudflareSAML"] = "CloudflareSAML";
13
+ ConnectionType["CyberArkSAML"] = "CyberArkSAML";
14
+ ConnectionType["DuoSAML"] = "DuoSAML";
9
15
  ConnectionType["GenericOIDC"] = "GenericOIDC";
10
16
  ConnectionType["GenericSAML"] = "GenericSAML";
11
17
  ConnectionType["GoogleOAuth"] = "GoogleOAuth";
12
18
  ConnectionType["GoogleSAML"] = "GoogleSAML";
19
+ ConnectionType["JumpCloudSAML"] = "JumpCloudSAML";
20
+ ConnectionType["KeycloakSAML"] = "KeycloakSAML";
21
+ ConnectionType["LastPassSAML"] = "LastPassSAML";
13
22
  ConnectionType["MagicLink"] = "MagicLink";
23
+ ConnectionType["MicrosoftOAuth"] = "MicrosoftOAuth";
24
+ ConnectionType["MiniOrangeSAML"] = "MiniOrangeSAML";
25
+ ConnectionType["NetIqSAML"] = "NetIqSAML";
14
26
  ConnectionType["OktaSAML"] = "OktaSAML";
15
27
  ConnectionType["OneLoginSAML"] = "OneLoginSAML";
28
+ ConnectionType["OracleSAML"] = "OracleSAML";
16
29
  ConnectionType["PingFederateSAML"] = "PingFederateSAML";
17
30
  ConnectionType["PingOneSAML"] = "PingOneSAML";
31
+ ConnectionType["RipplingSAML"] = "RipplingSAML";
32
+ ConnectionType["SalesforceSAML"] = "SalesforceSAML";
33
+ ConnectionType["ShibbolethGenericSAML"] = "ShibbolethGenericSAML";
34
+ ConnectionType["ShibbolethSAML"] = "ShibbolethSAML";
35
+ ConnectionType["SimpleSamlPhpSAML"] = "SimpleSamlPhpSAML";
18
36
  ConnectionType["VMwareSAML"] = "VMwareSAML";
19
37
  })(ConnectionType = exports.ConnectionType || (exports.ConnectionType = {}));
@@ -134,23 +134,40 @@ describe('SSO', () => {
134
134
  describe('with all information provided', () => {
135
135
  it('sends a request to the WorkOS api for a profile', () => __awaiter(void 0, void 0, void 0, function* () {
136
136
  const mock = new axios_mock_adapter_1.default(axios_1.default);
137
- mock.onPost('/sso/token').reply(200, {
138
- access_token: '01DMEK0J53CVMC32CK5SE0KZ8Q',
139
- profile: {
140
- id: 'prof_123',
141
- idp_id: '123',
142
- organization_id: 'org_123',
143
- connection_id: 'conn_123',
144
- connection_type: 'OktaSAML',
145
- email: 'foo@test.com',
146
- first_name: 'foo',
147
- last_name: 'bar',
148
- raw_attributes: {
149
- email: 'foo@test.com',
150
- first_name: 'foo',
151
- last_name: 'bar',
152
- },
153
- },
137
+ const expectedBody = new URLSearchParams({
138
+ client_id: 'proj_123',
139
+ client_secret: 'sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU',
140
+ code: 'authorization_code',
141
+ grant_type: 'authorization_code',
142
+ });
143
+ expectedBody.sort();
144
+ mock.onPost('/sso/token').replyOnce((config) => {
145
+ const actualBody = new URLSearchParams(config.data);
146
+ actualBody.sort();
147
+ if (actualBody.toString() === expectedBody.toString()) {
148
+ return [
149
+ 200,
150
+ {
151
+ access_token: '01DMEK0J53CVMC32CK5SE0KZ8Q',
152
+ profile: {
153
+ id: 'prof_123',
154
+ idp_id: '123',
155
+ organization_id: 'org_123',
156
+ connection_id: 'conn_123',
157
+ connection_type: 'OktaSAML',
158
+ email: 'foo@test.com',
159
+ first_name: 'foo',
160
+ last_name: 'bar',
161
+ raw_attributes: {
162
+ email: 'foo@test.com',
163
+ first_name: 'foo',
164
+ last_name: 'bar',
165
+ },
166
+ },
167
+ },
168
+ ];
169
+ }
170
+ return [404];
154
171
  });
155
172
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
156
173
  const { access_token: accessToken, profile } = yield workos.sso.getProfileAndToken({
@@ -169,7 +186,11 @@ describe('SSO', () => {
169
186
  describe('getProfile', () => {
170
187
  it('calls the `/sso/profile` endpoint with the provided access token', () => __awaiter(void 0, void 0, void 0, function* () {
171
188
  const mock = new axios_mock_adapter_1.default(axios_1.default);
172
- mock.onGet().reply(200, {
189
+ mock
190
+ .onGet('/sso/profile', {
191
+ accessToken: 'access_token',
192
+ })
193
+ .replyOnce(200, {
173
194
  id: 'prof_123',
174
195
  idp_id: '123',
175
196
  organization_id: 'org_123',
@@ -197,7 +218,7 @@ describe('SSO', () => {
197
218
  describe('deleteConnection', () => {
198
219
  it('sends request to delete a Connection', () => __awaiter(void 0, void 0, void 0, function* () {
199
220
  const mock = new axios_mock_adapter_1.default(axios_1.default);
200
- mock.onDelete().reply(200, {});
221
+ mock.onDelete('/connections/conn_123').replyOnce(200, {});
201
222
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
202
223
  yield workos.sso.deleteConnection('conn_123');
203
224
  expect(mock.history.delete[0].url).toEqual('/connections/conn_123');
@@ -206,7 +227,7 @@ describe('SSO', () => {
206
227
  describe('getConnection', () => {
207
228
  it(`requests a Connection`, () => __awaiter(void 0, void 0, void 0, function* () {
208
229
  const mock = new axios_mock_adapter_1.default(axios_1.default);
209
- mock.onGet().reply(200, {});
230
+ mock.onGet('/connections/conn_123').replyOnce(200, {});
210
231
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
211
232
  yield workos.sso.getConnection('conn_123');
212
233
  expect(mock.history.get[0].url).toEqual('/connections/conn_123');
@@ -215,7 +236,7 @@ describe('SSO', () => {
215
236
  describe('listConnections', () => {
216
237
  it(`requests a list of Connections`, () => __awaiter(void 0, void 0, void 0, function* () {
217
238
  const mock = new axios_mock_adapter_1.default(axios_1.default);
218
- mock.onGet().reply(200, {});
239
+ mock.onGet('/connections').replyOnce(200, {});
219
240
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
220
241
  yield workos.sso.listConnections();
221
242
  expect(mock.history.get[0].url).toEqual('/connections');
@@ -1,2 +1,4 @@
1
1
  export * from './webhook.interface';
2
2
  export * from './webhook-directory.interface';
3
+ export * from './webhook-directory-group.interface';
4
+ export * from './webhook-directory-user.interface';
@@ -16,3 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./webhook.interface"), exports);
18
18
  __exportStar(require("./webhook-directory.interface"), exports);
19
+ __exportStar(require("./webhook-directory-group.interface"), exports);
20
+ __exportStar(require("./webhook-directory-user.interface"), exports);
@@ -0,0 +1,9 @@
1
+ export interface WebhookDirectoryGroup<TRawAttributes = any> {
2
+ id: string;
3
+ idp_id: string;
4
+ directory_id: string;
5
+ name: string;
6
+ created_at: string;
7
+ updated_at: string;
8
+ raw_attributes: TRawAttributes;
9
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ import { User } from '../../directory-sync/interfaces';
2
+ export interface WebhookDirectoryUser extends User {
3
+ created_at: string;
4
+ updated_at: string;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,4 +1,20 @@
1
- import { Directory } from '../../directory-sync/interfaces';
2
- export interface WebhookDirectory extends Omit<Directory, 'state'> {
3
- state: 'active' | 'validating' | 'invalid_credentials' | 'inactive' | 'deleting';
1
+ export declare type WebhookDirectoryType = 'okta scim v1.1' | 'okta scim v2.0' | 'azure scim v2.0' | 'bamboohr' | 'breathe hr' | 'cyberark scim v2.0' | 'fourth hr' | 'gsuite directory' | 'generic scim v1.1' | 'generic scim v2.0' | 'gusto' | 'hibob' | 'jump cloud scim v2.0' | 'onelogin scim v2.0' | 'people hr' | 'pingfederate scim v2.0' | 'rippling' | 's3' | 'workday';
2
+ export declare type WebhookDirectoryState = 'active' | 'validating' | 'invalid_credentials' | 'inactive' | 'deleting';
3
+ interface WebhookDirectoryDomain {
4
+ object: 'organization_domain';
5
+ id: string;
6
+ domain: string;
4
7
  }
8
+ export interface WebhookDirectory {
9
+ object: 'directory';
10
+ id: string;
11
+ external_key: string;
12
+ type: WebhookDirectoryType;
13
+ state: WebhookDirectoryState;
14
+ name: string;
15
+ organization_id?: string;
16
+ domains: WebhookDirectoryDomain[];
17
+ created_at: string;
18
+ updated_at: string;
19
+ }
20
+ export {};
@@ -1,5 +1,6 @@
1
- import { Group, User } from '../../directory-sync/interfaces';
2
1
  import { Connection } from '../../sso/interfaces';
2
+ import { WebhookDirectoryGroup as Group } from './webhook-directory-group.interface';
3
+ import { WebhookDirectoryUser as User } from './webhook-directory-user.interface';
3
4
  import { WebhookDirectory as Directory } from './webhook-directory.interface';
4
5
  interface WebhookBase {
5
6
  id: string;
@@ -26,7 +27,7 @@ export interface DsyncDeactivatedWebhook extends WebhookBase {
26
27
  }
27
28
  export interface DsyncDeletedWebhook extends WebhookBase {
28
29
  event: 'dsync.deleted';
29
- data: Directory;
30
+ data: Omit<Directory, 'domains' | 'external_key'>;
30
31
  }
31
32
  export interface DsyncGroupCreatedWebhook extends WebhookBase {
32
33
  event: 'dsync.group.created';
@@ -38,14 +39,14 @@ export interface DsyncGroupDeletedWebhook extends WebhookBase {
38
39
  }
39
40
  export interface DsyncGroupUpdatedWebhook extends WebhookBase {
40
41
  event: 'dsync.group.updated';
41
- data: Group;
42
+ data: Group & Record<'previous_attributes', any>;
42
43
  }
43
44
  export interface DsyncGroupUserAddedWebhook extends WebhookBase {
44
45
  event: 'dsync.group.user_added';
45
46
  data: {
46
47
  directory_id: string;
47
48
  user: User;
48
- group: Group;
49
+ group: Pick<Group, 'id' | 'name'>;
49
50
  };
50
51
  }
51
52
  export interface DsyncGroupUserRemovedWebhook extends WebhookBase {
@@ -53,7 +54,7 @@ export interface DsyncGroupUserRemovedWebhook extends WebhookBase {
53
54
  data: {
54
55
  directory_id: string;
55
56
  user: User;
56
- group: Group;
57
+ group: Pick<Group, 'id' | 'name'>;
57
58
  };
58
59
  }
59
60
  export interface DsyncUserCreatedWebhook extends WebhookBase {
@@ -66,7 +67,7 @@ export interface DsyncUserDeletedWebhook extends WebhookBase {
66
67
  }
67
68
  export interface DsyncUserUpdatedWebhook extends WebhookBase {
68
69
  event: 'dsync.user.updated';
69
- data: User;
70
+ data: User & Record<'previous_attributes', any>;
70
71
  }
71
72
  export declare type Webhook = ConnectionActivatedWebhook | ConnectionDeactivatedWebhook | ConnectionDeletedWebhook | DsyncActivatedWebhook | DsyncDeactivatedWebhook | DsyncDeletedWebhook | DsyncGroupCreatedWebhook | DsyncGroupUpdatedWebhook | DsyncGroupDeletedWebhook | DsyncGroupUserAddedWebhook | DsyncGroupUserRemovedWebhook | DsyncUserCreatedWebhook | DsyncUserUpdatedWebhook | DsyncUserDeletedWebhook;
72
73
  export {};
package/lib/workos.js CHANGED
@@ -23,7 +23,7 @@ const portal_1 = require("./portal/portal");
23
23
  const sso_1 = require("./sso/sso");
24
24
  const webhooks_1 = require("./webhooks/webhooks");
25
25
  const mfa_1 = require("./mfa/mfa");
26
- const VERSION = '2.5.1';
26
+ const VERSION = '2.7.0';
27
27
  const DEFAULT_HOSTNAME = 'api.workos.com';
28
28
  class WorkOS {
29
29
  constructor(key, options = {}) {
@@ -78,14 +78,19 @@ class WorkOS {
78
78
  if (response) {
79
79
  const { status, data, headers } = response;
80
80
  const requestID = headers['X-Request-ID'];
81
- const { error, error_description: errorDescription } = data;
81
+ const { code, error_description: errorDescription, error, message, } = data;
82
82
  switch (status) {
83
83
  case 401: {
84
84
  throw new exceptions_1.UnauthorizedException(requestID);
85
85
  }
86
86
  case 422: {
87
87
  const { errors } = data;
88
- throw new exceptions_1.UnprocessableEntityException(errors, requestID);
88
+ throw new exceptions_1.UnprocessableEntityException({
89
+ code,
90
+ errors,
91
+ message,
92
+ requestID,
93
+ });
89
94
  }
90
95
  case 404: {
91
96
  throw new exceptions_1.NotFoundException(path, requestID);
@@ -122,14 +127,19 @@ class WorkOS {
122
127
  if (response) {
123
128
  const { status, data, headers } = response;
124
129
  const requestID = headers['X-Request-ID'];
125
- const { error, error_description: errorDescription } = data;
130
+ const { code, error_description: errorDescription, error, message, } = data;
126
131
  switch (status) {
127
132
  case 401: {
128
133
  throw new exceptions_1.UnauthorizedException(requestID);
129
134
  }
130
135
  case 422: {
131
136
  const { errors } = data;
132
- throw new exceptions_1.UnprocessableEntityException(errors, requestID);
137
+ throw new exceptions_1.UnprocessableEntityException({
138
+ code,
139
+ errors,
140
+ message,
141
+ requestID,
142
+ });
133
143
  }
134
144
  case 404: {
135
145
  throw new exceptions_1.NotFoundException(path, requestID);
@@ -165,14 +175,19 @@ class WorkOS {
165
175
  if (response) {
166
176
  const { status, data, headers } = response;
167
177
  const requestID = headers['X-Request-ID'];
168
- const { error, error_description: errorDescription } = data;
178
+ const { code, error_description: errorDescription, error, message, } = data;
169
179
  switch (status) {
170
180
  case 401: {
171
181
  throw new exceptions_1.UnauthorizedException(requestID);
172
182
  }
173
183
  case 422: {
174
184
  const { errors } = data;
175
- throw new exceptions_1.UnprocessableEntityException(errors, requestID);
185
+ throw new exceptions_1.UnprocessableEntityException({
186
+ code,
187
+ errors,
188
+ message,
189
+ requestID,
190
+ });
176
191
  }
177
192
  case 404: {
178
193
  throw new exceptions_1.NotFoundException(path, requestID);
@@ -203,14 +218,19 @@ class WorkOS {
203
218
  if (response) {
204
219
  const { status, data, headers } = response;
205
220
  const requestID = headers['X-Request-ID'];
206
- const { error, error_description: errorDescription } = data;
221
+ const { code, error_description: errorDescription, error, message, } = data;
207
222
  switch (status) {
208
223
  case 401: {
209
224
  throw new exceptions_1.UnauthorizedException(requestID);
210
225
  }
211
226
  case 422: {
212
227
  const { errors } = data;
213
- throw new exceptions_1.UnprocessableEntityException(errors, requestID);
228
+ throw new exceptions_1.UnprocessableEntityException({
229
+ code,
230
+ errors,
231
+ message,
232
+ requestID,
233
+ });
214
234
  }
215
235
  case 404: {
216
236
  throw new exceptions_1.NotFoundException(path, requestID);
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.5.1",
2
+ "version": "2.7.0",
3
3
  "name": "@workos-inc/node",
4
4
  "author": "WorkOS",
5
5
  "description": "A Node wrapper for the WorkOS API",
@@ -9,7 +9,7 @@
9
9
  "workos"
10
10
  ],
11
11
  "volta": {
12
- "node": "14.19.1",
12
+ "node": "14.19.3",
13
13
  "yarn": "1.22.18"
14
14
  },
15
15
  "main": "lib/index.js",
@@ -39,15 +39,15 @@
39
39
  "query-string": "7.1.1"
40
40
  },
41
41
  "devDependencies": {
42
- "@types/jest": "27.4.1",
43
- "@types/node": "14.18.12",
42
+ "@types/jest": "27.5.2",
43
+ "@types/node": "14.18.20",
44
44
  "@types/pluralize": "0.0.29",
45
- "axios-mock-adapter": "1.20.0",
45
+ "axios-mock-adapter": "1.21.1",
46
46
  "jest": "27.5.1",
47
- "prettier": "2.6.1",
48
- "supertest": "6.2.2",
49
- "ts-jest": "27.1.4",
47
+ "prettier": "2.6.2",
48
+ "supertest": "6.2.3",
49
+ "ts-jest": "27.1.5",
50
50
  "tslint": "6.1.3",
51
- "typescript": "4.6.3"
51
+ "typescript": "4.7.3"
52
52
  }
53
53
  }