@workos-inc/node 7.30.0 → 7.31.0-beta.actions1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/actions/actions.d.ts +19 -0
- package/lib/actions/actions.js +53 -0
- package/lib/actions/actions.spec.d.ts +1 -0
- package/lib/actions/actions.spec.js +78 -0
- package/lib/actions/fixtures/action-context.json +39 -0
- package/lib/actions/interfaces/response-payload.d.ts +23 -0
- package/lib/actions/interfaces/response-payload.js +2 -0
- package/lib/common/crypto/CryptoProvider.d.ts +32 -0
- package/lib/common/crypto/CryptoProvider.js +13 -0
- package/lib/common/crypto/CryptoProvider.spec.d.ts +1 -0
- package/lib/common/crypto/CryptoProvider.spec.js +57 -0
- package/lib/common/crypto/NodeCryptoProvider.d.ts +12 -0
- package/lib/common/crypto/NodeCryptoProvider.js +73 -0
- package/lib/common/crypto/SignatureProvider.d.ts +13 -0
- package/lib/common/crypto/SignatureProvider.js +53 -0
- package/lib/common/crypto/SignatureProvider.spec.d.ts +1 -0
- package/lib/common/crypto/SignatureProvider.spec.js +66 -0
- package/lib/common/crypto/SubtleCryptoProvider.d.ts +15 -0
- package/lib/common/crypto/SubtleCryptoProvider.js +75 -0
- package/lib/common/crypto/index.d.ts +4 -0
- package/lib/common/crypto/index.js +20 -0
- package/lib/common/net/fetch-client.spec.d.ts +1 -0
- package/lib/common/net/fetch-client.spec.js +140 -0
- package/lib/common/net/index.d.ts +5 -0
- package/lib/common/net/index.js +31 -0
- package/lib/common/net/node-client.js +1 -0
- package/lib/common/net/node-client.spec.d.ts +1 -0
- package/lib/common/net/node-client.spec.js +140 -0
- package/lib/common/utils/unreachable.d.ts +10 -0
- package/lib/common/utils/unreachable.js +18 -0
- package/lib/fga/fga.spec.js +0 -609
- package/lib/index.d.ts +3 -0
- package/lib/index.js +12 -0
- package/lib/index.worker.d.ts +3 -0
- package/lib/index.worker.js +6 -0
- package/lib/organizations/fixtures/clear-stripe-customer-id.json +13 -0
- package/lib/organizations/fixtures/set-stripe-customer-id-disabled.json +4 -0
- package/lib/organizations/fixtures/set-stripe-customer-id.json +14 -0
- package/lib/organizations/interfaces/organization.interface.d.ts +2 -0
- package/lib/organizations/interfaces/update-organization-options.interface.d.ts +2 -0
- package/lib/organizations/organizations.spec.js +39 -0
- package/lib/organizations/serializers/organization.serializer.js +3 -9
- package/lib/organizations/serializers/update-organization-options.serializer.js +1 -0
- package/lib/user-management/interfaces/authenticate-with-session-cookie.interface.d.ts +2 -0
- package/lib/user-management/session.js +2 -1
- package/lib/user-management/session.spec.js +2 -1
- package/lib/user-management/user-management.js +2 -1
- package/lib/user-management/user-management.spec.js +2 -1
- package/lib/webhooks/webhooks.d.ts +9 -9
- package/lib/webhooks/webhooks.js +11 -36
- package/lib/webhooks/webhooks.spec.js +23 -46
- package/lib/widgets/fixtures/get-token-error.json +5 -0
- package/lib/widgets/fixtures/token.json +3 -0
- package/lib/widgets/interfaces/get-token.d.ts +19 -0
- package/lib/widgets/interfaces/get-token.js +13 -0
- package/lib/widgets/widgets.d.ts +7 -0
- package/lib/widgets/widgets.js +25 -0
- package/lib/widgets/widgets.spec.d.ts +1 -0
- package/lib/widgets/widgets.spec.js +49 -0
- package/lib/workos.d.ts +5 -0
- package/lib/workos.js +9 -2
- package/lib/workos.spec.js +5 -1
- package/package.json +2 -1
package/lib/index.js
CHANGED
|
@@ -19,6 +19,7 @@ const node_crypto_provider_1 = require("./common/crypto/node-crypto-provider");
|
|
|
19
19
|
const subtle_crypto_provider_1 = require("./common/crypto/subtle-crypto-provider");
|
|
20
20
|
const fetch_client_1 = require("./common/net/fetch-client");
|
|
21
21
|
const node_client_1 = require("./common/net/node-client");
|
|
22
|
+
const actions_1 = require("./actions/actions");
|
|
22
23
|
const webhooks_1 = require("./webhooks/webhooks");
|
|
23
24
|
const workos_1 = require("./workos");
|
|
24
25
|
const web_iron_session_provider_1 = require("./common/iron-session/web-iron-session-provider");
|
|
@@ -61,6 +62,17 @@ class WorkOSNode extends workos_1.WorkOS {
|
|
|
61
62
|
return new webhooks_1.Webhooks(cryptoProvider);
|
|
62
63
|
}
|
|
63
64
|
/** @override */
|
|
65
|
+
createActionsClient() {
|
|
66
|
+
let cryptoProvider;
|
|
67
|
+
if (typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined') {
|
|
68
|
+
cryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
cryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
72
|
+
}
|
|
73
|
+
return new actions_1.Actions(cryptoProvider);
|
|
74
|
+
}
|
|
75
|
+
/** @override */
|
|
64
76
|
createIronSessionProvider() {
|
|
65
77
|
return new web_iron_session_provider_1.WebIronSessionProvider();
|
|
66
78
|
}
|
package/lib/index.worker.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Actions } from './actions/actions';
|
|
1
2
|
import { IronSessionProvider } from './common/iron-session/iron-session-provider';
|
|
2
3
|
import { HttpClient } from './common/net/http-client';
|
|
3
4
|
import { WorkOSOptions } from './index.worker';
|
|
@@ -21,6 +22,8 @@ declare class WorkOSWorker extends WorkOS {
|
|
|
21
22
|
/** @override */
|
|
22
23
|
createWebhookClient(): Webhooks;
|
|
23
24
|
/** @override */
|
|
25
|
+
createActionsClient(): Actions;
|
|
26
|
+
/** @override */
|
|
24
27
|
createIronSessionProvider(): IronSessionProvider;
|
|
25
28
|
/** @override */
|
|
26
29
|
emitWarning(warning: string): void;
|
package/lib/index.worker.js
CHANGED
|
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.WorkOS = void 0;
|
|
18
|
+
const actions_1 = require("./actions/actions");
|
|
18
19
|
const subtle_crypto_provider_1 = require("./common/crypto/subtle-crypto-provider");
|
|
19
20
|
const edge_iron_session_provider_1 = require("./common/iron-session/edge-iron-session-provider");
|
|
20
21
|
const fetch_client_1 = require("./common/net/fetch-client");
|
|
@@ -44,6 +45,11 @@ class WorkOSWorker extends workos_1.WorkOS {
|
|
|
44
45
|
return new webhooks_1.Webhooks(cryptoProvider);
|
|
45
46
|
}
|
|
46
47
|
/** @override */
|
|
48
|
+
createActionsClient() {
|
|
49
|
+
const cryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
50
|
+
return new actions_1.Actions(cryptoProvider);
|
|
51
|
+
}
|
|
52
|
+
/** @override */
|
|
47
53
|
createIronSessionProvider() {
|
|
48
54
|
return new edge_iron_session_provider_1.EdgeIronSessionProvider();
|
|
49
55
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Test Organization 3",
|
|
3
|
+
"object": "organization",
|
|
4
|
+
"id": "org_01EHT88Z8J8795GZNQ4ZP1J81T",
|
|
5
|
+
"allow_profiles_outside_organization": false,
|
|
6
|
+
"domains": [
|
|
7
|
+
{
|
|
8
|
+
"domain": "example.com",
|
|
9
|
+
"object": "organization_domain",
|
|
10
|
+
"id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Test Organization 3",
|
|
3
|
+
"object": "organization",
|
|
4
|
+
"id": "org_01EHT88Z8J8795GZNQ4ZP1J81T",
|
|
5
|
+
"allow_profiles_outside_organization": false,
|
|
6
|
+
"domains": [
|
|
7
|
+
{
|
|
8
|
+
"domain": "example.com",
|
|
9
|
+
"object": "organization_domain",
|
|
10
|
+
"id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"stripe_customer_id": "cus_MX8J9nfK4lP2Yw"
|
|
14
|
+
}
|
|
@@ -5,6 +5,7 @@ export interface Organization {
|
|
|
5
5
|
name: string;
|
|
6
6
|
allowProfilesOutsideOrganization: boolean;
|
|
7
7
|
domains: OrganizationDomain[];
|
|
8
|
+
stripeCustomerId?: string;
|
|
8
9
|
createdAt: string;
|
|
9
10
|
updatedAt: string;
|
|
10
11
|
}
|
|
@@ -14,6 +15,7 @@ export interface OrganizationResponse {
|
|
|
14
15
|
name: string;
|
|
15
16
|
allow_profiles_outside_organization: boolean;
|
|
16
17
|
domains: OrganizationDomainResponse[];
|
|
18
|
+
stripe_customer_id?: string;
|
|
17
19
|
created_at: string;
|
|
18
20
|
updated_at: string;
|
|
19
21
|
}
|
|
@@ -3,6 +3,7 @@ export interface UpdateOrganizationOptions {
|
|
|
3
3
|
organization: string;
|
|
4
4
|
name?: string;
|
|
5
5
|
domainData?: DomainData[];
|
|
6
|
+
stripeCustomerId?: string | null;
|
|
6
7
|
/**
|
|
7
8
|
* @deprecated If you need to allow sign-ins from any email domain, contact support@workos.com.
|
|
8
9
|
*/
|
|
@@ -15,6 +16,7 @@ export interface UpdateOrganizationOptions {
|
|
|
15
16
|
export interface SerializedUpdateOrganizationOptions {
|
|
16
17
|
name?: string;
|
|
17
18
|
domain_data?: DomainData[];
|
|
19
|
+
stripe_customer_id?: string | null;
|
|
18
20
|
/**
|
|
19
21
|
* @deprecated If you need to allow sign-ins from any email domain, contact support@workos.com.
|
|
20
22
|
*/
|
|
@@ -15,11 +15,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
const jest_fetch_mock_1 = __importDefault(require("jest-fetch-mock"));
|
|
16
16
|
const test_utils_1 = require("../common/utils/test-utils");
|
|
17
17
|
const workos_1 = require("../workos");
|
|
18
|
+
const clear_stripe_customer_id_json_1 = __importDefault(require("./fixtures/clear-stripe-customer-id.json"));
|
|
18
19
|
const create_organization_invalid_json_1 = __importDefault(require("./fixtures/create-organization-invalid.json"));
|
|
19
20
|
const create_organization_json_1 = __importDefault(require("./fixtures/create-organization.json"));
|
|
20
21
|
const get_organization_json_1 = __importDefault(require("./fixtures/get-organization.json"));
|
|
21
22
|
const list_organizations_json_1 = __importDefault(require("./fixtures/list-organizations.json"));
|
|
22
23
|
const update_organization_json_1 = __importDefault(require("./fixtures/update-organization.json"));
|
|
24
|
+
const set_stripe_customer_id_json_1 = __importDefault(require("./fixtures/set-stripe-customer-id.json"));
|
|
25
|
+
const set_stripe_customer_id_disabled_json_1 = __importDefault(require("./fixtures/set-stripe-customer-id-disabled.json"));
|
|
23
26
|
const interfaces_1 = require("./interfaces");
|
|
24
27
|
const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
|
|
25
28
|
describe('Organizations', () => {
|
|
@@ -235,5 +238,41 @@ describe('Organizations', () => {
|
|
|
235
238
|
}));
|
|
236
239
|
});
|
|
237
240
|
});
|
|
241
|
+
describe('when given `stripeCustomerId`', () => {
|
|
242
|
+
it('updates the organization’s Stripe customer ID', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
243
|
+
(0, test_utils_1.fetchOnce)(set_stripe_customer_id_json_1.default);
|
|
244
|
+
const subject = yield workos.organizations.updateOrganization({
|
|
245
|
+
organization: 'org_01EHT88Z8J8795GZNQ4ZP1J81T',
|
|
246
|
+
stripeCustomerId: 'cus_MX8J9nfK4lP2Yw',
|
|
247
|
+
});
|
|
248
|
+
expect((0, test_utils_1.fetchBody)()).toMatchObject({
|
|
249
|
+
stripe_customer_id: 'cus_MX8J9nfK4lP2Yw',
|
|
250
|
+
});
|
|
251
|
+
expect(subject.stripeCustomerId).toBe('cus_MX8J9nfK4lP2Yw');
|
|
252
|
+
}));
|
|
253
|
+
it('clears the organization’s Stripe customer ID with a `null` value', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
254
|
+
(0, test_utils_1.fetchOnce)(clear_stripe_customer_id_json_1.default);
|
|
255
|
+
const subject = yield workos.organizations.updateOrganization({
|
|
256
|
+
organization: 'org_01EHT88Z8J8795GZNQ4ZP1J81T',
|
|
257
|
+
stripeCustomerId: null,
|
|
258
|
+
});
|
|
259
|
+
expect((0, test_utils_1.fetchBody)()).toEqual({
|
|
260
|
+
stripe_customer_id: null,
|
|
261
|
+
});
|
|
262
|
+
expect(subject.stripeCustomerId).toBeUndefined();
|
|
263
|
+
}));
|
|
264
|
+
describe('when the feature is not enabled', () => {
|
|
265
|
+
it('returns an error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
266
|
+
(0, test_utils_1.fetchOnce)(set_stripe_customer_id_disabled_json_1.default, { status: 422 });
|
|
267
|
+
yield expect(workos.organizations.updateOrganization({
|
|
268
|
+
organization: 'org_01EHT88Z8J8795GZNQ4ZP1J81T',
|
|
269
|
+
stripeCustomerId: 'cus_MX8J9nfK4lP2Yw',
|
|
270
|
+
})).rejects.toThrowError('stripe_customer_id is not enabled for this environment');
|
|
271
|
+
expect((0, test_utils_1.fetchBody)()).toEqual({
|
|
272
|
+
stripe_customer_id: 'cus_MX8J9nfK4lP2Yw',
|
|
273
|
+
});
|
|
274
|
+
}));
|
|
275
|
+
});
|
|
276
|
+
});
|
|
238
277
|
});
|
|
239
278
|
});
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.deserializeOrganization = void 0;
|
|
4
4
|
const organization_domain_serializer_1 = require("../../organization-domains/serializers/organization-domain.serializer");
|
|
5
|
-
const deserializeOrganization = (organization) => ({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
name: organization.name,
|
|
9
|
-
allowProfilesOutsideOrganization: organization.allow_profiles_outside_organization,
|
|
10
|
-
domains: organization.domains.map(organization_domain_serializer_1.deserializeOrganizationDomain),
|
|
11
|
-
createdAt: organization.created_at,
|
|
12
|
-
updatedAt: organization.updated_at,
|
|
13
|
-
});
|
|
5
|
+
const deserializeOrganization = (organization) => (Object.assign(Object.assign({ object: organization.object, id: organization.id, name: organization.name, allowProfilesOutsideOrganization: organization.allow_profiles_outside_organization, domains: organization.domains.map(organization_domain_serializer_1.deserializeOrganizationDomain) }, (typeof organization.stripe_customer_id === 'undefined'
|
|
6
|
+
? undefined
|
|
7
|
+
: { stripeCustomerId: organization.stripe_customer_id })), { createdAt: organization.created_at, updatedAt: organization.updated_at }));
|
|
14
8
|
exports.deserializeOrganization = deserializeOrganization;
|
|
@@ -6,5 +6,6 @@ const serializeUpdateOrganizationOptions = (options) => ({
|
|
|
6
6
|
allow_profiles_outside_organization: options.allowProfilesOutsideOrganization,
|
|
7
7
|
domain_data: options.domainData,
|
|
8
8
|
domains: options.domains,
|
|
9
|
+
stripe_customer_id: options.stripeCustomerId,
|
|
9
10
|
});
|
|
10
11
|
exports.serializeUpdateOrganizationOptions = serializeUpdateOrganizationOptions;
|
|
@@ -10,6 +10,7 @@ export interface AccessToken {
|
|
|
10
10
|
org_id?: string;
|
|
11
11
|
role?: string;
|
|
12
12
|
permissions?: string[];
|
|
13
|
+
entitlements?: string[];
|
|
13
14
|
}
|
|
14
15
|
export type SessionCookieData = Pick<AuthenticationResponse, 'accessToken' | 'impersonator' | 'organizationId' | 'refreshToken' | 'user'>;
|
|
15
16
|
export declare enum AuthenticateWithSessionCookieFailureReason {
|
|
@@ -27,6 +28,7 @@ export type AuthenticateWithSessionCookieSuccessResponse = {
|
|
|
27
28
|
organizationId?: string;
|
|
28
29
|
role?: string;
|
|
29
30
|
permissions?: string[];
|
|
31
|
+
entitlements?: string[];
|
|
30
32
|
user: User;
|
|
31
33
|
impersonator?: Impersonator;
|
|
32
34
|
};
|
|
@@ -64,13 +64,14 @@ class Session {
|
|
|
64
64
|
reason: interfaces_1.AuthenticateWithSessionCookieFailureReason.INVALID_JWT,
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
const { sid: sessionId, org_id: organizationId, role, permissions, } = (0, jose_1.decodeJwt)(session.accessToken);
|
|
67
|
+
const { sid: sessionId, org_id: organizationId, role, permissions, entitlements, } = (0, jose_1.decodeJwt)(session.accessToken);
|
|
68
68
|
return {
|
|
69
69
|
authenticated: true,
|
|
70
70
|
sessionId,
|
|
71
71
|
organizationId,
|
|
72
72
|
role,
|
|
73
73
|
permissions,
|
|
74
|
+
entitlements,
|
|
74
75
|
user: session.user,
|
|
75
76
|
impersonator: session.impersonator,
|
|
76
77
|
};
|
|
@@ -120,7 +120,7 @@ describe('Session', () => {
|
|
|
120
120
|
.mockResolvedValue({});
|
|
121
121
|
const cookiePassword = 'alongcookiesecretmadefortestingsessions';
|
|
122
122
|
const sessionData = yield (0, iron_session_1.sealData)({
|
|
123
|
-
accessToken: '
|
|
123
|
+
accessToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkIjp0cnVlLCJpbXBlcnNvbmF0b3IiOnsiZW1haWwiOiJhZG1pbkBleGFtcGxlLmNvbSIsInJlYXNvbiI6InRlc3QifSwic2lkIjoic2Vzc2lvbl8xMjMiLCJvcmdfaWQiOiJvcmdfMTIzIiwicm9sZSI6Im1lbWJlciIsInBlcm1pc3Npb25zIjpbInBvc3RzOmNyZWF0ZSIsInBvc3RzOmRlbGV0ZSJdLCJlbnRpdGxlbWVudHMiOlsiYXVkaXQtbG9ncyJdLCJ1c2VyIjp7Im9iamVjdCI6InVzZXIiLCJpZCI6InVzZXJfMDFINUpRRFY3UjdBVEVZWkRFRzBXNVBSWVMiLCJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifX0.A8mDST4wtq_0vId6ALg7k2Ukr7FXrszZtdJ_6dfXeAc',
|
|
124
124
|
refreshToken: 'def456',
|
|
125
125
|
impersonator: {
|
|
126
126
|
email: 'admin@example.com',
|
|
@@ -146,6 +146,7 @@ describe('Session', () => {
|
|
|
146
146
|
organizationId: 'org_123',
|
|
147
147
|
role: 'member',
|
|
148
148
|
permissions: ['posts:create', 'posts:delete'],
|
|
149
|
+
entitlements: ['audit-logs'],
|
|
149
150
|
user: {
|
|
150
151
|
object: 'user',
|
|
151
152
|
id: 'user_01H5JQDV7R7ATEYZDEG0W5PRYS',
|
|
@@ -192,7 +192,7 @@ class UserManagement {
|
|
|
192
192
|
reason: authenticate_with_session_cookie_interface_1.AuthenticateWithSessionCookieFailureReason.INVALID_JWT,
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
|
-
const { sid: sessionId, org_id: organizationId, role, permissions, } = (0, jose_1.decodeJwt)(session.accessToken);
|
|
195
|
+
const { sid: sessionId, org_id: organizationId, role, permissions, entitlements, } = (0, jose_1.decodeJwt)(session.accessToken);
|
|
196
196
|
return {
|
|
197
197
|
authenticated: true,
|
|
198
198
|
sessionId,
|
|
@@ -200,6 +200,7 @@ class UserManagement {
|
|
|
200
200
|
role,
|
|
201
201
|
user: session.user,
|
|
202
202
|
permissions,
|
|
203
|
+
entitlements,
|
|
203
204
|
};
|
|
204
205
|
});
|
|
205
206
|
}
|
|
@@ -720,7 +720,7 @@ describe('UserManagement', () => {
|
|
|
720
720
|
.mockResolvedValue({});
|
|
721
721
|
const cookiePassword = 'alongcookiesecretmadefortestingsessions';
|
|
722
722
|
const sessionData = yield (0, iron_session_1.sealData)({
|
|
723
|
-
accessToken: '
|
|
723
|
+
accessToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkIjp0cnVlLCJpbXBlcnNvbmF0b3IiOnsiZW1haWwiOiJhZG1pbkBleGFtcGxlLmNvbSIsInJlYXNvbiI6InRlc3QifSwic2lkIjoic2Vzc2lvbl8xMjMiLCJvcmdfaWQiOiJvcmdfMTIzIiwicm9sZSI6Im1lbWJlciIsInBlcm1pc3Npb25zIjpbInBvc3RzOmNyZWF0ZSIsInBvc3RzOmRlbGV0ZSJdLCJlbnRpdGxlbWVudHMiOlsiYXVkaXQtbG9ncyJdLCJ1c2VyIjp7Im9iamVjdCI6InVzZXIiLCJpZCI6InVzZXJfMDFINUpRRFY3UjdBVEVZWkRFRzBXNVBSWVMiLCJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifX0.A8mDST4wtq_0vId6ALg7k2Ukr7FXrszZtdJ_6dfXeAc',
|
|
724
724
|
refreshToken: 'def456',
|
|
725
725
|
user: {
|
|
726
726
|
object: 'user',
|
|
@@ -737,6 +737,7 @@ describe('UserManagement', () => {
|
|
|
737
737
|
organizationId: 'org_123',
|
|
738
738
|
role: 'member',
|
|
739
739
|
permissions: ['posts:create', 'posts:delete'],
|
|
740
|
+
entitlements: ['audit-logs'],
|
|
740
741
|
user: expect.objectContaining({
|
|
741
742
|
email: 'test@example.com',
|
|
742
743
|
}),
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { Event } from '../common/interfaces';
|
|
2
2
|
import { CryptoProvider } from '../common/crypto/crypto-provider';
|
|
3
3
|
export declare class Webhooks {
|
|
4
|
-
private
|
|
4
|
+
private signatureProvider;
|
|
5
5
|
constructor(cryptoProvider: CryptoProvider);
|
|
6
|
+
get verifyHeader(): ({ payload, sigHeader, secret, tolerance, }: {
|
|
7
|
+
payload: any;
|
|
8
|
+
sigHeader: string;
|
|
9
|
+
secret: string;
|
|
10
|
+
tolerance?: number | undefined;
|
|
11
|
+
}) => Promise<boolean>;
|
|
12
|
+
get computeSignature(): (timestamp: any, payload: any, secret: string) => Promise<string>;
|
|
13
|
+
get getTimestampAndSignatureHash(): (sigHeader: string) => [string, string];
|
|
6
14
|
constructEvent({ payload, sigHeader, secret, tolerance, }: {
|
|
7
15
|
payload: unknown;
|
|
8
16
|
sigHeader: string;
|
|
9
17
|
secret: string;
|
|
10
18
|
tolerance?: number;
|
|
11
19
|
}): Promise<Event>;
|
|
12
|
-
verifyHeader({ payload, sigHeader, secret, tolerance, }: {
|
|
13
|
-
payload: any;
|
|
14
|
-
sigHeader: string;
|
|
15
|
-
secret: string;
|
|
16
|
-
tolerance?: number;
|
|
17
|
-
}): Promise<boolean>;
|
|
18
|
-
getTimestampAndSignatureHash(sigHeader: string): [string, string];
|
|
19
|
-
computeSignature(timestamp: any, payload: any, secret: string): Promise<string>;
|
|
20
20
|
}
|
package/lib/webhooks/webhooks.js
CHANGED
|
@@ -10,11 +10,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Webhooks = void 0;
|
|
13
|
-
const exceptions_1 = require("../common/exceptions");
|
|
14
13
|
const serializers_1 = require("../common/serializers");
|
|
14
|
+
const SignatureProvider_1 = require("../common/crypto/SignatureProvider");
|
|
15
15
|
class Webhooks {
|
|
16
16
|
constructor(cryptoProvider) {
|
|
17
|
-
this.
|
|
17
|
+
this.signatureProvider = new SignatureProvider_1.SignatureProvider(cryptoProvider);
|
|
18
|
+
}
|
|
19
|
+
get verifyHeader() {
|
|
20
|
+
return this.signatureProvider.verifyHeader.bind(this.signatureProvider);
|
|
21
|
+
}
|
|
22
|
+
get computeSignature() {
|
|
23
|
+
return this.signatureProvider.computeSignature.bind(this.signatureProvider);
|
|
24
|
+
}
|
|
25
|
+
get getTimestampAndSignatureHash() {
|
|
26
|
+
return this.signatureProvider.getTimestampAndSignatureHash.bind(this.signatureProvider);
|
|
18
27
|
}
|
|
19
28
|
constructEvent({ payload, sigHeader, secret, tolerance = 180000, }) {
|
|
20
29
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -24,39 +33,5 @@ class Webhooks {
|
|
|
24
33
|
return (0, serializers_1.deserializeEvent)(webhookPayload);
|
|
25
34
|
});
|
|
26
35
|
}
|
|
27
|
-
verifyHeader({ payload, sigHeader, secret, tolerance = 180000, }) {
|
|
28
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
-
const [timestamp, signatureHash] = this.getTimestampAndSignatureHash(sigHeader);
|
|
30
|
-
if (!signatureHash || Object.keys(signatureHash).length === 0) {
|
|
31
|
-
throw new exceptions_1.SignatureVerificationException('No signature hash found with expected scheme v1');
|
|
32
|
-
}
|
|
33
|
-
if (parseInt(timestamp, 10) < Date.now() - tolerance) {
|
|
34
|
-
throw new exceptions_1.SignatureVerificationException('Timestamp outside the tolerance zone');
|
|
35
|
-
}
|
|
36
|
-
const expectedSig = yield this.computeSignature(timestamp, payload, secret);
|
|
37
|
-
if ((yield this.cryptoProvider.secureCompare(expectedSig, signatureHash)) ===
|
|
38
|
-
false) {
|
|
39
|
-
throw new exceptions_1.SignatureVerificationException('Signature hash does not match the expected signature hash for payload');
|
|
40
|
-
}
|
|
41
|
-
return true;
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
getTimestampAndSignatureHash(sigHeader) {
|
|
45
|
-
const signature = sigHeader;
|
|
46
|
-
const [t, v1] = signature.split(',');
|
|
47
|
-
if (typeof t === 'undefined' || typeof v1 === 'undefined') {
|
|
48
|
-
throw new exceptions_1.SignatureVerificationException('Signature or timestamp missing');
|
|
49
|
-
}
|
|
50
|
-
const { 1: timestamp } = t.split('=');
|
|
51
|
-
const { 1: signatureHash } = v1.split('=');
|
|
52
|
-
return [timestamp, signatureHash];
|
|
53
|
-
}
|
|
54
|
-
computeSignature(timestamp, payload, secret) {
|
|
55
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
-
payload = JSON.stringify(payload);
|
|
57
|
-
const signedPayload = `${timestamp}.${payload}`;
|
|
58
|
-
return yield this.cryptoProvider.computeHMACSignatureAsync(signedPayload, secret);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
36
|
}
|
|
62
37
|
exports.Webhooks = Webhooks;
|
|
@@ -17,8 +17,6 @@ const workos_1 = require("../workos");
|
|
|
17
17
|
const webhook_json_1 = __importDefault(require("./fixtures/webhook.json"));
|
|
18
18
|
const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
|
|
19
19
|
const exceptions_1 = require("../common/exceptions");
|
|
20
|
-
const node_crypto_provider_1 = require("../common/crypto/node-crypto-provider");
|
|
21
|
-
const subtle_crypto_provider_1 = require("../common/crypto/subtle-crypto-provider");
|
|
22
20
|
describe('Webhooks', () => {
|
|
23
21
|
let payload;
|
|
24
22
|
let secret;
|
|
@@ -166,55 +164,34 @@ describe('Webhooks', () => {
|
|
|
166
164
|
});
|
|
167
165
|
});
|
|
168
166
|
describe('verifyHeader', () => {
|
|
169
|
-
it('
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
167
|
+
it('aliases to the signature provider', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
+
const spy = jest.spyOn(
|
|
169
|
+
// tslint:disable-next-line
|
|
170
|
+
workos.webhooks['signatureProvider'], 'verifyHeader');
|
|
171
|
+
yield workos.webhooks.verifyHeader({
|
|
172
|
+
payload,
|
|
173
|
+
sigHeader: `t=${timestamp}, v1=${signatureHash}`,
|
|
174
|
+
secret,
|
|
175
|
+
});
|
|
176
|
+
expect(spy).toHaveBeenCalled();
|
|
174
177
|
}));
|
|
175
178
|
});
|
|
176
|
-
describe('getTimestampAndSignatureHash', () => {
|
|
177
|
-
it('returns the timestamp and signature when the signature is valid', () => {
|
|
178
|
-
const sigHeader = `t=${timestamp}, v1=${signatureHash}`;
|
|
179
|
-
const timestampAndSignature = workos.webhooks.getTimestampAndSignatureHash(sigHeader);
|
|
180
|
-
expect(timestampAndSignature).toEqual([
|
|
181
|
-
timestamp.toString(),
|
|
182
|
-
signatureHash,
|
|
183
|
-
]);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
179
|
describe('computeSignature', () => {
|
|
187
|
-
it('
|
|
188
|
-
const
|
|
189
|
-
|
|
180
|
+
it('aliases to the signature provider', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
181
|
+
const spy = jest.spyOn(
|
|
182
|
+
// tslint:disable-next-line
|
|
183
|
+
workos.webhooks['signatureProvider'], 'computeSignature');
|
|
184
|
+
yield workos.webhooks.computeSignature(timestamp, payload, secret);
|
|
185
|
+
expect(spy).toHaveBeenCalled();
|
|
190
186
|
}));
|
|
191
187
|
});
|
|
192
|
-
describe('
|
|
193
|
-
it('
|
|
188
|
+
describe('getTimestampAndSignatureHash', () => {
|
|
189
|
+
it('aliases to the signature provider', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
190
|
+
const spy = jest.spyOn(
|
|
194
191
|
// tslint:disable-next-line
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
describe('when computing HMAC signature', () => {
|
|
200
|
-
it('returns the same for the Node crypto and Web Crypto versions', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
201
|
-
const nodeCryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
202
|
-
const subtleCryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
203
|
-
const stringifiedPayload = JSON.stringify(payload);
|
|
204
|
-
const payloadHMAC = `${timestamp}.${stringifiedPayload}`;
|
|
205
|
-
const nodeCompare = yield nodeCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
|
|
206
|
-
const subtleCompare = yield subtleCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
|
|
207
|
-
expect(nodeCompare).toEqual(subtleCompare);
|
|
208
|
-
}));
|
|
209
|
-
});
|
|
210
|
-
describe('when securely comparing', () => {
|
|
211
|
-
it('returns the same for the Node crypto and Web Crypto versions', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
212
|
-
const nodeCryptoProvider = new node_crypto_provider_1.NodeCryptoProvider();
|
|
213
|
-
const subtleCryptoProvider = new subtle_crypto_provider_1.SubtleCryptoProvider();
|
|
214
|
-
const signature = yield workos.webhooks.computeSignature(timestamp, payload, secret);
|
|
215
|
-
expect(nodeCryptoProvider.secureCompare(signature, signatureHash)).toEqual(subtleCryptoProvider.secureCompare(signature, signatureHash));
|
|
216
|
-
expect(nodeCryptoProvider.secureCompare(signature, 'foo')).toEqual(subtleCryptoProvider.secureCompare(signature, 'foo'));
|
|
217
|
-
}));
|
|
218
|
-
});
|
|
192
|
+
workos.webhooks['signatureProvider'], 'getTimestampAndSignatureHash');
|
|
193
|
+
workos.webhooks.getTimestampAndSignatureHash(`t=${timestamp}, v1=${signatureHash}`);
|
|
194
|
+
expect(spy).toHaveBeenCalled();
|
|
195
|
+
}));
|
|
219
196
|
});
|
|
220
197
|
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type WidgetScope = 'widgets:users-table:manage';
|
|
2
|
+
export interface GetTokenOptions {
|
|
3
|
+
organizationId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
scopes: [WidgetScope];
|
|
6
|
+
}
|
|
7
|
+
export interface SerializedGetTokenOptions {
|
|
8
|
+
organization_id: string;
|
|
9
|
+
user_id: string;
|
|
10
|
+
scopes: [WidgetScope];
|
|
11
|
+
}
|
|
12
|
+
export declare const serializeGetTokenOptions: (options: GetTokenOptions) => SerializedGetTokenOptions;
|
|
13
|
+
export interface GetTokenResponse {
|
|
14
|
+
token: string;
|
|
15
|
+
}
|
|
16
|
+
export interface GetTokenResponseResponse {
|
|
17
|
+
token: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const deserializeGetTokenResponse: (data: GetTokenResponseResponse) => GetTokenResponse;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deserializeGetTokenResponse = exports.serializeGetTokenOptions = void 0;
|
|
4
|
+
const serializeGetTokenOptions = (options) => ({
|
|
5
|
+
organization_id: options.organizationId,
|
|
6
|
+
user_id: options.userId,
|
|
7
|
+
scopes: options.scopes,
|
|
8
|
+
});
|
|
9
|
+
exports.serializeGetTokenOptions = serializeGetTokenOptions;
|
|
10
|
+
const deserializeGetTokenResponse = (data) => ({
|
|
11
|
+
token: data.token,
|
|
12
|
+
});
|
|
13
|
+
exports.deserializeGetTokenResponse = deserializeGetTokenResponse;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Widgets = void 0;
|
|
13
|
+
const get_token_1 = require("./interfaces/get-token");
|
|
14
|
+
class Widgets {
|
|
15
|
+
constructor(workos) {
|
|
16
|
+
this.workos = workos;
|
|
17
|
+
}
|
|
18
|
+
getToken(payload) {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
const { data } = yield this.workos.post('/widgets/token', (0, get_token_1.serializeGetTokenOptions)(payload));
|
|
21
|
+
return (0, get_token_1.deserializeGetTokenResponse)(data).token;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.Widgets = Widgets;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const jest_fetch_mock_1 = __importDefault(require("jest-fetch-mock"));
|
|
16
|
+
const workos_1 = require("../workos");
|
|
17
|
+
const test_utils_1 = require("../common/utils/test-utils");
|
|
18
|
+
const token_json_1 = __importDefault(require("./fixtures/token.json"));
|
|
19
|
+
const get_token_error_json_1 = __importDefault(require("./fixtures/get-token-error.json"));
|
|
20
|
+
describe('Widgets', () => {
|
|
21
|
+
let workos;
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', {
|
|
24
|
+
apiHostname: 'api.workos.test',
|
|
25
|
+
clientId: 'proj_123',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
beforeEach(() => jest_fetch_mock_1.default.resetMocks());
|
|
29
|
+
describe('getToken', () => {
|
|
30
|
+
it('sends a Get Token request', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
+
(0, test_utils_1.fetchOnce)(token_json_1.default);
|
|
32
|
+
const token = yield workos.widgets.getToken({
|
|
33
|
+
organizationId: 'org_123',
|
|
34
|
+
userId: 'user_123',
|
|
35
|
+
scopes: ['widgets:users-table:manage'],
|
|
36
|
+
});
|
|
37
|
+
expect((0, test_utils_1.fetchURL)()).toContain('/widgets/token');
|
|
38
|
+
expect(token).toEqual('this.is.a.token');
|
|
39
|
+
}));
|
|
40
|
+
it('returns an error if the API returns an error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
|
+
(0, test_utils_1.fetchOnce)(get_token_error_json_1.default, { status: 404 });
|
|
42
|
+
yield expect(workos.widgets.getToken({
|
|
43
|
+
organizationId: 'org_123',
|
|
44
|
+
userId: 'user_123',
|
|
45
|
+
scopes: ['widgets:users-table:manage'],
|
|
46
|
+
})).rejects.toThrow("User not found 'user_123'");
|
|
47
|
+
}));
|
|
48
|
+
});
|
|
49
|
+
});
|