dce-expresskit 4.0.0-beta-logreviewer.1
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/.eslintrc.js +93 -0
- package/LICENSE +21 -0
- package/README.md +17 -0
- package/genEncodedSecret.ts +107 -0
- package/genSalt.ts +15 -0
- package/lib/constants/LOG_REVIEW_PAGE_SIZE.d.ts +6 -0
- package/lib/constants/LOG_REVIEW_PAGE_SIZE.js +9 -0
- package/lib/constants/LOG_REVIEW_PAGE_SIZE.js.map +1 -0
- package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +6 -0
- package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.js +13 -0
- package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.js.map +1 -0
- package/lib/constants/LOG_REVIEW_STATUS_ROUTE.d.ts +7 -0
- package/lib/constants/LOG_REVIEW_STATUS_ROUTE.js +14 -0
- package/lib/constants/LOG_REVIEW_STATUS_ROUTE.js.map +1 -0
- package/lib/constants/LOG_ROUTE_PATH.d.ts +6 -0
- package/lib/constants/LOG_ROUTE_PATH.js +13 -0
- package/lib/constants/LOG_ROUTE_PATH.js.map +1 -0
- package/lib/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
- package/lib/constants/ROUTE_PATH_PREFIX.js +9 -0
- package/lib/constants/ROUTE_PATH_PREFIX.js.map +1 -0
- package/lib/errors/ErrorWithCode.d.ts +9 -0
- package/lib/errors/ErrorWithCode.js +33 -0
- package/lib/errors/ErrorWithCode.js.map +1 -0
- package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.d.ts +9 -0
- package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.js +17 -0
- package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.js.map +1 -0
- package/lib/helpers/addDBEditorEndpoints/index.d.ts +41 -0
- package/lib/helpers/addDBEditorEndpoints/index.js +134 -0
- package/lib/helpers/addDBEditorEndpoints/index.js.map +1 -0
- package/lib/helpers/dataSigner.d.ts +40 -0
- package/lib/helpers/dataSigner.js +242 -0
- package/lib/helpers/dataSigner.js.map +1 -0
- package/lib/helpers/genRouteHandler.d.ts +75 -0
- package/lib/helpers/genRouteHandler.js +662 -0
- package/lib/helpers/genRouteHandler.js.map +1 -0
- package/lib/helpers/getLogReviewerLogs.d.ts +27 -0
- package/lib/helpers/getLogReviewerLogs.js +238 -0
- package/lib/helpers/getLogReviewerLogs.js.map +1 -0
- package/lib/helpers/handleError.d.ts +18 -0
- package/lib/helpers/handleError.js +51 -0
- package/lib/helpers/handleError.js.map +1 -0
- package/lib/helpers/handleSuccess.d.ts +8 -0
- package/lib/helpers/handleSuccess.js +20 -0
- package/lib/helpers/handleSuccess.js.map +1 -0
- package/lib/helpers/initCrossServerCredentialCollection.d.ts +11 -0
- package/lib/helpers/initCrossServerCredentialCollection.js +15 -0
- package/lib/helpers/initCrossServerCredentialCollection.js.map +1 -0
- package/lib/helpers/initLogCollection.d.ts +11 -0
- package/lib/helpers/initLogCollection.js +26 -0
- package/lib/helpers/initLogCollection.js.map +1 -0
- package/lib/helpers/initServer.d.ts +45 -0
- package/lib/helpers/initServer.js +292 -0
- package/lib/helpers/initServer.js.map +1 -0
- package/lib/helpers/parseUserAgent.d.ts +17 -0
- package/lib/helpers/parseUserAgent.js +108 -0
- package/lib/helpers/parseUserAgent.js.map +1 -0
- package/lib/helpers/visitEndpointOnAnotherServer/index.d.ts +18 -0
- package/lib/helpers/visitEndpointOnAnotherServer/index.js +89 -0
- package/lib/helpers/visitEndpointOnAnotherServer/index.js.map +1 -0
- package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.d.ts +23 -0
- package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.js +236 -0
- package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.js.map +1 -0
- package/lib/html/genErrorPage.d.ts +19 -0
- package/lib/html/genErrorPage.js +27 -0
- package/lib/html/genErrorPage.js.map +1 -0
- package/lib/html/genInfoPage.d.ts +13 -0
- package/lib/html/genInfoPage.js +16 -0
- package/lib/html/genInfoPage.js.map +1 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +68 -0
- package/lib/index.js.map +1 -0
- package/lib/types/CrossServerCredential.d.ts +11 -0
- package/lib/types/CrossServerCredential.js +3 -0
- package/lib/types/CrossServerCredential.js.map +1 -0
- package/lib/types/ExpressKitErrorCode.d.ts +31 -0
- package/lib/types/ExpressKitErrorCode.js +38 -0
- package/lib/types/ExpressKitErrorCode.js.map +1 -0
- package/package.json +53 -0
- package/src/constants/LOG_REVIEW_PAGE_SIZE.ts +7 -0
- package/src/errors/ErrorWithCode.tsx +15 -0
- package/src/helpers/addDBEditorEndpoints/generateEndpointPath.ts +16 -0
- package/src/helpers/addDBEditorEndpoints/index.ts +130 -0
- package/src/helpers/dataSigner.ts +319 -0
- package/src/helpers/genRouteHandler.ts +920 -0
- package/src/helpers/getLogReviewerLogs.ts +259 -0
- package/src/helpers/handleError.ts +66 -0
- package/src/helpers/handleSuccess.ts +18 -0
- package/src/helpers/initCrossServerCredentialCollection.ts +19 -0
- package/src/helpers/initLogCollection.ts +30 -0
- package/src/helpers/initServer.ts +283 -0
- package/src/helpers/parseUserAgent.ts +108 -0
- package/src/helpers/visitEndpointOnAnotherServer/index.ts +70 -0
- package/src/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.ts +257 -0
- package/src/html/genErrorPage.ts +144 -0
- package/src/html/genInfoPage.ts +101 -0
- package/src/index.ts +125 -0
- package/src/types/CrossServerCredential.ts +16 -0
- package/src/types/ExpressKitErrorCode.ts +37 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Import express
|
|
2
|
+
import express from 'express';
|
|
3
|
+
|
|
4
|
+
// Import dce-reactkit
|
|
5
|
+
import {
|
|
6
|
+
ParamType,
|
|
7
|
+
} from 'dce-reactkit';
|
|
8
|
+
|
|
9
|
+
// Import shared helpers
|
|
10
|
+
import genRouteHandler from '../genRouteHandler';
|
|
11
|
+
import generateEndpointPath from './generateEndpointPath';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Interface for a collection in the database
|
|
15
|
+
* @author Yuen Ler Chow
|
|
16
|
+
*/
|
|
17
|
+
type DCEMangoCollection = {
|
|
18
|
+
/**
|
|
19
|
+
* Find all items in the collection that match the filter query
|
|
20
|
+
* @param filterQuery query for the filter
|
|
21
|
+
* @returns list of items that match the filter query
|
|
22
|
+
*/
|
|
23
|
+
find: (filterQuery: any) => Promise<any[]>,
|
|
24
|
+
/**
|
|
25
|
+
* Insert an item into the collection
|
|
26
|
+
* @param item the item to insert
|
|
27
|
+
*/
|
|
28
|
+
insert: (item: any) => Promise<void>,
|
|
29
|
+
/**
|
|
30
|
+
* Delete an item in the collection
|
|
31
|
+
* @param id the id of the item to delete
|
|
32
|
+
*/
|
|
33
|
+
delete: (filterQuery: { id: string }) => Promise<void>,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Add all routes for the DBEditor
|
|
38
|
+
* @author Yuen Ler Chow
|
|
39
|
+
* @param opts object containing all arguments
|
|
40
|
+
* @param opts.app express app to add routes too
|
|
41
|
+
* @param opts.collectionName the name of the collection
|
|
42
|
+
* @param opts.adminsOnly true if the endpoint is for admins only
|
|
43
|
+
* @param opts.collection dce-mango db collection
|
|
44
|
+
*/
|
|
45
|
+
const addDBEditorEndpoints = (
|
|
46
|
+
opts: {
|
|
47
|
+
app: express.Application,
|
|
48
|
+
collectionName: string,
|
|
49
|
+
adminsOnly: boolean,
|
|
50
|
+
collection: DCEMangoCollection,
|
|
51
|
+
},
|
|
52
|
+
) => {
|
|
53
|
+
const {
|
|
54
|
+
app,
|
|
55
|
+
collectionName,
|
|
56
|
+
adminsOnly,
|
|
57
|
+
collection,
|
|
58
|
+
} = opts;
|
|
59
|
+
|
|
60
|
+
// Generate the endpoint path
|
|
61
|
+
const endpointPath = generateEndpointPath(collectionName, adminsOnly);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* List all items in the collection
|
|
65
|
+
* @author Yuen Ler Chow
|
|
66
|
+
* @returns {any[]} the list of items in the collection
|
|
67
|
+
*/
|
|
68
|
+
app.get(
|
|
69
|
+
endpointPath,
|
|
70
|
+
genRouteHandler({
|
|
71
|
+
paramTypes: {
|
|
72
|
+
filterQuery: ParamType.JSONOptional,
|
|
73
|
+
},
|
|
74
|
+
handler: async ({ params }) => {
|
|
75
|
+
const filterQuery = params.filterQuery ?? {};
|
|
76
|
+
const categories = await collection.find(filterQuery);
|
|
77
|
+
return categories;
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create a new item in the collection
|
|
84
|
+
* @author Yuen Ler Chow
|
|
85
|
+
* @param {any} item the item to create
|
|
86
|
+
*/
|
|
87
|
+
app.post(
|
|
88
|
+
endpointPath,
|
|
89
|
+
genRouteHandler({
|
|
90
|
+
paramTypes: {
|
|
91
|
+
item: ParamType.JSON,
|
|
92
|
+
},
|
|
93
|
+
handler: async ({
|
|
94
|
+
params,
|
|
95
|
+
}) => {
|
|
96
|
+
// Destructure params
|
|
97
|
+
const {
|
|
98
|
+
item,
|
|
99
|
+
} = params;
|
|
100
|
+
|
|
101
|
+
await collection.insert(item);
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Remove an item from the collection by id
|
|
108
|
+
* @author Yuen Ler Chow
|
|
109
|
+
*/
|
|
110
|
+
app.delete(
|
|
111
|
+
`${endpointPath}/:id`,
|
|
112
|
+
genRouteHandler({
|
|
113
|
+
paramTypes: {
|
|
114
|
+
id: ParamType.String,
|
|
115
|
+
},
|
|
116
|
+
handler: async ({
|
|
117
|
+
params,
|
|
118
|
+
}) => {
|
|
119
|
+
// Destructure params
|
|
120
|
+
const {
|
|
121
|
+
id,
|
|
122
|
+
} = params;
|
|
123
|
+
|
|
124
|
+
await collection.delete({ id });
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export default addDBEditorEndpoints;
|
|
@@ -0,0 +1,319 @@
|
|
|
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
|
+
|
|
176
|
+
// Return the augmented params
|
|
177
|
+
return augmentedParams;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Validate a signed request. Throws an error if invalid
|
|
182
|
+
* @author Gabe Abrams
|
|
183
|
+
* @param opts object containing all arguments
|
|
184
|
+
* @param opts.method the method of the data validate
|
|
185
|
+
* @param opts.path the http request path to validate
|
|
186
|
+
* @param opts.scope the name of the scope to validate
|
|
187
|
+
* @param opts.params the request data to validate
|
|
188
|
+
* @returns parsed and validated params
|
|
189
|
+
*/
|
|
190
|
+
export const validateSignedRequest = async (
|
|
191
|
+
opts: {
|
|
192
|
+
method: string,
|
|
193
|
+
path: string,
|
|
194
|
+
scope: string,
|
|
195
|
+
params: { [key: string]: any },
|
|
196
|
+
},
|
|
197
|
+
) => {
|
|
198
|
+
/* ---------- Collect Info ---------- */
|
|
199
|
+
|
|
200
|
+
// Get the signature
|
|
201
|
+
if (!opts.params.oauth_signature) {
|
|
202
|
+
throw new ErrorWithCode(
|
|
203
|
+
'Could not validate a cross-server request there was no oauth signature.',
|
|
204
|
+
ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const signature = opts.params.oauth_signature;
|
|
208
|
+
|
|
209
|
+
// Get the timestamp
|
|
210
|
+
if (
|
|
211
|
+
// No timestamp
|
|
212
|
+
!opts.params.oauth_timestamp
|
|
213
|
+
// Invalid timestamp
|
|
214
|
+
|| Number.isNaN(Number.parseInt(opts.params.oauth_timestamp, 10))
|
|
215
|
+
) {
|
|
216
|
+
throw new ErrorWithCode(
|
|
217
|
+
'Could not validate a cross-server request there was no valid oauth timestamp.',
|
|
218
|
+
ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const timestamp = Number.parseInt(opts.params.oauth_timestamp, 10);
|
|
222
|
+
|
|
223
|
+
// Get the key
|
|
224
|
+
if (!opts.params.oauth_consumer_key) {
|
|
225
|
+
throw new ErrorWithCode(
|
|
226
|
+
'Could not validate a cross-server request there was no oauth consumer key.',
|
|
227
|
+
ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const key = opts.params.oauth_consumer_key;
|
|
231
|
+
|
|
232
|
+
// Get the rest of the info
|
|
233
|
+
const {
|
|
234
|
+
method,
|
|
235
|
+
path,
|
|
236
|
+
params,
|
|
237
|
+
scope,
|
|
238
|
+
} = opts;
|
|
239
|
+
|
|
240
|
+
/* ------- Look Up Credential ------- */
|
|
241
|
+
|
|
242
|
+
// Get the cross-server credential collection
|
|
243
|
+
const crossServerCredentialCollection = internalGetCrossServerCredentialCollection();
|
|
244
|
+
if (!crossServerCredentialCollection) {
|
|
245
|
+
throw new ErrorWithCode(
|
|
246
|
+
'Could not validate a cross-server request because the cross-server credential collection was not ready in time.',
|
|
247
|
+
ExpressKitErrorCode.SignedRequestInvalidCollection,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Get the cross-server credential
|
|
252
|
+
const crossServerCredentialMatches: CrossServerCredential[] = await crossServerCredentialCollection.find({ key });
|
|
253
|
+
if (!crossServerCredentialMatches || crossServerCredentialMatches.length === 0) {
|
|
254
|
+
throw new ErrorWithCode(
|
|
255
|
+
'Could not validate a cross-server request because the credential was not found.',
|
|
256
|
+
ExpressKitErrorCode.SignedRequestInvalidCredential,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
const crossServerCredential = crossServerCredentialMatches[0];
|
|
260
|
+
|
|
261
|
+
// Make sure the scope is included
|
|
262
|
+
const allowedScopes = crossServerCredential.scopes;
|
|
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
|
+
// Curate what goes into the params
|
|
283
|
+
const paramsToSign: {
|
|
284
|
+
[key: string]: any,
|
|
285
|
+
} = {
|
|
286
|
+
...params,
|
|
287
|
+
};
|
|
288
|
+
Object.keys(paramsToSign).forEach((key) => {
|
|
289
|
+
// Delete oauth params
|
|
290
|
+
if (key.startsWith('oauth_')) {
|
|
291
|
+
delete paramsToSign[key];
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Generate a new signature to compare
|
|
296
|
+
const expectedSignature = await genSignature({
|
|
297
|
+
method,
|
|
298
|
+
path,
|
|
299
|
+
params: paramsToSign,
|
|
300
|
+
secret,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Make sure the signatures match
|
|
304
|
+
if (signature !== expectedSignature) {
|
|
305
|
+
throw new ErrorWithCode(
|
|
306
|
+
'Could not validate a cross-server request because the signature did not match.',
|
|
307
|
+
ExpressKitErrorCode.SignedRequestInvalidSignature,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Make sure the timestamp was recent enough
|
|
312
|
+
const elapsedMs = Math.abs(Date.now() - timestamp);
|
|
313
|
+
if (elapsedMs > MINUTE_IN_MS) {
|
|
314
|
+
throw new ErrorWithCode(
|
|
315
|
+
'Could not validate a cross-server request because the request was too old.',
|
|
316
|
+
ExpressKitErrorCode.SignedRequestInvalidTimestamp,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
};
|