dce-expresskit 4.0.0-beta.10

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.
Files changed (95) hide show
  1. package/.eslintrc.js +93 -0
  2. package/LICENSE +21 -0
  3. package/README.md +17 -0
  4. package/genEncodedSecret.ts +107 -0
  5. package/genSalt.ts +15 -0
  6. package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +6 -0
  7. package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.js +13 -0
  8. package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.js.map +1 -0
  9. package/lib/constants/LOG_REVIEW_STATUS_ROUTE.d.ts +7 -0
  10. package/lib/constants/LOG_REVIEW_STATUS_ROUTE.js +14 -0
  11. package/lib/constants/LOG_REVIEW_STATUS_ROUTE.js.map +1 -0
  12. package/lib/constants/LOG_ROUTE_PATH.d.ts +6 -0
  13. package/lib/constants/LOG_ROUTE_PATH.js +13 -0
  14. package/lib/constants/LOG_ROUTE_PATH.js.map +1 -0
  15. package/lib/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
  16. package/lib/constants/ROUTE_PATH_PREFIX.js +9 -0
  17. package/lib/constants/ROUTE_PATH_PREFIX.js.map +1 -0
  18. package/lib/errors/ErrorWithCode.d.ts +9 -0
  19. package/lib/errors/ErrorWithCode.js +33 -0
  20. package/lib/errors/ErrorWithCode.js.map +1 -0
  21. package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.d.ts +9 -0
  22. package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.js +17 -0
  23. package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.js.map +1 -0
  24. package/lib/helpers/addDBEditorEndpoints/index.d.ts +41 -0
  25. package/lib/helpers/addDBEditorEndpoints/index.js +134 -0
  26. package/lib/helpers/addDBEditorEndpoints/index.js.map +1 -0
  27. package/lib/helpers/dataSigner.d.ts +40 -0
  28. package/lib/helpers/dataSigner.js +236 -0
  29. package/lib/helpers/dataSigner.js.map +1 -0
  30. package/lib/helpers/genRouteHandler.d.ts +75 -0
  31. package/lib/helpers/genRouteHandler.js +661 -0
  32. package/lib/helpers/genRouteHandler.js.map +1 -0
  33. package/lib/helpers/handleError.d.ts +18 -0
  34. package/lib/helpers/handleError.js +51 -0
  35. package/lib/helpers/handleError.js.map +1 -0
  36. package/lib/helpers/handleSuccess.d.ts +8 -0
  37. package/lib/helpers/handleSuccess.js +20 -0
  38. package/lib/helpers/handleSuccess.js.map +1 -0
  39. package/lib/helpers/initCrossServerCredentialCollection.d.ts +11 -0
  40. package/lib/helpers/initCrossServerCredentialCollection.js +15 -0
  41. package/lib/helpers/initCrossServerCredentialCollection.js.map +1 -0
  42. package/lib/helpers/initLogCollection.d.ts +11 -0
  43. package/lib/helpers/initLogCollection.js +26 -0
  44. package/lib/helpers/initLogCollection.js.map +1 -0
  45. package/lib/helpers/initServer.d.ts +45 -0
  46. package/lib/helpers/initServer.js +293 -0
  47. package/lib/helpers/initServer.js.map +1 -0
  48. package/lib/helpers/parseUserAgent.d.ts +17 -0
  49. package/lib/helpers/parseUserAgent.js +108 -0
  50. package/lib/helpers/parseUserAgent.js.map +1 -0
  51. package/lib/helpers/visitEndpointOnAnotherServer/index.d.ts +18 -0
  52. package/lib/helpers/visitEndpointOnAnotherServer/index.js +156 -0
  53. package/lib/helpers/visitEndpointOnAnotherServer/index.js.map +1 -0
  54. package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.d.ts +23 -0
  55. package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.js +168 -0
  56. package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.js.map +1 -0
  57. package/lib/html/genErrorPage.d.ts +19 -0
  58. package/lib/html/genErrorPage.js +27 -0
  59. package/lib/html/genErrorPage.js.map +1 -0
  60. package/lib/html/genInfoPage.d.ts +13 -0
  61. package/lib/html/genInfoPage.js +16 -0
  62. package/lib/html/genInfoPage.js.map +1 -0
  63. package/lib/index.d.ts +11 -0
  64. package/lib/index.js +68 -0
  65. package/lib/index.js.map +1 -0
  66. package/lib/types/CrossServerCredential.d.ts +11 -0
  67. package/lib/types/CrossServerCredential.js +3 -0
  68. package/lib/types/CrossServerCredential.js.map +1 -0
  69. package/lib/types/ExpressKitErrorCode.d.ts +31 -0
  70. package/lib/types/ExpressKitErrorCode.js +38 -0
  71. package/lib/types/ExpressKitErrorCode.js.map +1 -0
  72. package/package.json +53 -0
  73. package/src/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.ts +9 -0
  74. package/src/constants/LOG_REVIEW_STATUS_ROUTE.ts +10 -0
  75. package/src/constants/LOG_ROUTE_PATH.ts +9 -0
  76. package/src/constants/ROUTE_PATH_PREFIX.ts +7 -0
  77. package/src/errors/ErrorWithCode.tsx +15 -0
  78. package/src/helpers/addDBEditorEndpoints/generateEndpointPath.ts +16 -0
  79. package/src/helpers/addDBEditorEndpoints/index.ts +130 -0
  80. package/src/helpers/dataSigner.ts +306 -0
  81. package/src/helpers/genRouteHandler.ts +914 -0
  82. package/src/helpers/handleError.ts +66 -0
  83. package/src/helpers/handleSuccess.ts +18 -0
  84. package/src/helpers/initCrossServerCredentialCollection.ts +19 -0
  85. package/src/helpers/initLogCollection.ts +31 -0
  86. package/src/helpers/initServer.ts +284 -0
  87. package/src/helpers/parseUserAgent.ts +108 -0
  88. package/src/helpers/visitEndpointOnAnotherServer/index.ts +157 -0
  89. package/src/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.ts +164 -0
  90. package/src/html/genErrorPage.ts +144 -0
  91. package/src/html/genInfoPage.ts +101 -0
  92. package/src/index.ts +125 -0
  93. package/src/types/CrossServerCredential.ts +16 -0
  94. package/src/types/ExpressKitErrorCode.ts +37 -0
  95. package/tsconfig.json +19 -0
