From 369df5c68056c2094e691408c2ac34aff04984f9 Mon Sep 17 00:00:00 2001 From: Sokratis Vidros Date: Mon, 9 May 2022 16:26:33 +0300 Subject: [PATCH] feat(clerk-sdk-node): Teach node-sdk to work with fetch Polyfill global.fetch only in Node environments if necessary and increase interoperability for the SDK across Node and V8 runtime environments. Fixes https://github.com/clerkinc/javascript/issues/127 --- package-lock.json | 68 +- packages/backend-core/src/api/index.ts | 2 +- packages/edge/src/constants.ts | 10 - packages/edge/src/vercel-edge/ClerkAPI.ts | 52 +- packages/edge/src/vercel-edge/index.ts | 21 +- packages/edge/src/vercel-edge/utils/index.ts | 1 - .../utils/injectAuthIntoRequest.ts | 16 - packages/edge/vercel-edge/package.json | 6 - .../sdk-node/examples/node/package-lock.json | 782 +++++++++--------- packages/sdk-node/package.json | 4 +- packages/sdk-node/src/Clerk.ts | 213 ++--- .../sdk-node/src/__tests__/middleware.test.ts | 4 +- packages/sdk-node/src/utils/Crypto.ts | 43 + packages/sdk-node/src/utils/Encoding.ts | 13 + packages/sdk-node/src/utils/ErrorHandler.ts | 18 - packages/sdk-node/src/utils/Errors.ts | 42 - packages/sdk-node/src/utils/Logger.ts | 46 -- packages/sdk-node/src/utils/crypto.ts | 24 - packages/sdk-node/src/utils/index.ts | 2 + 19 files changed, 650 insertions(+), 717 deletions(-) delete mode 100644 packages/edge/src/constants.ts delete mode 100644 packages/edge/src/vercel-edge/utils/index.ts delete mode 100644 packages/edge/src/vercel-edge/utils/injectAuthIntoRequest.ts delete mode 100644 packages/edge/vercel-edge/package.json create mode 100644 packages/sdk-node/src/utils/Crypto.ts create mode 100644 packages/sdk-node/src/utils/Encoding.ts delete mode 100644 packages/sdk-node/src/utils/ErrorHandler.ts delete mode 100644 packages/sdk-node/src/utils/Errors.ts delete mode 100644 packages/sdk-node/src/utils/Logger.ts delete mode 100644 packages/sdk-node/src/utils/crypto.ts create mode 100644 packages/sdk-node/src/utils/index.ts diff --git a/package-lock.json b/package-lock.json index 1d22d8f67d3..2275a5201c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13054,7 +13054,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", - "dev": true, "engines": { "node": ">= 12" } @@ -15955,7 +15954,6 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz", "integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==", - "dev": true, "funding": [ { "type": "github", @@ -16375,7 +16373,6 @@ "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dev": true, "dependencies": { "fetch-blob": "^3.1.2" }, @@ -25741,7 +25738,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "dev": true, "funding": [ { "type": "github", @@ -34162,7 +34158,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", - "dev": true, "engines": { "node": ">= 8" } @@ -35181,9 +35176,7 @@ "devDependencies": { "@peculiar/webcrypto": "^1.3.2", "@types/jest": "^27.4.0", - "@types/node": "^16.11.12", "@types/node-fetch": "^2", - "got": "^11.0.0", "jest": "^27.4.7", "nock": "^13.2.1", "node-fetch": "^2.6.0", @@ -35191,12 +35184,6 @@ "typescript": "^4.6.4" } }, - "packages/backend-core/node_modules/@types/node": { - "version": "16.11.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.22.tgz", - "integrity": "sha512-DYNtJWauMQ9RNpesl4aVothr97/tIJM8HbyOXJ0AYT1Z2bEjLHyfjOBPAQQVMLf8h3kSShYfNk8Wnto8B2zHUA==", - "dev": true - }, "packages/clerk-js": { "name": "@clerk/clerk-js", "version": "3.15.0", @@ -35661,9 +35648,9 @@ "camelcase-keys": "^6.2.2", "cookies": "^0.8.0", "deepmerge": "^4.2.2", - "got": "^11.8.2", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^2.0.4", + "node-fetch": "^3.2.4", "snakecase-keys": "^3.2.1", "tslib": "^2.3.1" }, @@ -35680,7 +35667,7 @@ "typescript": "^4.6.4" }, "engines": { - "node": ">=12" + "node": ">=16" } }, "packages/sdk-node/node_modules/camelcase": { @@ -35707,6 +35694,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/sdk-node/node_modules/node-fetch": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.4.tgz", + "integrity": "sha512-WvYJRN7mMyOLurFR2YpysQGuwYrJN+qrrpHjJDuKMcSPdfFccRUla/kng2mz6HWSBxJcqPbvatS6Gb4RhOzCJw==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "packages/sdk-node/node_modules/quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -37032,25 +37036,14 @@ "@clerk/types": "^2.17.0", "@peculiar/webcrypto": "^1.3.2", "@types/jest": "^27.4.0", - "@types/node": "^16.11.12", "@types/node-fetch": "^2", - "got": "^11.0.0", "jest": "^27.4.7", "nock": "^13.2.1", "node-fetch": "^2.6.0", - "query-string": "^7.0.1", "snakecase-keys": "^5.1.2", "ts-jest": "^27.1.3", "tslib": "^2.3.1", "typescript": "^4.6.4" - }, - "dependencies": { - "@types/node": { - "version": "16.11.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.22.tgz", - "integrity": "sha512-DYNtJWauMQ9RNpesl4aVothr97/tIJM8HbyOXJ0AYT1Z2bEjLHyfjOBPAQQVMLf8h3kSShYfNk8Wnto8B2zHUA==", - "dev": true - } } }, "@clerk/clerk-expo": { @@ -37243,11 +37236,11 @@ "camelcase-keys": "^6.2.2", "cookies": "^0.8.0", "deepmerge": "^4.2.2", - "got": "^11.8.2", "jest": "^27.4.7", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^2.0.4", "nock": "^13.0.7", + "node-fetch": "^3.2.4", "npm-run-all": "^4.1.5", "prettier": "^2.5.0", "snakecase-keys": "^3.2.1", @@ -37271,6 +37264,16 @@ "quick-lru": "^4.0.1" } }, + "node-fetch": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.4.tgz", + "integrity": "sha512-WvYJRN7mMyOLurFR2YpysQGuwYrJN+qrrpHjJDuKMcSPdfFccRUla/kng2mz6HWSBxJcqPbvatS6Gb4RhOzCJw==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, "quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -46028,8 +46031,7 @@ "data-uri-to-buffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", - "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", - "dev": true + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" }, "data-urls": { "version": "2.0.0", @@ -48191,7 +48193,6 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz", "integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==", - "dev": true, "requires": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -48475,7 +48476,6 @@ "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dev": true, "requires": { "fetch-blob": "^3.1.2" } @@ -55711,8 +55711,7 @@ "node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "dev": true + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" }, "node-fetch": { "version": "2.6.7", @@ -62026,8 +62025,7 @@ "web-streams-polyfill": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", - "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", - "dev": true + "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==" }, "webcrypto-core": { "version": "1.7.1", diff --git a/packages/backend-core/src/api/index.ts b/packages/backend-core/src/api/index.ts index 5f9fee96dee..0bcc27a3019 100644 --- a/packages/backend-core/src/api/index.ts +++ b/packages/backend-core/src/api/index.ts @@ -2,5 +2,5 @@ export * from './ClerkBackendApi'; export * from './resources'; export { ClerkAPIResponseError } from './errors'; -export type { APIClient } from './endpoints/AbstractApi'; +export type { APIClient, APIRequestOptions } from './endpoints/AbstractApi'; export type { Session } from './resources/Session'; diff --git a/packages/edge/src/constants.ts b/packages/edge/src/constants.ts deleted file mode 100644 index 2432f64729a..00000000000 --- a/packages/edge/src/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { LIB_NAME, LIB_VERSION } from './info'; - -const SERVER_API_URL = 'https://api.clerk.dev'; -const apiVersion = 'v1'; -const INTERSTITIAL_path = '/internal/interstitial'; - -export const INTERSTITIAL_METHOD = 'GET'; -export const PACKAGE_REPO = 'https://github.com/clerkinc/clerk-sdk-edge'; -export const INTERSTITIAL_URL = `${SERVER_API_URL}/${apiVersion}${INTERSTITIAL_path}`; -export const SDK_USER_AGENT = `${LIB_NAME}/${LIB_VERSION} (${PACKAGE_REPO})`; diff --git a/packages/edge/src/vercel-edge/ClerkAPI.ts b/packages/edge/src/vercel-edge/ClerkAPI.ts index 24041035ff1..2c0fdbd2a4c 100644 --- a/packages/edge/src/vercel-edge/ClerkAPI.ts +++ b/packages/edge/src/vercel-edge/ClerkAPI.ts @@ -1,22 +1,46 @@ -import { ClerkBackendAPI } from '@clerk/backend-core'; +import { ClerkAPIResponseError, ClerkBackendAPI } from '@clerk/backend-core'; -import { PACKAGE_REPO } from '../constants'; import { LIB_NAME, LIB_VERSION } from '../info'; export const ClerkAPI = new ClerkBackendAPI({ libName: LIB_NAME, libVersion: LIB_VERSION, - packageRepo: PACKAGE_REPO, - fetcher: (url, { method, authorization, contentType, userAgent, body }) => { - return fetch(url, { - method, - headers: { - authorization: authorization, - 'Content-Type': contentType, - 'User-Agent': userAgent, - 'X-Clerk-SDK': `vercel-edge/${LIB_VERSION}`, - }, - ...(body && { body: JSON.stringify(body) }), - }).then(body => (contentType === 'text/html' ? body : body.json())); + apiClient: { + async request({ url, method, queryParams, headerParams, bodyParams }) { + // Build final URL with search parameters + const finalUrl = new URL(url || ''); + + if (queryParams) { + for (const [key, val] of Object.entries(queryParams as Record)) { + // Support array values for queryParams such as { foo: [42, 43] } + if (val) { + [val].flat().forEach(v => finalUrl.searchParams.append(key, v)); + } + } + } + + const response = await fetch(finalUrl.href, { + method, + headers: { + ...(headerParams as Record), + 'X-Clerk-SDK': `vercel-edge/${LIB_VERSION}`, + }, + ...(bodyParams && Object.keys(bodyParams).length > 0 && { body: JSON.stringify(bodyParams) }), + }); + + // Parse JSON or Text response. + const isJSONResponse = headerParams && headerParams['Content-Type'] === 'application/json'; + const data = await (isJSONResponse ? response.json() : response.text()); + + // Check for errors + if (!response.ok) { + throw new ClerkAPIResponseError(response.statusText, { + data: data?.errors || data, + status: response.status, + }); + } + + return data; + }, }, }); diff --git a/packages/edge/src/vercel-edge/index.ts b/packages/edge/src/vercel-edge/index.ts index e7c8e1a25f3..56a1f2729a8 100644 --- a/packages/edge/src/vercel-edge/index.ts +++ b/packages/edge/src/vercel-edge/index.ts @@ -2,14 +2,28 @@ import { AuthStatus, Base, createGetToken, createSignedOutState } from '@clerk/b import { ClerkJWTClaims } from '@clerk/types'; import { NextFetchEvent, NextRequest, NextResponse } from 'next/server'; -import { ClerkAPI } from './ClerkAPI'; +import { ClerkAPI } from './ClerkApi'; import { + AuthData, NextMiddlewareResult, + RequestWithAuth, WithEdgeMiddlewareAuthCallback, WithEdgeMiddlewareAuthMiddlewareResult, WithEdgeMiddlewareAuthOptions, } from './types'; -import { injectAuthIntoRequest } from './utils'; + +export function injectAuthIntoRequest(req: NextRequest, authData: AuthData): RequestWithAuth { + const { user, session, userId, sessionId, getToken, claims } = authData; + const auth = { + userId, + sessionId, + getToken, + claims, + }; + + /* Object.assign is used here as NextRequest properties also include Symbols */ + return Object.assign(req, { auth, user, session }); +} /** * @@ -55,8 +69,7 @@ const users = ClerkAPI.users; export { allowlistIdentifiers, clients, emails, invitations, organizations, sessions, smsMessages, users }; async function fetchInterstitial() { - const response = await ClerkAPI.fetchInterstitial(); - return response.text(); + return ClerkAPI.fetchInterstitial(); } export function withEdgeMiddlewareAuth< diff --git a/packages/edge/src/vercel-edge/utils/index.ts b/packages/edge/src/vercel-edge/utils/index.ts deleted file mode 100644 index 801966fa275..00000000000 --- a/packages/edge/src/vercel-edge/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './injectAuthIntoRequest'; diff --git a/packages/edge/src/vercel-edge/utils/injectAuthIntoRequest.ts b/packages/edge/src/vercel-edge/utils/injectAuthIntoRequest.ts deleted file mode 100644 index 1df52a93990..00000000000 --- a/packages/edge/src/vercel-edge/utils/injectAuthIntoRequest.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NextRequest } from 'next/server'; - -import { AuthData, RequestWithAuth } from '../types'; - -export function injectAuthIntoRequest(req: NextRequest, authData: AuthData): RequestWithAuth { - const { user, session, userId, sessionId, getToken, claims } = authData; - const auth = { - userId, - sessionId, - getToken, - claims, - }; - - /* Object.assign is used here as NextRequest properties also include Symbols */ - return Object.assign(req, { auth, user, session }); -} diff --git a/packages/edge/vercel-edge/package.json b/packages/edge/vercel-edge/package.json deleted file mode 100644 index 98c7952c226..00000000000 --- a/packages/edge/vercel-edge/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "vercel-edge", - "description": "Clerk for Vercel Edge", - "main": "../dist/cjs/vercel-edge/index.js", - "module": "../dist/mjs/vercel-edge/index.js" -} diff --git a/packages/sdk-node/examples/node/package-lock.json b/packages/sdk-node/examples/node/package-lock.json index 4c71c41e11d..58c7d8f3239 100644 --- a/packages/sdk-node/examples/node/package-lock.json +++ b/packages/sdk-node/examples/node/package-lock.json @@ -1,396 +1,396 @@ { - "name": "clerk-sdk-node-examples", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "clerk-sdk-node-examples", - "license": "MIT", - "dependencies": { - "@clerk/clerk-sdk-node": "file:../../dist", - "dotenv": "^8.6.0", - "log-that-http": "^1.0.1" - }, - "devDependencies": { - "@types/react-dom": "^17.0.1", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, - "../../dist": {}, - "node_modules/@clerk/clerk-sdk-node": { - "resolved": "../../dist", - "link": true - }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "node_modules/@types/prop-types": { - "version": "15.7.4", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", - "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", - "dev": true - }, - "node_modules/@types/react": { - "version": "17.0.38", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", - "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", - "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/csstype": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", - "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/log-that-http": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/log-that-http/-/log-that-http-1.0.1.tgz", - "integrity": "sha512-m382CmTTsDdf288oUdQdYMBDUisiQgXkqIIPw8stFqjSZwjgrg4ap7S3viniy7qv3KTnv3CCb/GFQWAuqLJu3Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/ts-node": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", - "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - } + "name": "clerk-sdk-node-examples", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "clerk-sdk-node-examples", + "license": "MIT", + "dependencies": { + "@clerk/clerk-sdk-node": "file:../../dist", + "dotenv": "^8.6.0", + "log-that-http": "^1.0.1" + }, + "devDependencies": { + "@types/react-dom": "^17.0.1", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } }, - "dependencies": { - "@clerk/clerk-sdk-node": { - "version": "file:../../dist" - }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "requires": { - "@cspotcode/source-map-consumer": "0.8.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "@types/prop-types": { - "version": "15.7.4", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", - "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", - "dev": true - }, - "@types/react": { - "version": "17.0.38", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", - "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", - "dev": true, - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", - "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true - }, - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "csstype": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", - "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" - }, - "log-that-http": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/log-that-http/-/log-that-http-1.0.1.tgz", - "integrity": "sha512-m382CmTTsDdf288oUdQdYMBDUisiQgXkqIIPw8stFqjSZwjgrg4ap7S3viniy7qv3KTnv3CCb/GFQWAuqLJu3Q==" - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "ts-node": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", - "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "yn": "3.1.1" - } - }, - "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true + "../../dist": {}, + "node_modules/@clerk/clerk-sdk-node": { + "resolved": "../../dist", + "link": true + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, + "node_modules/@types/react": { + "version": "17.0.38", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", + "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", + "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "engines": { + "node": ">=10" + } + }, + "node_modules/log-that-http": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/log-that-http/-/log-that-http-1.0.1.tgz", + "integrity": "sha512-m382CmTTsDdf288oUdQdYMBDUisiQgXkqIIPw8stFqjSZwjgrg4ap7S3viniy7qv3KTnv3CCb/GFQWAuqLJu3Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true } + } + }, + "node_modules/typescript": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@clerk/clerk-sdk-node": { + "version": "file:../../dist" + }, + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, + "@types/react": { + "version": "17.0.38", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", + "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", + "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" + }, + "log-that-http": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/log-that-http/-/log-that-http-1.0.1.tgz", + "integrity": "sha512-m382CmTTsDdf288oUdQdYMBDUisiQgXkqIIPw8stFqjSZwjgrg4ap7S3viniy7qv3KTnv3CCb/GFQWAuqLJu3Q==" + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "ts-node": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + } + }, + "typescript": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } + } } diff --git a/packages/sdk-node/package.json b/packages/sdk-node/package.json index d461bf205cb..bd35818f6b5 100644 --- a/packages/sdk-node/package.json +++ b/packages/sdk-node/package.json @@ -12,7 +12,7 @@ "package.json" ], "engines": { - "node": ">=12" + "node": ">=14" }, "scripts": { "build:es5": "node ./scripts/info.js && tsc -p tsconfig.build.json", @@ -53,9 +53,9 @@ "camelcase-keys": "^6.2.2", "cookies": "^0.8.0", "deepmerge": "^4.2.2", - "got": "^11.8.2", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^2.0.4", + "node-fetch": "^3.2.4", "snakecase-keys": "^3.2.1", "tslib": "^2.3.1" }, diff --git a/packages/sdk-node/src/Clerk.ts b/packages/sdk-node/src/Clerk.ts index d47dace83c4..3f49bef6e21 100644 --- a/packages/sdk-node/src/Clerk.ts +++ b/packages/sdk-node/src/Clerk.ts @@ -1,27 +1,29 @@ import { + APIRequestOptions, AuthStatus, Base, + ClerkAPIResponseError, ClerkBackendAPI, - ClerkFetcher, createGetToken, createSignedOutState, + Logger, } from '@clerk/backend-core'; import { ClerkJWTClaims, ServerGetToken } from '@clerk/types'; -import { Crypto, CryptoKey } from '@peculiar/webcrypto'; import Cookies from 'cookies'; -import deepmerge from 'deepmerge'; +import deepMerge from 'deepmerge'; import type { NextFunction, Request, Response } from 'express'; -import got, { OptionsOfUnknownResponseBody } from 'got'; import jwt from 'jsonwebtoken'; import jwks, { JwksClient } from 'jwks-rsa'; -import querystring from 'querystring'; +import fetch from 'node-fetch'; import { SupportMessages } from './constants/SupportMessages'; import { LIB_NAME, LIB_VERSION } from './info'; -import { decodeBase64, toSPKIDer } from './utils/crypto'; -import { ClerkServerError } from './utils/Errors'; -// utils -import Logger from './utils/Logger'; +import { + decodeBase64, + importJSONWebKey, + importPKIKey, + verifySignature, +} from './utils'; const defaultApiKey = process.env.CLERK_API_KEY || ''; const defaultJWTKey = process.env.CLERK_JWT_KEY; @@ -29,13 +31,6 @@ const defaultApiVersion = process.env.CLERK_API_VERSION || 'v1'; const defaultServerApiUrl = process.env.CLERK_API_URL || 'https://api.clerk.dev'; const JWKS_MAX_AGE = 3600000; // 1 hour -const packageRepo = 'https://github.com/clerkinc/clerk-sdk-node'; - -export type MiddlewareOptions = { - onError?: Function; - authorizedParties?: string[]; - jwtKey?: string; -}; export type WithAuthProp = T & { auth: { @@ -55,25 +50,25 @@ export type RequireAuthProp = T & { }; }; -const crypto = new Crypto(); +export type Middleware = ( + req: WithAuthProp, + res: Response, + next: NextFunction +) => Promise; -const importKey = async (jwk: JsonWebKey, algorithm: Algorithm) => { - return await crypto.subtle.importKey('jwk', jwk, algorithm, true, ['verify']); +export type MiddlewareOptions = { + onError?: (error: ClerkAPIResponseError) => unknown; + authorizedParties?: string[]; + jwtKey?: string; }; -const verifySignature = async ( - algorithm: Algorithm, - key: CryptoKey, - signature: Uint8Array, - data: Uint8Array -) => { - return await crypto.subtle.verify(algorithm, key, signature, data); -}; +// eslint-disable-next-line @typescript-eslint/no-empty-function +const noop: NextFunction = () => {}; export default class Clerk extends ClerkBackendAPI { base: Base; jwtKey?: string; - httpOptions: OptionsOfUnknownResponseBody; + httpOptions: RequestInit; _jwksClient: JwksClient; @@ -83,46 +78,71 @@ export default class Clerk extends ClerkBackendAPI { constructor({ apiKey = defaultApiKey, jwtKey = defaultJWTKey, - serverApiUrl = defaultServerApiUrl, + apiUrl = defaultServerApiUrl, apiVersion = defaultApiVersion, httpOptions = {}, jwksCacheMaxAge = JWKS_MAX_AGE, }: { apiKey?: string; jwtKey?: string; - serverApiUrl?: string; + apiUrl?: string; apiVersion?: string; - httpOptions?: OptionsOfUnknownResponseBody; + httpOptions?: RequestInit; jwksCacheMaxAge?: number; } = {}) { - const fetcher: ClerkFetcher = ( - url, - { method, authorization, contentType, userAgent, body } - ) => { - const finalHTTPOptions = deepmerge(this.httpOptions, { - method, - responseType: contentType === 'text/html' ? 'text' : 'json', - headers: { - authorization, - 'Content-Type': contentType, - 'User-Agent': userAgent, - 'X-Clerk-SDK': `node/${LIB_VERSION}`, - }, - // @ts-ignore - ...(body && { body: querystring.stringify(body) }), - }) as OptionsOfUnknownResponseBody; - - return got(url, finalHTTPOptions).then((data) => data.body); - }; - super({ apiKey, apiVersion, - serverApiUrl, + apiUrl, libName: LIB_NAME, libVersion: LIB_VERSION, - packageRepo, - fetcher, + apiClient: { + async request(options: APIRequestOptions) { + const { url, method, queryParams, headerParams, bodyParams } = + options; + // Build final URL with search parameters + const finalUrl = new URL(url || ''); + + if (queryParams) { + for (const [key, val] of Object.entries( + queryParams as Record + )) { + // Support array values for queryParams such as { foo: [42, 43] } + if (val) { + [val] + .flat() + .forEach((v) => finalUrl.searchParams.append(key, v)); + } + } + } + + const response = await fetch(finalUrl.href, { + method, + headers: headerParams as Record, + ...(bodyParams && + Object.keys(bodyParams).length > 0 && { + body: JSON.stringify(bodyParams), + }), + }); + + // Parse JSON or Text response. + const isJSONResponse = + headerParams && headerParams['Content-Type'] === 'application/json'; + const data = (await (isJSONResponse + ? response.json() + : response.text())) as T; + + // Check for errors + if (!response.ok) { + throw new ClerkAPIResponseError(response.statusText, { + data: (data as any)?.errors || data, + status: response.status, + }); + } + + return data; + }, + }, }); if (!apiKey) { @@ -133,7 +153,7 @@ export default class Clerk extends ClerkBackendAPI { this.jwtKey = jwtKey; this._jwksClient = jwks({ - jwksUri: `${serverApiUrl}/${apiVersion}/jwks`, + jwksUri: `${apiUrl}/${apiVersion}/jwks`, requestHeaders: { Authorization: `Bearer ${apiKey}`, }, @@ -151,25 +171,15 @@ export default class Clerk extends ClerkBackendAPI { const signingKey = await this._jwksClient.getSigningKey( decoded.header.kid ); - const pubKey = signingKey.getPublicKey(); - - return await crypto.subtle.importKey( - 'spki', - toSPKIDer(pubKey), - { - name: 'RSASSA-PKCS1-v1_5', - hash: 'SHA-256', - }, - true, - ['verify'] - ); + const publicKey = signingKey.getPublicKey(); + + return importPKIKey(publicKey); }; /** Base initialization */ - // TODO: More comprehensive base initialization this.base = new Base( - importKey, + importJSONWebKey, verifySignature, decodeBase64, loadCryptoKey @@ -219,34 +229,36 @@ export default class Clerk extends ClerkBackendAPI { // Middlewares // defaultOnError swallows the error - defaultOnError(error: Error & { data: any }) { + protected defaultOnError = (error: ClerkAPIResponseError) => { Logger.warn(error.message); - (error.data || []).forEach((serverError: ClerkServerError) => { - Logger.warn(serverError.longMessage); - }); - } + (error.errors || []).forEach(({ message, longMessage }) => + Logger.warn(longMessage || message) + ); + }; // strictOnError returns the error so that Express will halt the request chain - strictOnError(error: Error & { data: any }) { + protected strictOnError = (error: ClerkAPIResponseError) => { Logger.error(error.message); - (error.data || []).forEach((serverError: ClerkServerError) => { - Logger.error(serverError.longMessage); - }); + (error.errors || []).forEach(({ message, longMessage }) => + Logger.error(longMessage || message) + ); return error; - } + }; - expressWithAuth( - { onError, authorizedParties, jwtKey }: MiddlewareOptions = { - onError: this.defaultOnError, - } - ): (req: Request, res: Response, next: NextFunction) => Promise { + // Middlewares + + expressWithAuth(options?: MiddlewareOptions): Middleware { function signedOut() { throw new Error('Unauthenticated'); } + options = deepMerge(options || {}, { onError: this.defaultOnError }); + + const { onError, authorizedParties, jwtKey } = options; + async function authenticate( this: Clerk, req: Request, @@ -315,7 +327,7 @@ export default class Clerk extends ClerkBackendAPI { return next(); } - const err = await onError(error); + const err = await onError(error as any); if (err) { next(err); @@ -328,12 +340,10 @@ export default class Clerk extends ClerkBackendAPI { return authenticate.bind(this); } - expressRequireAuth( - options: MiddlewareOptions = { - onError: this.strictOnError, - } - ) { - return this.expressWithAuth(options); + expressRequireAuth(options?: MiddlewareOptions) { + return this.expressWithAuth( + deepMerge(options || {}, { onError: this.strictOnError }) + ); } // Credits to https://nextjs.org/docs/api-routes/api-middlewares @@ -354,12 +364,7 @@ export default class Clerk extends ClerkBackendAPI { } // Set the session on the request and then call provided handler - withAuth( - handler: Function, - options: MiddlewareOptions = { - onError: this.defaultOnError, - } - ) { + withAuth(handler: Middleware, options?: MiddlewareOptions) { return async ( req: WithAuthProp, res: Response, @@ -367,7 +372,7 @@ export default class Clerk extends ClerkBackendAPI { ) => { try { await this._runMiddleware(req, res, this.expressWithAuth(options)); - return handler(req, res, next); + return handler(req, res, next || noop); } catch (error) { // @ts-ignore const errorData = error.data || { error: error.message }; @@ -384,12 +389,10 @@ export default class Clerk extends ClerkBackendAPI { } // Stricter version, short-circuits if session can't be determined - requireAuth( - handler: Function, - options: MiddlewareOptions = { - onError: this.strictOnError, - } - ) { - return this.withAuth(handler, options); + requireAuth(handler: Middleware, options?: MiddlewareOptions) { + return this.withAuth( + handler, + deepMerge(options || {}, { onError: this.strictOnError }) + ); } } diff --git a/packages/sdk-node/src/__tests__/middleware.test.ts b/packages/sdk-node/src/__tests__/middleware.test.ts index d253503109e..230f7b4060a 100644 --- a/packages/sdk-node/src/__tests__/middleware.test.ts +++ b/packages/sdk-node/src/__tests__/middleware.test.ts @@ -1,10 +1,10 @@ +import { AuthStatus } from '@clerk/backend-core'; import type { NextFunction, Request, Response } from 'express'; import jwt from 'jsonwebtoken'; import Clerk from '../Clerk'; -import { AuthStatus } from '@clerk/backend-core'; -const mockGetToken = () => {}; +const mockGetToken = jest.fn(); jest.mock('@clerk/backend-core', () => { return { diff --git a/packages/sdk-node/src/utils/Crypto.ts b/packages/sdk-node/src/utils/Crypto.ts new file mode 100644 index 00000000000..c97406bf6cc --- /dev/null +++ b/packages/sdk-node/src/utils/Crypto.ts @@ -0,0 +1,43 @@ +import { Crypto, CryptoKey } from '@peculiar/webcrypto'; + +import { decodeBase64, str2ab } from './Encoding'; + +export const crypto = new Crypto(); + +// toSPKIDer converts a PEM encoded Public Key to DER encoded +export function toSPKIDer(pem: string): ArrayBuffer { + const pemHeader = '-----BEGIN PUBLIC KEY-----'; + const pemFooter = '-----END PUBLIC KEY-----'; + const pemContents = pem.substring( + pemHeader.length, + pem.length - pemFooter.length + ); + const binaryDerString = decodeBase64(pemContents); + return str2ab(binaryDerString); +} + +export async function importPKIKey(publicKey: string) { + return crypto.subtle.importKey( + 'spki', + toSPKIDer(publicKey), + { + name: 'RSASSA-PKCS1-v1_5', + hash: 'SHA-256', + }, + true, + ['verify'] + ); +} + +export async function importJSONWebKey(jwk: JsonWebKey, algorithm: Algorithm) { + return await crypto.subtle.importKey('jwk', jwk, algorithm, true, ['verify']); +} + +export async function verifySignature( + algorithm: Algorithm, + key: CryptoKey, + signature: Uint8Array, + data: Uint8Array +) { + return crypto.subtle.verify(algorithm, key, signature, data); +} diff --git a/packages/sdk-node/src/utils/Encoding.ts b/packages/sdk-node/src/utils/Encoding.ts new file mode 100644 index 00000000000..edb39b9c730 --- /dev/null +++ b/packages/sdk-node/src/utils/Encoding.ts @@ -0,0 +1,13 @@ +export function decodeBase64(base64: string) { + return Buffer.from(base64, 'base64').toString('binary'); +} + +// https://stackoverflow.com/a/11058858 +export function str2ab(input: string): ArrayBuffer { + const buf = new ArrayBuffer(input.length); + const bufView = new Uint8Array(buf); + for (let i = 0, strLen = input.length; i < strLen; i++) { + bufView[i] = input.charCodeAt(i); + } + return buf; +} diff --git a/packages/sdk-node/src/utils/ErrorHandler.ts b/packages/sdk-node/src/utils/ErrorHandler.ts deleted file mode 100644 index b062e93c629..00000000000 --- a/packages/sdk-node/src/utils/ErrorHandler.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ClerkServerError, ClerkServerErrorJSON, HttpError } from './Errors'; - -export default function handleError(error: any): never { - const statusCode = error?.response?.statusCode || 500; - const message = error.message || ''; - const body = error?.response?.body; - let data; - - if (body && body.errors) { - data = (body.errors || []).map((errorJSON: ClerkServerErrorJSON) => { - return ClerkServerError.fromJSON(errorJSON); - }); - } else { - data = body; - } - - throw new HttpError(statusCode, message, data); -} diff --git a/packages/sdk-node/src/utils/Errors.ts b/packages/sdk-node/src/utils/Errors.ts deleted file mode 100644 index 21afe6d296d..00000000000 --- a/packages/sdk-node/src/utils/Errors.ts +++ /dev/null @@ -1,42 +0,0 @@ -export class HttpError extends Error { - public statusCode: number; - public data: unknown; - - constructor(statusCode: number, message: string, data: unknown) { - super(message); - this.statusCode = statusCode; - this.data = data; - } -} - -export interface ClerkServerErrorProps { - message: string; - longMessage: string; - code: string; -} - -export interface ClerkServerErrorJSON { - message: string; - long_message: string; - code: string; -} - -export class ClerkServerError { - public message: string; - public longMessage: string; - public code: string; - - constructor(data: ClerkServerErrorProps) { - this.message = data.message; - this.longMessage = data.longMessage; - this.code = data.code; - } - - static fromJSON(data: ClerkServerErrorJSON) { - return new ClerkServerError({ - message: data.message, - longMessage: data.long_message, - code: data.code, - }); - } -} diff --git a/packages/sdk-node/src/utils/Logger.ts b/packages/sdk-node/src/utils/Logger.ts deleted file mode 100644 index f8cb1d52cd3..00000000000 --- a/packages/sdk-node/src/utils/Logger.ts +++ /dev/null @@ -1,46 +0,0 @@ -// TODO use EventEmitter for an async Logger instead -// TODO support more than just console output - -enum LogLevel { - Info = 'INFO', - Debug = 'DEBUG', - Warn = 'WARN', - Error = 'ERROR', -} - -type LogMessage = { - level: LogLevel; - timestamp: string; - message: string; -}; - -export default class Logger { - public static info(msg: string): void { - Logger.log(LogLevel.Info, msg); - } - - public static debug(msg: string): void { - Logger.log(LogLevel.Debug, msg); - } - - public static warn(msg: string): void { - Logger.log(LogLevel.Warn, msg); - } - - public static error(msg: string): void { - Logger.log(LogLevel.Error, msg); - } - - private static log(logLevel: LogLevel, msg: string): void { - if (process.env.CLERK_LOGGING == 'true') { - const logMessage: LogMessage = { - timestamp: new Date().toISOString(), - level: logLevel, - message: msg, - }; - - // @ts-ignore - console[logLevel.toLowerCase()](logMessage); - } - } -} diff --git a/packages/sdk-node/src/utils/crypto.ts b/packages/sdk-node/src/utils/crypto.ts deleted file mode 100644 index 5c3e11083d9..00000000000 --- a/packages/sdk-node/src/utils/crypto.ts +++ /dev/null @@ -1,24 +0,0 @@ -export const decodeBase64 = (base64: string) => - Buffer.from(base64, 'base64').toString('binary'); - -// toSPKIDer converts a PEM encoded Public Key to DER encoded -export function toSPKIDer(pem: string): ArrayBuffer { - const pemHeader = '-----BEGIN PUBLIC KEY-----'; - const pemFooter = '-----END PUBLIC KEY-----'; - const pemContents = pem.substring( - pemHeader.length, - pem.length - pemFooter.length - ); - const binaryDerString = decodeBase64(pemContents); - return str2ab(binaryDerString); -} - -// https://stackoverflow.com/a/11058858 -function str2ab(input: string): ArrayBuffer { - const buf = new ArrayBuffer(input.length); - const bufView = new Uint8Array(buf); - for (let i = 0, strLen = input.length; i < strLen; i++) { - bufView[i] = input.charCodeAt(i); - } - return buf; -} diff --git a/packages/sdk-node/src/utils/index.ts b/packages/sdk-node/src/utils/index.ts new file mode 100644 index 00000000000..d8ec93a02db --- /dev/null +++ b/packages/sdk-node/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from './Crypto'; +export * from './Encoding';