Socket
Socket
Sign inDemoInstall

@workos-inc/node

Package Overview
Dependencies
Maintainers
7
Versions
155
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@workos-inc/node - npm Package Compare versions

Comparing version 7.9.0 to 7.10.0

lib/common/crypto/CryptoProvider.d.ts

1

lib/common/interfaces/index.d.ts

@@ -10,1 +10,2 @@ export * from './event.interface';

export * from './pagination-options.interface';
export * from './http-client.interface';

@@ -26,1 +26,2 @@ "use strict";

__exportStar(require("./pagination-options.interface"), exports);
__exportStar(require("./http-client.interface"), exports);

@@ -8,2 +8,3 @@ import { AppInfo } from './app-info.interface';

appInfo?: AppInfo;
fetchFn?: typeof fetch;
}

4

lib/webhooks/webhooks.d.ts
import { Event } from '../common/interfaces';
export declare class Webhooks {
private encoder;
private cryptoProvider;
constructor(subtleCrypto?: typeof crypto.subtle);
constructEvent({ payload, sigHeader, secret, tolerance, }: {

@@ -18,3 +19,2 @@ payload: unknown;

computeSignature(timestamp: any, payload: any, secret: string): Promise<string>;
secureCompare(stringA: string, stringB: string): Promise<boolean>;
}

@@ -15,5 +15,11 @@ "use strict";

const serializers_1 = require("../common/serializers");
const crypto_1 = require("../common/crypto");
class Webhooks {
constructor() {
this.encoder = new TextEncoder();
constructor(subtleCrypto) {
if (typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined') {
this.cryptoProvider = new crypto_1.SubtleCryptoProvider(subtleCrypto);
}
else {
this.cryptoProvider = new crypto_1.NodeCryptoProvider();
}
}

@@ -38,3 +44,4 @@ constructEvent({ payload, sigHeader, secret, tolerance = 180000, }) {

const expectedSig = yield this.computeSignature(timestamp, payload, secret);
if ((yield this.secureCompare(expectedSig, signatureHash)) === false) {
if ((yield this.cryptoProvider.secureCompare(expectedSig, signatureHash)) ===
false) {
throw new exceptions_1.SignatureVerificationException('Signature hash does not match the expected signature hash for payload');

@@ -59,39 +66,6 @@ }

const signedPayload = `${timestamp}.${payload}`;
const key = yield crypto.subtle.importKey('raw', this.encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
const signatureBuffer = yield crypto.subtle.sign('HMAC', key, this.encoder.encode(signedPayload));
// crypto.subtle returns the signature in base64 format. This must be
// encoded in hex to match the CryptoProvider contract. We map each byte in
// the buffer to its corresponding hex octet and then combine into a string.
const signatureBytes = new Uint8Array(signatureBuffer);
const signatureHexCodes = new Array(signatureBytes.length);
for (let i = 0; i < signatureBytes.length; i++) {
signatureHexCodes[i] = byteHexMapping[signatureBytes[i]];
}
return signatureHexCodes.join('');
return yield this.cryptoProvider.computeHMACSignatureAsync(signedPayload, secret);
});
}
secureCompare(stringA, stringB) {
return __awaiter(this, void 0, void 0, function* () {
const bufferA = this.encoder.encode(stringA);
const bufferB = this.encoder.encode(stringB);
if (bufferA.length !== bufferB.length) {
return false;
}
const algorithm = { name: 'HMAC', hash: 'SHA-256' };
const key = (yield crypto.subtle.generateKey(algorithm, false, [
'sign',
'verify',
]));
const hmac = yield crypto.subtle.sign(algorithm, key, bufferA);
const equal = yield crypto.subtle.verify(algorithm, key, hmac, bufferB);
return equal;
});
}
}
exports.Webhooks = Webhooks;
// Cached mapping of byte to hex representation. We do this once to avoid re-
// computing every time we need to convert the result of a signature to hex.
const byteHexMapping = new Array(256);
for (let i = 0; i < byteHexMapping.length; i++) {
byteHexMapping[i] = i.toString(16).padStart(2, '0');
}

@@ -20,2 +20,3 @@ "use strict";

const exceptions_1 = require("../common/exceptions");
const crypto_2 = require("../common/crypto");
describe('Webhooks', () => {

@@ -191,2 +192,30 @@ let payload;

});
describe('when in an environment that supports SubtleCrypto', () => {
it('automatically uses the subtle crypto library', () => {
// tslint:disable-next-line
expect(workos.webhooks['cryptoProvider']).toBeInstanceOf(crypto_2.SubtleCryptoProvider);
});
});
describe('CryptoProvider', () => {
describe('when computing HMAC signature', () => {
it('returns the same for the Node crypto and Web Crypto versions', () => __awaiter(void 0, void 0, void 0, function* () {
const nodeCryptoProvider = new crypto_2.NodeCryptoProvider();
const subtleCryptoProvider = new crypto_2.SubtleCryptoProvider();
const stringifiedPayload = JSON.stringify(payload);
const payloadHMAC = `${timestamp}.${stringifiedPayload}`;
const nodeCompare = yield nodeCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
const subtleCompare = yield subtleCryptoProvider.computeHMACSignatureAsync(payloadHMAC, secret);
expect(nodeCompare).toEqual(subtleCompare);
}));
});
describe('when securely comparing', () => {
it('returns the same for the Node crypto and Web Crypto versions', () => __awaiter(void 0, void 0, void 0, function* () {
const nodeCryptoProvider = new crypto_2.NodeCryptoProvider();
const subtleCryptoProvider = new crypto_2.SubtleCryptoProvider();
const signature = yield workos.webhooks.computeSignature(timestamp, payload, secret);
expect(nodeCryptoProvider.secureCompare(signature, signatureHash)).toEqual(subtleCryptoProvider.secureCompare(signature, signatureHash));
expect(nodeCryptoProvider.secureCompare(signature, 'foo')).toEqual(subtleCryptoProvider.secureCompare(signature, 'foo'));
}));
});
});
});

@@ -42,3 +42,3 @@ import { GetOptions, PostOptions, PutOptions, WorkOSOptions } from './common/interfaces';

emitWarning(warning: string): void;
private handleFetchError;
private handleHttpError;
}