@@ -0,0 +1,306 @@
1
+ // Import dce-reactkit
2
+ import {
3
+ ErrorWithCode,
4
+ MINUTE_IN_MS,
5
+ } from 'dce-reactkit';
6
+
7
+ // Import oauth
8
+ import oauth from 'oauth-signature';
9
+
10
+ // Import crypto
11
+ import crypto from 'crypto';
12
+
13
+ // Import shared helpers
14
+ import { internalGetCrossServerCredentialCollection } from './initServer';
15
+
16
+ // Import shared types
17
+ import ExpressKitErrorCode from '../types/ExpressKitErrorCode';
18
+ import CrossServerCredential from '../types/CrossServerCredential';
19
+
20
+ /*------------------------------------------------------------------------*/
21
+ /* ------------------------------- Helpers ------------------------------ */
22
+ /*------------------------------------------------------------------------*/
23
+
24
+ /**
25
+ * Generate an oauth signature
26
+ * @author Gabe Abrams
27
+ * @param opts object containing all arguments
28
+ * @param opts.method the http method
29
+ * @param opts.path the http request path
30
+ * @param opts.params the data in the body to sign
31
+ * @param opts.secret the secret to sign with
32
+ * @return the signature
33
+ */
34
+ const genSignature = async (
35
+ opts: {
36
+ method?: string,
37
+ path?: string,
38
+ params?: { [key: string]: any },
39
+ secret: string,
40
+ },
41
+ ): Promise<string> => {
42
+ // Destructure opts
43
+ const {
44
+ method,
45
+ path,
46
+ params,
47
+ secret,
48
+ } = opts;
49
+
50
+ // Order the params alphabetically by key
51
+ const keys = Object.keys(params ?? {});
52
+ keys.sort();
53
+ const orderedParams: {
54
+ [key: string]: any,
55
+ } = {};
56
+ keys.forEach((key) => {
57
+ // Skip oauth_signature
58
+ if (key === 'oauth_signature') {
59
+ return;
60
+ }
61
+
62
+ // Add the param
63
+ orderedParams[key] = (params ?? {})[key];
64
+ });
65
+
66
+ // Generate the signature
67
+ return decodeURIComponent(oauth.generate(
68
+ method ?? 'GET',
69
+ path ?? 'no-path',
70
+ orderedParams,
71
+ secret,
72
+ ));
73
+ };
74
+
75
+ /**
76
+ * Decrypt an encrypted string using a secret
77
+ * @author Gabe Abrams
78
+ * @param str the encrypted string
79
+ * @return the decrypted string
80
+ */
81
+ const decrypt = async (
82
+ encryptedPack: string,
83
+ ): Promise<string> => {
84
+ // Decryption process based on:
85
+ // https://medium.com/@tony.infisical/guide-to-nodes-crypto-module-for-encryption-decryption-65c077176980
86
+
87
+ // Get the encryption secret
88
+ const { DCEKIT_CRED_ENCODING_SALT } = process.env;
89
+ if (!DCEKIT_CRED_ENCODING_SALT) {
90
+ throw new ErrorWithCode(
91
+ 'Could not decrypt a string because the encryption salt was not set.',
92
+ ExpressKitErrorCode.CrossServerNoCredentialEncodingSalt,
93
+ );
94
+ }
95
+
96
+ // Separate encrypted pack
97
+ const {
98
+ ciphertext,
99
+ iv,
100
+ tag,
101
+ } = JSON.parse(decodeURIComponent(encryptedPack));
102
+
103
+ // Parse the encrypted data
104
+ const decipher = crypto.createDecipheriv(
105
+ 'aes-256-gcm',
106
+ Buffer.from(DCEKIT_CRED_ENCODING_SALT, 'base64'),
107
+ Buffer.from(iv, 'base64'),
108
+ );
109
+
110
+ // Set the authentication tag
111
+ decipher.setAuthTag(Buffer.from(tag, 'base64'));
112
+
113
+ // Decrypt the string
114
+ let str = decipher.update(ciphertext, 'base64', 'utf8');
115
+ str += decipher.final('utf8');
116
+
117
+ // Return the decrypted string
118
+ return str;
119
+ };
120
+
121
+ /*------------------------------------------------------------------------*/
122
+ /* ------------------------------- Signing ------------------------------ */
123
+ /*------------------------------------------------------------------------*/
124
+
125
+ /**
126
+ * Sign a request and get the new request params
127
+ * @author Gabe Abrams
128
+ * @param opts object containing all arguments
129
+ * @param opts.method the method to sign
130
+ * @param opts.path the http request path
131
+ * @param opts.params the data in the body to sign
132
+ * @param opts.key the dcekit key to sign with
133
+ * @param opts.secret the dcekit secret to sign with
134
+ * @return augmented params for the request, including a signature, timestamp, and key
135
+ */
136
+ export const signRequest = async (
137
+ opts: {
138
+ method: string,
139
+ path: string,
140
+ params: { [key: string]: any },
141
+ key: string,
142
+ secret: string,
143
+ },
144
+ ): Promise<{ [key: string]: any }> => {
145
+ // Destructure opts
146
+ const method = opts.method.toUpperCase();
147
+ const {
148
+ path,
149
+ params,
150
+ key,
151
+ secret,
152
+ } = opts;
153
+
154
+ // Augment the params
155
+ const augmentedParams: {
156
+ [key: string]: any,
157
+ } = {
158
+ ...params,
159
+ oauth_consumer_key: key,
160
+ oauth_nonce: Math.random().toString(36),
161
+ oauth_timestamp: Date.now(),
162
+ };
163
+
164
+ // Generate a signature
165
+ const signature = await genSignature({
166
+ method,
167
+ path,
168
+ params,
169
+ secret,
170
+ });
171
+
172
+ // Add signature to the augmented params
173
+ augmentedParams.oauth_signature = signature;
174
+
175
+ // Return the augmented params
176
+ return augmentedParams;
177
+ };
178
+
179
+ /**
180
+ * Validate a signed request. Throws an error if invalid
181
+ * @author Gabe Abrams
182
+ * @param opts object containing all arguments
183
+ * @param opts.method the method of the data validate
184
+ * @param opts.path the http request path to validate
185
+ * @param opts.scope the name of the scope to validate
186
+ * @param opts.params the request data to validate
187
+ * @returns parsed and validated params
188
+ */
189
+ export const validateSignedRequest = async (
190
+ opts: {
191
+ method: string,
192
+ path: string,
193
+ scope: string,
194
+ params: { [key: string]: any },
195
+ },
196
+ ) => {
197
+ /* ---------- Collect Info ---------- */
198
+
199
+ // Get the signature
200
+ if (!opts.params.oauth_signature) {
201
+ throw new ErrorWithCode(
202
+ 'Could not validate a cross-server request there was no oauth signature.',
203
+ ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
204
+ );
205
+ }
206
+ const signature = opts.params.oauth_signature;
207
+
208
+ // Get the timestamp
209
+ if (
210
+ // No timestamp
211
+ !opts.params.oauth_timestamp
212
+ // Invalid timestamp
213
+ || Number.isNaN(Number.parseInt(opts.params.oauth_timestamp, 10))
214
+ ) {
215
+ throw new ErrorWithCode(
216
+ 'Could not validate a cross-server request there was no valid oauth timestamp.',
217
+ ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
218
+ );
219
+ }
220
+ const timestamp = Number.parseInt(opts.params.oauth_timestamp, 10);
221
+
222
+ // Get the key
223
+ if (!opts.params.oauth_consumer_key) {
224
+ throw new ErrorWithCode(
225
+ 'Could not validate a cross-server request there was no oauth consumer key.',
226
+ ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
227
+ );
228
+ }
229
+ const key = opts.params.oauth_consumer_key;
230
+
231
+ // Get the rest of the info
232
+ const {
233
+ method,
234
+ path,
235
+ params,
236
+ scope,
237
+ } = opts;
238
+
239
+ /* ------- Look Up Credential ------- */
240
+
241
+ // Get the cross-server credential collection
242
+ const crossServerCredentialCollection = internalGetCrossServerCredentialCollection();
243
+ if (!crossServerCredentialCollection) {
244
+ throw new ErrorWithCode(
245
+ 'Could not validate a cross-server request because the cross-server credential collection was not ready in time.',
246
+ ExpressKitErrorCode.SignedRequestInvalidCollection,
247
+ );
248
+ }
249
+
250
+ // Get the cross-server credential
251
+ const crossServerCredentialMatches: CrossServerCredential[] = await crossServerCredentialCollection.find({ key });
252
+ if (!crossServerCredentialMatches || crossServerCredentialMatches.length === 0) {
253
+ throw new ErrorWithCode(
254
+ 'Could not validate a cross-server request because the credential was not found.',
255
+ ExpressKitErrorCode.SignedRequestInvalidCredential,
256
+ );
257
+ }
258
+ const crossServerCredential = crossServerCredentialMatches[0];
259
+
260
+ // Make sure the scope is included
261
+ const allowedScopes = crossServerCredential.scopes;
262
+ console.log('Scope:', scope, 'Allowed Scopes:', allowedScopes);
263
+ if (!allowedScopes || !Array.isArray(allowedScopes)) {
264
+ throw new ErrorWithCode(
265
+ 'Could not validate a cross-server request because the credential does not have access to any scopes.',
266
+ ExpressKitErrorCode.SignedRequestInvalidScope,
267
+ );
268
+
269
+ }
270
+ if (!allowedScopes.includes(scope)) {
271
+ throw new ErrorWithCode(
272
+ 'Could not validate a cross-server request because the required scope was not approved for the credential.',
273
+ ExpressKitErrorCode.SignedRequestInvalidScope,
274
+ );
275
+ }
276
+
277
+ // Decode the secret
278
+ const secret = await decrypt(crossServerCredential.encodedeSecret);
279
+
280
+ /* -------- Verify Signature -------- */
281
+
282
+ // Generate a new signature to compare
283
+ const expectedSignature = await genSignature({
284
+ method,
285
+ path,
286
+ params,
287
+ secret,
288
+ });
289
+
290
+ // Make sure the signatures match
291
+ if (signature !== expectedSignature) {
292
+ throw new ErrorWithCode(
293
+ 'Could not validate a cross-server request because the signature did not match.',
294
+ ExpressKitErrorCode.SignedRequestInvalidSignature,
295
+ );
296
+ }
297
+
298
+ // Make sure the timestamp was recent enough
299
+ const elapsedMs = Math.abs(Date.now() - timestamp);
300
+ if (elapsedMs < MINUTE_IN_MS) {
301
+ throw new ErrorWithCode(
302
+ 'Could not validate a cross-server request because the request was too old.',
303
+ ExpressKitErrorCode.SignedRequestInvalidTimestamp,
304
+ );
305
+ }
306
+ };