@workos-inc/node 2.6.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.
- package/README.md +1 -1
- package/lib/audit-logs/audit-logs.d.ts +7 -0
- package/lib/audit-logs/audit-logs.js +26 -0
- package/lib/audit-logs/audit-logs.spec.d.ts +1 -0
- package/lib/audit-logs/audit-logs.spec.js +110 -0
- package/lib/audit-logs/interfaces/create-audit-log-event-options.interface.d.ts +26 -0
- package/lib/{mfa/interfaces/verify-factor-response.js → audit-logs/interfaces/create-audit-log-event-options.interface.js} +0 -0
- package/lib/audit-logs/interfaces/index.d.ts +1 -0
- package/lib/audit-logs/interfaces/index.js +2 -0
- package/lib/common/exceptions/bad-request.exception.d.ts +14 -0
- package/lib/common/exceptions/bad-request.exception.js +22 -0
- package/lib/common/exceptions/unprocessable-entity.exception.d.ts +8 -2
- package/lib/common/exceptions/unprocessable-entity.exception.js +15 -6
- package/lib/common/interfaces/pagination-options.interface.d.ts +1 -0
- package/lib/directory-sync/directory-sync.spec.js +3 -0
- package/lib/directory-sync/interfaces/group.interface.d.ts +3 -0
- package/lib/mfa/interfaces/challenge.interface.d.ts +1 -1
- package/lib/mfa/interfaces/verify-challenge-options.d.ts +4 -0
- package/lib/mfa/interfaces/verify-challenge-options.js +2 -0
- package/lib/mfa/interfaces/verify-challenge-response.d.ts +5 -0
- package/lib/mfa/interfaces/verify-challenge-response.js +2 -0
- package/lib/mfa/interfaces/verify-factor-options.d.ts +3 -0
- package/lib/mfa/mfa.d.ts +6 -1
- package/lib/mfa/mfa.js +10 -4
- package/lib/mfa/mfa.spec.js +95 -11
- package/lib/sso/interfaces/connection-type.enum.d.ts +18 -0
- package/lib/sso/interfaces/connection-type.enum.js +18 -0
- package/lib/workos.d.ts +2 -0
- package/lib/workos.js +42 -9
- package/package.json +8 -8
- package/lib/mfa/interfaces/verify-factor-response.d.ts +0 -10
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# WorkOS Node.js Library
|
|
2
2
|
|
|
3
3
|