@@ -26,4 +26,4 @@ "use strict";

const bad_request_exception_1 = require("./common/exceptions/bad-request.exception");
const fetch_client_1 = require("./common/utils/fetch-client");
const VERSION = '7.9.0';
const net_1 = require("./common/net");
const VERSION = '7.10.0';
const DEFAULT_HOSTNAME = 'api.workos.com';

@@ -68,3 +68,3 @@ class WorkOS {

}
this.client = new fetch_client_1.FetchClient(this.baseURL, Object.assign(Object.assign({}, options.config), { headers: Object.assign(Object.assign({}, (_a = options.config) === null || _a === void 0 ? void 0 : _a.headers), { Authorization: `Bearer ${this.key}`, 'User-Agent': userAgent }) }));
this.client = (0, net_1.createHttpClient)(this.baseURL, Object.assign(Object.assign({}, options.config), { headers: Object.assign(Object.assign({}, (_a = options.config) === null || _a === void 0 ? void 0 : _a.headers), { Authorization: `Bearer ${this.key}`, 'User-Agent': userAgent }) }), options.fetchFn);
}

@@ -81,9 +81,10 @@ get version() {

try {
return yield this.client.post(path, entity, {
const res = yield this.client.post(path, entity, {
params: options.query,
headers: requestHeaders,
});
return { data: yield res.toJSON() };
}
catch (error) {
this.handleFetchError({ path, error });
this.handleHttpError({ path, error });
throw error;

@@ -97,3 +98,3 @@ }

const { accessToken } = options;
return yield this.client.get(path, {
const res = yield this.client.get(path, {
params: options.query,

@@ -104,5 +105,6 @@ headers: accessToken

});
return { data: yield res.toJSON() };
}
catch (error) {
this.handleFetchError({ path, error });
this.handleHttpError({ path, error });
throw error;

@@ -119,9 +121,10 @@ }

try {
return yield this.client.put(path, entity, {
const res = yield this.client.put(path, entity, {
params: options.query,
headers: requestHeaders,
});
return { data: yield res.toJSON() };
}
catch (error) {
this.handleFetchError({ path, error });
this.handleHttpError({ path, error });
throw error;

@@ -139,3 +142,3 @@ }

catch (error) {
this.handleFetchError({ path, error });
this.handleHttpError({ path, error });
throw error;

@@ -153,8 +156,11 @@ }

}
handleFetchError({ path, error }) {
handleHttpError({ path, error }) {
var _a;
if (!(error instanceof net_1.HttpClientError)) {
throw new Error(`Unexpected error: ${error}`);
}
const { response } = error;
if (response) {
const { status, data, headers } = response;
const requestID = (_a = headers.get('X-Request-ID')) !== null && _a !== void 0 ? _a : '';
const requestID = (_a = headers['X-Request-ID']) !== null && _a !== void 0 ? _a : '';
const { code, error_description: errorDescription, error, errors, message, } = data;

@@ -161,0 +167,0 @@ switch (status) {

@@ -21,2 +21,3 @@ "use strict";

const rate_limit_exceeded_exception_1 = require("./common/exceptions/rate-limit-exceeded.exception");
const net_1 = require("./common/net");
describe('WorkOS', () => {

@@ -99,6 +100,36 @@ beforeEach(() => jest_fetch_mock_1.default.resetMocks());

expect((0, test_utils_1.fetchHeaders)()).toMatchObject({
'User-Agent': `workos-node/${packageJson.version} fooApp: 1.0.0`,
'User-Agent': `workos-node/${packageJson.version}/fetch fooApp: 1.0.0`,
});
}));
});
describe('when no `appInfo` option is provided', () => {
it('adds the HTTP client name to the user-agent', () => __awaiter(void 0, void 0, void 0, function* () {
(0, test_utils_1.fetchOnce)('{}');
const packageJson = JSON.parse(yield promises_1.default.readFile('package.json', 'utf8'));
const workos = new workos_1.WorkOS('sk_test');
yield workos.post('/somewhere', {});
expect((0, test_utils_1.fetchHeaders)()).toMatchObject({
'User-Agent': `workos-node/${packageJson.version}/fetch`,
});
}));
});
describe('when no `appInfo` option is provided', () => {
it('adds the HTTP client name to the user-agent', () => __awaiter(void 0, void 0, void 0, function* () {
(0, test_utils_1.fetchOnce)('{}');
const packageJson = JSON.parse(yield promises_1.default.readFile('package.json', 'utf8'));
const workos = new workos_1.WorkOS('sk_test');
yield workos.post('/somewhere', {});
expect((0, test_utils_1.fetchHeaders)()).toMatchObject({
'User-Agent': `workos-node/${packageJson.version}/fetch`,
});
}));
});
describe('when using an environment that supports fetch', () => {
it('automatically uses the fetch HTTP client', () => {
const workos = new workos_1.WorkOS('sk_test');
// Bracket notation gets past private visibility
// tslint:disable-next-line
expect(workos['client']).toBeInstanceOf(net_1.FetchHttpClient);
});
});
});

@@ -182,10 +213,32 @@ describe('version', () => {

describe('when the entity is null', () => {
it('sends a null body', () => __awaiter(void 0, void 0, void 0, function* () {
it('sends an empty string body', () => __awaiter(void 0, void 0, void 0, function* () {
(0, test_utils_1.fetchOnce)();
const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
yield workos.post('/somewhere', null);
expect((0, test_utils_1.fetchBody)({ raw: true })).toBeNull();
expect((0, test_utils_1.fetchBody)({ raw: true })).toBe('');
}));
});
});
describe('when in an environment that does not support fetch', () => {
const fetchFn = globalThis.fetch;
beforeEach(() => {
// @ts-ignore
delete globalThis.fetch;
});
afterEach(() => {
globalThis.fetch = fetchFn;
});
it('automatically uses the node HTTP client', () => {
const workos = new workos_1.WorkOS('sk_test_key');
// tslint:disable-next-line
expect(workos['client']).toBeInstanceOf(net_1.NodeHttpClient);
});
it('uses a fetch function if provided', () => {
const workos = new workos_1.WorkOS('sk_test_key', {
fetchFn,
});
// tslint:disable-next-line
expect(workos['client']).toBeInstanceOf(net_1.FetchHttpClient);
});
});
});
{
"version": "7.9.0",
"version": "7.10.0",
"name": "@workos-inc/node",

@@ -16,5 +16,4 @@ "author": "WorkOS",

"engines": {
"node": ">=19"
"node": ">=16"
},
"engineStrict": true,
"main": "lib/index.js",

@@ -21,0 +20,0 @@ "typings": "lib/index.d.ts",

@@ -12,2 +12,6 @@ # WorkOS Node.js Library

## Requirements
Node 16 or higher.
## Installation

@@ -14,0 +18,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc