@workos-inc/node 2.7.0 → 2.8.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.
@@ -0,0 +1,7 @@
1
+ import { WorkOS } from '../workos';
2
+ import { CreateAuditLogEventOptions, CreateAuditLogEventRequestOptions } from './interfaces';
3
+ export declare class AuditLogs {
4
+ private readonly workos;
5
+ constructor(workos: WorkOS);
6
+ createEvent(organization: string, event: CreateAuditLogEventOptions, options?: CreateAuditLogEventRequestOptions): Promise<void>;
7
+ }
@@ -0,0 +1,26 @@
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.AuditLogs = void 0;
13
+ class AuditLogs {
14
+ constructor(workos) {
15
+ this.workos = workos;
16
+ }
17
+ createEvent(organization, event, options = {}) {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ yield this.workos.post('/audit_logs/events', {
20
+ event,
21
+ organization_id: organization,
22
+ }, options);
23
+ });
24
+ }
25
+ }
26
+ exports.AuditLogs = AuditLogs;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,110 @@
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 axios_1 = __importDefault(require("axios"));
16
+ const axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
17
+ const exceptions_1 = require("../common/exceptions");
18
+ const bad_request_exception_1 = require("../common/exceptions/bad-request.exception");
19
+ const workos_1 = require("../workos");
20
+ const mock = new axios_mock_adapter_1.default(axios_1.default);
21
+ const event = {
22
+ action: 'document.updated',
23
+ occurred_at: new Date(),
24
+ actor: {
25
+ id: 'user_1',
26
+ name: 'Jon Smith',
27
+ type: 'user',
28
+ },
29
+ targets: [
30
+ {
31
+ id: 'document_39127',
32
+ type: 'document',
33
+ },
34
+ ],
35
+ context: {
36
+ location: ' 192.0.0.8',
37
+ user_agent: 'Firefox',
38
+ },
39
+ metadata: {
40
+ successful: true,
41
+ },
42
+ };
43
+ const serializeEventOptions = (options) => (Object.assign(Object.assign({}, options), { occurred_at: options.occurred_at.toISOString() }));
44
+ describe('AuditLogs', () => {
45
+ describe('createEvent', () => {
46
+ describe('with an idempotency key', () => {
47
+ it('includes an idempotency key with request', () => __awaiter(void 0, void 0, void 0, function* () {
48
+ mock
49
+ .onPost('/audit_logs/events', {
50
+ event: serializeEventOptions(event),
51
+ organization_id: 'org_123',
52
+ })
53
+ .replyOnce(201, { success: true });
54
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
55
+ yield expect(workos.auditLogs.createEvent('org_123', event, {
56
+ idempotencyKey: 'the-idempotency-key',
57
+ })).resolves.toBeUndefined();
58
+ expect(mock.history.post[0].headers['Idempotency-Key']).toEqual('the-idempotency-key');
59
+ }));
60
+ });
61
+ describe('when the api responds with a 200', () => {
62
+ it('returns void', () => __awaiter(void 0, void 0, void 0, function* () {
63
+ mock
64
+ .onPost('/audit_logs/events', {
65
+ organization_id: 'org_123',
66
+ event: serializeEventOptions(event),
67
+ })
68
+ .replyOnce(201, { success: true });
69
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
70
+ yield expect(workos.auditLogs.createEvent('org_123', event)).resolves.toBeUndefined();
71
+ }));
72
+ });
73
+ describe('when the api responds with a 401', () => {
74
+ it('throws an UnauthorizedException', () => __awaiter(void 0, void 0, void 0, function* () {
75
+ mock
76
+ .onPost('/audit_logs/events', {
77
+ organization_id: 'org_123',
78
+ event: serializeEventOptions(event),
79
+ })
80
+ .replyOnce(401, {
81
+ message: 'Unauthorized',
82
+ }, { 'X-Request-ID': 'a-request-id' });
83
+ const workos = new workos_1.WorkOS('invalid apikey');
84
+ yield expect(workos.auditLogs.createEvent('org_123', event)).rejects.toStrictEqual(new exceptions_1.UnauthorizedException('a-request-id'));
85
+ }));
86
+ });
87
+ describe('when the api responds with a 400', () => {
88
+ it('throws an BadRequestException', () => __awaiter(void 0, void 0, void 0, function* () {
89
+ const errors = [
90
+ {
91
+ field: 'occurred_at',
92
+ code: 'occurred_at must be an ISO 8601 date string',
93
+ },
94
+ ];
95
+ mock
96
+ .onPost('/audit_logs/events', {
97
+ organization_id: 'org_123',
98
+ event: serializeEventOptions(event),
99
+ })
100
+ .replyOnce(400, {
101
+ message: 'Audit Log could not be processed due to missing or incorrect data.',
102
+ code: 'invalid_audit_log',
103
+ errors,
104
+ }, { 'X-Request-ID': 'a-request-id' });
105
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
106
+ yield expect(workos.auditLogs.createEvent('org_123', event)).rejects.toThrow(bad_request_exception_1.BadRequestException);
107
+ }));
108
+ });
109
+ });
110
+ });
@@ -0,0 +1,26 @@
1
+ import { PostOptions } from '../../common/interfaces';
2
+ export interface AuditLogActor {
3
+ id: string;
4
+ name?: string;
5
+ type: string;
6
+ metadata?: Record<string, string | number | boolean>;
7
+ }
8
+ export interface AuditLogTarget {
9
+ id: string;
10
+ name?: string;
11
+ type: string;
12
+ metadata?: Record<string, string | number | boolean>;
13
+ }
14
+ export interface CreateAuditLogEventOptions {
15
+ action: string;
16
+ version?: number;
17
+ occurred_at: Date;
18
+ actor: AuditLogActor;
19
+ targets: AuditLogTarget[];
20
+ context: {
21
+ location: string;
22
+ user_agent?: string;
23
+ };
24
+ metadata?: Record<string, string | number | boolean>;
25
+ }
26
+ export declare type CreateAuditLogEventRequestOptions = Pick<PostOptions, 'idempotencyKey'>;
@@ -0,0 +1 @@
1
+ export { CreateAuditLogEventOptions, CreateAuditLogEventRequestOptions, } from './create-audit-log-event-options.interface';
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,14 @@
1
+ export declare class BadRequestException extends Error {
2
+ readonly status: number;
3
+ readonly name: string;
4
+ readonly message: string;
5
+ readonly code?: string;
6
+ readonly errors?: unknown[];
7
+ readonly requestID: string;
8
+ constructor({ code, errors, message, requestID, }: {
9
+ code?: string;
10
+ errors?: unknown[];
11
+ message?: string;
12
+ requestID: string;
13
+ });
14
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BadRequestException = void 0;
4
+ class BadRequestException extends Error {
5
+ constructor({ code, errors, message, requestID, }) {
6
+ super();
7
+ this.status = 400;
8
+ this.name = 'BadRequestException';
9
+ this.message = 'Bad request';
10
+ this.requestID = requestID;
11
+ if (message) {
12
+ this.message = message;
13
+ }
14
+ if (code) {
15
+ this.code = code;
16
+ }
17
+ if (errors) {
18
+ this.errors = errors;
19
+ }
20
+ }
21
+ }
22
+ exports.BadRequestException = BadRequestException;
@@ -0,0 +1,4 @@
1
+ export interface VerifyChallengeOptions {
2
+ authenticationChallengeId: string;
3
+ code: string;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,3 +1,6 @@
1
+ /**
2
+ * @deprecated Please use `VerifyChallengeOptions` instead.
3
+ */
1
4
  export declare type VerifyFactorOptions = {
2
5
  authenticationChallengeId: string;
3
6
  code: string;
package/lib/mfa/mfa.d.ts CHANGED
@@ -4,7 +4,8 @@ import { Challenge } from './interfaces/challenge.interface';
4
4
  import { EnrollFactorOptions } from './interfaces/enroll-factor-options';
5
5
  import { Factor } from './interfaces/factor.interface';
6
6
  import { VerifyFactorOptions } from './interfaces/verify-factor-options';
7
- import { VerifyResponse } from './interfaces/verify-factor-response';
7
+ import { VerifyResponse } from './interfaces/verify-challenge-response';
8
+ import { VerifyChallengeOptions } from './interfaces/verify-challenge-options';
8
9
  export declare class Mfa {
9
10
  private readonly workos;
10
11
  constructor(workos: WorkOS);
@@ -12,5 +13,9 @@ export declare class Mfa {
12
13
  getFactor(id: string): Promise<any>;
13
14
  enrollFactor(options: EnrollFactorOptions): Promise<Factor>;
14
15
  challengeFactor(options: ChallengeFactorOptions): Promise<Challenge>;
16
+ /**
17
+ * @deprecated Please use `verifyChallenge` instead.
18
+ */
15
19
  verifyFactor(options: VerifyFactorOptions): Promise<VerifyResponse>;
20
+ verifyChallenge(options: VerifyChallengeOptions): Promise<VerifyResponse>;
16
21
  }
package/lib/mfa/mfa.js CHANGED
@@ -47,17 +47,23 @@ class Mfa {
47
47
  }
48
48
  challengeFactor(options) {
49
49
  return __awaiter(this, void 0, void 0, function* () {
50
- const { data } = yield this.workos.post('/auth/factors/challenge', {
51
- authentication_factor_id: options.authenticationFactorId,
50
+ const { data } = yield this.workos.post(`/auth/factors/${options.authenticationFactorId}/challenge`, {
52
51
  sms_template: 'smsTemplate' in options ? options.smsTemplate : undefined,
53
52
  });
54
53
  return data;
55
54
  });
56
55
  }
56
+ /**
57
+ * @deprecated Please use `verifyChallenge` instead.
58
+ */
57
59
  verifyFactor(options) {
58
60
  return __awaiter(this, void 0, void 0, function* () {
59
- const { data } = yield this.workos.post('/auth/factors/verify', {
60
- authentication_challenge_id: options.authenticationChallengeId,
61
+ return this.verifyChallenge(options);
62
+ });
63
+ }
64
+ verifyChallenge(options) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ const { data } = yield this.workos.post(`/auth/challenges/${options.authenticationChallengeId}/verify`, {
61
67
  code: options.code,
62
68
  });
63
69
  return data;
@@ -157,11 +157,7 @@ describe('MFA', () => {
157
157
  describe('with no sms template', () => {
158
158
  it('challenge a factor with no sms template', () => __awaiter(void 0, void 0, void 0, function* () {
159
159
  const mock = new axios_mock_adapter_1.default(axios_1.default);
160
- mock
161
- .onPost('/auth/factors/challenge', {
162
- authentication_factor_id: 'auth_factor_1234',
163
- })
164
- .reply(200, {
160
+ mock.onPost('/auth/factors/auth_factor_1234/challenge').reply(200, {
165
161
  object: 'authentication_challenge',
166
162
  id: 'auth_challenge_1234',
167
163
  created_at: '2022-03-15T20:39:19.892Z',
@@ -193,8 +189,7 @@ describe('MFA', () => {
193
189
  it('challenge a factor with sms template', () => __awaiter(void 0, void 0, void 0, function* () {
194
190
  const mock = new axios_mock_adapter_1.default(axios_1.default);
195
191
  mock
196
- .onPost('/auth/factors/challenge', {
197
- authentication_factor_id: 'auth_factor_1234',
192
+ .onPost('/auth/factors/auth_factor_1234/challenge', {
198
193
  sms_template: 'This is your code: 12345',
199
194
  })
200
195
  .reply(200, {
@@ -227,13 +222,12 @@ describe('MFA', () => {
227
222
  }));
228
223
  });
229
224
  });
230
- describe('verifyFactor', () => {
225
+ describe('verifyChallenge', () => {
231
226
  describe('verify with successful response', () => {
232
227
  it('verifies a successful factor', () => __awaiter(void 0, void 0, void 0, function* () {
233
228
  const mock = new axios_mock_adapter_1.default(axios_1.default);
234
229
  mock
235
- .onPost('/auth/factors/verify', {
236
- authentication_challenge_id: 'auth_challenge_1234',
230
+ .onPost('/auth/challenges/auth_challenge_1234/verify', {
237
231
  code: '12345',
238
232
  })
239
233
  .reply(200, {
@@ -251,7 +245,7 @@ describe('MFA', () => {
251
245
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
252
246
  apiHostname: 'api.workos.dev',
253
247
  });
254
- const verifyResponse = yield workos.mfa.verifyFactor({
248
+ const verifyResponse = yield workos.mfa.verifyChallenge({
255
249
  authenticationChallengeId: 'auth_challenge_1234',
256
250
  code: '12345',
257
251
  });
@@ -275,8 +269,7 @@ describe('MFA', () => {
275
269
  it('throws an exception', () => __awaiter(void 0, void 0, void 0, function* () {
276
270
  const mock = new axios_mock_adapter_1.default(axios_1.default);
277
271
  mock
278
- .onPost('/auth/factors/verify', {
279
- authentication_challenge_id: 'auth_challenge_1234',
272
+ .onPost('/auth/challenges/auth_challenge_1234/verify', {
280
273
  code: '12345',
281
274
  })
282
275
  .reply(422, {
@@ -288,7 +281,7 @@ describe('MFA', () => {
288
281
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
289
282
  apiHostname: 'api.workos.dev',
290
283
  });
291
- yield expect(workos.mfa.verifyFactor({
284
+ yield expect(workos.mfa.verifyChallenge({
292
285
  authenticationChallengeId: 'auth_challenge_1234',
293
286
  code: '12345',
294
287
  })).rejects.toThrow(exceptions_1.UnprocessableEntityException);
@@ -298,8 +291,7 @@ describe('MFA', () => {
298
291
  it('throws an exception', () => __awaiter(void 0, void 0, void 0, function* () {
299
292
  const mock = new axios_mock_adapter_1.default(axios_1.default);
300
293
  mock
301
- .onPost('/auth/factors/verify', {
302
- authentication_challenge_id: 'auth_challenge_1234',
294
+ .onPost('/auth/challenges/auth_challenge_1234/verify', {
303
295
  code: '12345',
304
296
  })
305
297
  .reply(422, {
@@ -311,7 +303,7 @@ describe('MFA', () => {
311
303
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
312
304
  apiHostname: 'api.workos.dev',
313
305
  });
314
- yield expect(workos.mfa.verifyFactor({
306
+ yield expect(workos.mfa.verifyChallenge({
315
307
  authenticationChallengeId: 'auth_challenge_1234',
316
308
  code: '12345',
317
309
  })).rejects.toThrow(exceptions_1.UnprocessableEntityException);
@@ -319,8 +311,7 @@ describe('MFA', () => {
319
311
  it('exception has code', () => __awaiter(void 0, void 0, void 0, function* () {
320
312
  const mock = new axios_mock_adapter_1.default(axios_1.default);
321
313
  mock
322
- .onPost('/auth/factors/verify', {
323
- authentication_challenge_id: 'auth_challenge_1234',
314
+ .onPost('/auth/challenges/auth_challenge_1234/verify', {
324
315
  code: '12345',
325
316
  })
326
317
  .reply(422, {
@@ -333,7 +324,7 @@ describe('MFA', () => {
333
324
  apiHostname: 'api.workos.dev',
334
325
  });
335
326
  try {
336
- yield workos.mfa.verifyFactor({
327
+ yield workos.mfa.verifyChallenge({
337
328
  authenticationChallengeId: 'auth_challenge_1234',
338
329
  code: '12345',
339
330
  });
package/lib/workos.d.ts CHANGED
@@ -8,11 +8,13 @@ import { Portal } from './portal/portal';
8
8
  import { SSO } from './sso/sso';
9
9
  import { Webhooks } from './webhooks/webhooks';
10
10
  import { Mfa } from './mfa/mfa';
11
+ import { AuditLogs } from './audit-logs/audit-logs';
11
12
  export declare class WorkOS {
12
13
  readonly key?: string | undefined;
13
14
  readonly options: WorkOSOptions;
14
15
  readonly baseURL: string;
15
16
  private readonly client;
17
+ readonly auditLogs: AuditLogs;
16
18
  readonly auditTrail: AuditTrail;
17
19
  readonly directorySync: DirectorySync;
18
20
  readonly organizations: Organizations;
package/lib/workos.js CHANGED
@@ -23,12 +23,15 @@ 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.7.0';
26
+ const audit_logs_1 = require("./audit-logs/audit-logs");
27
+ const bad_request_exception_1 = require("./common/exceptions/bad-request.exception");
28
+ const VERSION = '2.8.0';
27
29
  const DEFAULT_HOSTNAME = 'api.workos.com';
28
30
  class WorkOS {
29
31
  constructor(key, options = {}) {
30
32
  this.key = key;
31
33
  this.options = options;
34
+ this.auditLogs = new audit_logs_1.AuditLogs(this);
32
35
  this.auditTrail = new audit_trail_1.AuditTrail(this);
33
36
  this.directorySync = new directory_sync_1.DirectorySync(this);
34
37
  this.organizations = new organizations_1.Organizations(this);
@@ -78,7 +81,7 @@ class WorkOS {
78
81
  if (response) {
79
82
  const { status, data, headers } = response;
80
83
  const requestID = headers['X-Request-ID'];
81
- const { code, error_description: errorDescription, error, message, } = data;
84
+ const { code, error_description: errorDescription, error, errors, message, } = data;
82
85
  switch (status) {
83
86
  case 401: {
84
87
  throw new exceptions_1.UnauthorizedException(requestID);
@@ -99,6 +102,16 @@ class WorkOS {
99
102
  if (error || errorDescription) {
100
103
  throw new exceptions_1.OauthException(status, requestID, error, errorDescription);
101
104
  }
105
+ else if (code && errors) {
106
+ // Note: ideally this should be mapped directly with a `400` status code.
107
+ // However, this would break existing logic for the `OauthException` exception.
108
+ throw new bad_request_exception_1.BadRequestException({
109
+ code,
110
+ errors,
111
+ message,
112
+ requestID,
113
+ });
114
+ }
102
115
  else {
103
116
  throw new exceptions_1.GenericServerException(status, data.message, requestID);
104
117
  }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.7.0",
2
+ "version": "2.8.0",
3
3
  "name": "@workos-inc/node",
4
4
  "author": "WorkOS",
5
5
  "description": "A Node wrapper for the WorkOS API",
@@ -9,8 +9,8 @@
9
9
  "workos"
10
10
  ],
11
11
  "volta": {
12
- "node": "14.19.3",
13
- "yarn": "1.22.18"
12
+ "node": "14.20.0",
13
+ "yarn": "1.22.19"
14
14
  },
15
15
  "main": "lib/index.js",
16
16
  "typings": "lib/index.d.ts",
@@ -40,14 +40,14 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/jest": "27.5.2",
43
- "@types/node": "14.18.20",
43
+ "@types/node": "14.18.21",
44
44
  "@types/pluralize": "0.0.29",
45
45
  "axios-mock-adapter": "1.21.1",
46
46
  "jest": "27.5.1",
47
- "prettier": "2.6.2",
47
+ "prettier": "2.7.1",
48
48
  "supertest": "6.2.3",
49
49
  "ts-jest": "27.1.5",
50
50
  "tslint": "6.1.3",
51
- "typescript": "4.7.3"
51
+ "typescript": "4.7.4"
52
52
  }
53
53
  }