|
|
4
|
-
[](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
|
|
|
@@ -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'>;
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CreateAuditLogEventOptions, CreateAuditLogEventRequestOptions, } from './create-audit-log-event-options.interface';
|
|
@@ -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;
|
|
@@ -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
|
-
|
|
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
|
-
|
|
15
|
-
this.
|
|
16
|
-
|
|
17
|
-
this.message =
|
|
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
|
}
|
|
@@ -33,8 +33,11 @@ describe('DirectorySync', () => {
|
|
|
33
33
|
};
|
|
34
34
|
const groupResponse = {
|
|
35
35
|
id: 'dir_grp_123',
|
|
36
|
+
idp_id: '123',
|
|
36
37
|
directory_id: 'dir_123',
|
|
37
38
|
name: 'Foo Group',
|
|
39
|
+
created_at: `2021-10-27 15:21:50.640958`,
|
|
40
|
+
updated_at: '2021-10-27 15:21:50.640959',
|
|
38
41
|
raw_attributes: {
|
|
39
42
|
foo: 'bar',
|
|
40
43
|
},
|
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-
|
|
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(
|
|
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
|
-
|
|
60
|
-
|
|
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;
|
package/lib/mfa/mfa.spec.js
CHANGED
|
@@ -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', () => {
|
|
@@ -132,17 +133,31 @@ describe('MFA', () => {
|
|
|
132
133
|
}
|
|
133
134
|
`);
|
|
134
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
|
+
});
|
|
135
154
|
});
|
|
136
155
|
});
|
|
137
156
|
describe('challengeFactor', () => {
|
|
138
157
|
describe('with no sms template', () => {
|
|
139
158
|
it('challenge a factor with no sms template', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
140
159
|
const mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
141
|
-
mock
|
|
142
|
-
.onPost('/auth/factors/challenge', {
|
|
143
|
-
authentication_factor_id: 'auth_factor_1234',
|
|
144
|
-
})
|
|
145
|
-
.reply(200, {
|
|
160
|
+
mock.onPost('/auth/factors/auth_factor_1234/challenge').reply(200, {
|
|
146
161
|
object: 'authentication_challenge',
|
|
147
162
|
id: 'auth_challenge_1234',
|
|
148
163
|
created_at: '2022-03-15T20:39:19.892Z',
|
|
@@ -174,8 +189,7 @@ describe('MFA', () => {
|
|
|
174
189
|
it('challenge a factor with sms template', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
175
190
|
const mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
176
191
|
mock
|
|
177
|
-
.onPost('/auth/factors/challenge', {
|
|
178
|
-
authentication_factor_id: 'auth_factor_1234',
|
|
192
|
+
.onPost('/auth/factors/auth_factor_1234/challenge', {
|
|
179
193
|
sms_template: 'This is your code: 12345',
|
|
180
194
|
})
|
|
181
195
|
.reply(200, {
|
|
@@ -208,13 +222,12 @@ describe('MFA', () => {
|
|
|
208
222
|
}));
|
|
209
223
|
});
|
|
210
224
|
});
|
|
211
|
-
describe('
|
|
225
|
+
describe('verifyChallenge', () => {
|
|
212
226
|
describe('verify with successful response', () => {
|
|
213
227
|
it('verifies a successful factor', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
214
228
|
const mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
215
229
|
mock
|
|
216
|
-
.onPost('/auth/
|
|
217
|
-
authentication_challenge_id: 'auth_challenge_1234',
|
|
230
|
+
.onPost('/auth/challenges/auth_challenge_1234/verify', {
|
|
218
231
|
code: '12345',
|
|
219
232
|
})
|
|
220
233
|
.reply(200, {
|
|
@@ -232,7 +245,7 @@ describe('MFA', () => {
|
|
|
232
245
|
const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
|
|
233
246
|
apiHostname: 'api.workos.dev',
|
|
234
247
|
});
|
|
235
|
-
const verifyResponse = yield workos.mfa.
|
|
248
|
+
const verifyResponse = yield workos.mfa.verifyChallenge({
|
|
236
249
|
authenticationChallengeId: 'auth_challenge_1234',
|
|
237
250
|
code: '12345',
|
|
238
251
|
});
|
|
@@ -252,5 +265,76 @@ describe('MFA', () => {
|
|
|
252
265
|
`);
|
|
253
266
|
}));
|
|
254
267
|
});
|
|
268
|
+
describe('when the challenge has been previously verified', () => {
|
|
269
|
+
it('throws an exception', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
270
|
+
const mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
271
|
+
mock
|
|
272
|
+
.onPost('/auth/challenges/auth_challenge_1234/verify', {
|
|
273
|
+
code: '12345',
|
|
274
|
+
})
|
|
275
|
+
.reply(422, {
|
|
276
|
+
message: `The authentication challenge '12345' has already been verified.`,
|
|
277
|
+
code: 'authentication_challenge_previously_verified',
|
|
278
|
+
}, {
|
|
279
|
+
'X-Request-ID': 'req_123',
|
|
280
|
+
});
|
|
281
|
+
const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
|
|
282
|
+
apiHostname: 'api.workos.dev',
|
|
283
|
+
});
|
|
284
|
+
yield expect(workos.mfa.verifyChallenge({
|
|
285
|
+
authenticationChallengeId: 'auth_challenge_1234',
|
|
286
|
+
code: '12345',
|
|
287
|
+
})).rejects.toThrow(exceptions_1.UnprocessableEntityException);
|
|
288
|
+
}));
|
|
289
|
+
});
|
|
290
|
+
describe('when the challenge has expired', () => {
|
|
291
|
+
it('throws an exception', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
292
|
+
const mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
293
|
+
mock
|
|
294
|
+
.onPost('/auth/challenges/auth_challenge_1234/verify', {
|
|
295
|
+
code: '12345',
|
|
296
|
+
})
|
|
297
|
+
.reply(422, {
|
|
298
|
+
message: `The authentication challenge '12345' has expired.`,
|
|
299
|
+
code: 'authentication_challenge_expired',
|
|
300
|
+
}, {
|
|
301
|
+
'X-Request-ID': 'req_123',
|
|
302
|
+
});
|
|
303
|
+
const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
|
|
304
|
+
apiHostname: 'api.workos.dev',
|
|
305
|
+
});
|
|
306
|
+
yield expect(workos.mfa.verifyChallenge({
|
|
307
|
+
authenticationChallengeId: 'auth_challenge_1234',
|
|
308
|
+
code: '12345',
|
|
309
|
+
})).rejects.toThrow(exceptions_1.UnprocessableEntityException);
|
|
310
|
+
}));
|
|
311
|
+
it('exception has code', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
312
|
+
const mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
313
|
+
mock
|
|
314
|
+
.onPost('/auth/challenges/auth_challenge_1234/verify', {
|
|
315
|
+
code: '12345',
|
|
316
|
+
})
|
|
317
|
+
.reply(422, {
|
|
318
|
+
message: `The authentication challenge '12345' has expired.`,
|
|
319
|
+
code: 'authentication_challenge_expired',
|
|
320
|
+
}, {
|
|
321
|
+
'X-Request-ID': 'req_123',
|
|
322
|
+
});
|
|
323
|
+
const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
|
|
324
|
+
apiHostname: 'api.workos.dev',
|
|
325
|
+
});
|
|
326
|
+
try {
|
|
327
|
+
yield workos.mfa.verifyChallenge({
|
|
328
|
+
authenticationChallengeId: 'auth_challenge_1234',
|
|
329
|
+
code: '12345',
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
expect(error).toMatchObject({
|
|
334
|
+
code: 'authentication_challenge_expired',
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}));
|
|
338
|
+
});
|
|
255
339
|
});
|
|
256
340
|
});
|
|
@@ -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 = {}));
|
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
|
|
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,14 +81,19 @@ class WorkOS {
|
|
|
78
81
|
if (response) {
|
|
79
82
|
const { status, data, headers } = response;
|
|
80
83
|
const requestID = headers['X-Request-ID'];
|
|
81
|
-
const {
|
|
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);
|
|
85
88
|
}
|
|
86
89
|
case 422: {
|
|
87
90
|
const { errors } = data;
|
|
88
|
-
throw new exceptions_1.UnprocessableEntityException(
|
|
91
|
+
throw new exceptions_1.UnprocessableEntityException({
|
|
92
|
+
code,
|
|
93
|
+
errors,
|
|
94
|
+
message,
|
|
95
|
+
requestID,
|
|
96
|
+
});
|
|
89
97
|
}
|
|
90
98
|
case 404: {
|
|
91
99
|
throw new exceptions_1.NotFoundException(path, requestID);
|
|
@@ -94,6 +102,16 @@ class WorkOS {
|
|
|
94
102
|
if (error || errorDescription) {
|
|
95
103
|
throw new exceptions_1.OauthException(status, requestID, error, errorDescription);
|
|
96
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
|
+
}
|
|
97
115
|
else {
|
|
98
116
|
throw new exceptions_1.GenericServerException(status, data.message, requestID);
|
|
99
117
|
}
|
|
@@ -122,14 +140,19 @@ class WorkOS {
|
|
|
122
140
|
if (response) {
|
|
123
141
|
const { status, data, headers } = response;
|
|
124
142
|
const requestID = headers['X-Request-ID'];
|
|
125
|
-
const {
|
|
143
|
+
const { code, error_description: errorDescription, error, message, } = data;
|
|
126
144
|
switch (status) {
|
|
127
145
|
case 401: {
|
|
128
146
|
throw new exceptions_1.UnauthorizedException(requestID);
|
|
129
147
|
}
|
|
130
148
|
case 422: {
|
|
131
149
|
const { errors } = data;
|
|
132
|
-
throw new exceptions_1.UnprocessableEntityException(
|
|
150
|
+
throw new exceptions_1.UnprocessableEntityException({
|
|
151
|
+
code,
|
|
152
|
+
errors,
|
|
153
|
+
message,
|
|
154
|
+
requestID,
|
|
155
|
+
});
|
|
133
156
|
}
|
|
134
157
|
case 404: {
|
|
135
158
|
throw new exceptions_1.NotFoundException(path, requestID);
|
|
@@ -165,14 +188,19 @@ class WorkOS {
|
|
|
165
188
|
if (response) {
|
|
166
189
|
const { status, data, headers } = response;
|
|
167
190
|
const requestID = headers['X-Request-ID'];
|
|
168
|
-
const {
|
|
191
|
+
const { code, error_description: errorDescription, error, message, } = data;
|
|
169
192
|
switch (status) {
|
|
170
193
|
case 401: {
|
|
171
194
|
throw new exceptions_1.UnauthorizedException(requestID);
|
|
172
195
|
}
|
|
173
196
|
case 422: {
|
|
174
197
|
const { errors } = data;
|
|
175
|
-
throw new exceptions_1.UnprocessableEntityException(
|
|
198
|
+
throw new exceptions_1.UnprocessableEntityException({
|
|
199
|
+
code,
|
|
200
|
+
errors,
|
|
201
|
+
message,
|
|
202
|
+
requestID,
|
|
203
|
+
});
|
|
176
204
|
}
|
|
177
205
|
case 404: {
|
|
178
206
|
throw new exceptions_1.NotFoundException(path, requestID);
|
|
@@ -203,14 +231,19 @@ class WorkOS {
|
|
|
203
231
|
if (response) {
|
|
204
232
|
const { status, data, headers } = response;
|
|
205
233
|
const requestID = headers['X-Request-ID'];
|
|
206
|
-
const {
|
|
234
|
+
const { code, error_description: errorDescription, error, message, } = data;
|
|
207
235
|
switch (status) {
|
|
208
236
|
case 401: {
|
|
209
237
|
throw new exceptions_1.UnauthorizedException(requestID);
|
|
210
238
|
}
|
|
211
239
|
case 422: {
|
|
212
240
|
const { errors } = data;
|
|
213
|
-
throw new exceptions_1.UnprocessableEntityException(
|
|
241
|
+
throw new exceptions_1.UnprocessableEntityException({
|
|
242
|
+
code,
|
|
243
|
+
errors,
|
|
244
|
+
message,
|
|
245
|
+
requestID,
|
|
246
|
+
});
|
|
214
247
|
}
|
|
215
248
|
case 404: {
|
|
216
249
|
throw new exceptions_1.NotFoundException(path, requestID);
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
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.
|
|
13
|
-
"yarn": "1.22.
|
|
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",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"query-string": "7.1.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@types/jest": "27.5.
|
|
43
|
-
"@types/node": "14.18.
|
|
42
|
+
"@types/jest": "27.5.2",
|
|
43
|
+
"@types/node": "14.18.21",
|
|
44
44
|
"@types/pluralize": "0.0.29",
|
|
45
|
-
"axios-mock-adapter": "1.
|
|
45
|
+
"axios-mock-adapter": "1.21.1",
|
|
46
46
|
"jest": "27.5.1",
|
|
47
|
-
"prettier": "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.
|
|
51
|
+
"typescript": "4.7.4"
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Challenge } from './challenge.interface';
|
|
2
|
-
export interface VerifyResponseSuccess {
|
|
3
|
-
challenge: Challenge;
|
|
4
|
-
valid: boolean;
|
|
5
|
-
}
|
|
6
|
-
export interface VerifyResponseError {
|
|
7
|
-
code: string;
|
|
8
|
-
message: string;
|
|
9
|
-
}
|
|
10
|
-
export declare type VerifyResponse = VerifyResponseSuccess | VerifyResponseError;
|