paygate-mcp 8.90.0 → 8.92.0
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/dist/server.d.ts +5 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +117 -25
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/server.d.ts
CHANGED
|
@@ -380,6 +380,11 @@ export declare class PayGateServer {
|
|
|
380
380
|
private syncKeyMutation;
|
|
381
381
|
/** Resolve the CORS origin based on config and incoming request Origin header */
|
|
382
382
|
private resolveCorsOrigin;
|
|
383
|
+
/**
|
|
384
|
+
* Check Content-Type is JSON. Returns true if valid, false and sends 415 if not.
|
|
385
|
+
* Exempt paths (like /oauth/token) accept form-urlencoded per RFC 6749.
|
|
386
|
+
*/
|
|
387
|
+
private requireJsonContentType;
|
|
383
388
|
private readBody;
|
|
384
389
|
stop(): Promise<void>;
|
|
385
390
|
/**
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAgB,eAAe,EAA0B,MAAM,MAAM,CAAC;AAI7E,OAAO,EAAE,aAAa,EAAkB,mBAAmB,EAAkB,MAAM,SAAS,CAAC;AAE7F,OAAO,EAAE,MAAM,EAAiC,MAAM,UAAU,CAAC;AASjE,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,cAAc,EAAqD,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAmB,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAS,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,eAAe,EAA6B,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,aAAa,EAAqB,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG3C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -135,6 +135,44 @@ function clampArray(arr, maxLen) {
|
|
|
135
135
|
return arr;
|
|
136
136
|
return arr.slice(0, maxLen);
|
|
137
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Sanitize error messages before sending to clients — prevents information disclosure.
|
|
140
|
+
* Returns a generic message unless the error is a known-safe validation error.
|
|
141
|
+
* The full error is returned for internal logging only.
|
|
142
|
+
*/
|
|
143
|
+
function safeErrorMessage(err, fallback = 'Invalid request') {
|
|
144
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
145
|
+
// Truncate before regex matching to prevent ReDoS on crafted long strings
|
|
146
|
+
const msg = raw.slice(0, 500);
|
|
147
|
+
// Allow known-safe, controlled error messages to pass through.
|
|
148
|
+
// These are validation messages from our own code, not system/library errors.
|
|
149
|
+
const safePatterns = [
|
|
150
|
+
/^invalid_grant/,
|
|
151
|
+
/^Request body too large$/,
|
|
152
|
+
/^Request body read timeout$/,
|
|
153
|
+
/^Missing required field/i,
|
|
154
|
+
/^Invalid (?:key|token|group|filter|parameter|redirect)/i,
|
|
155
|
+
/^(?:Key|Token|Group|Filter)\b.*\bnot found/i,
|
|
156
|
+
/^Unknown (?:client|action)/i,
|
|
157
|
+
/^Insufficient/i,
|
|
158
|
+
/^Duplicate/i,
|
|
159
|
+
/^Not found/i,
|
|
160
|
+
/^Unauthorized/i,
|
|
161
|
+
/^Forbidden/i,
|
|
162
|
+
/^(?:ACL|Quota|Rate) limit/i,
|
|
163
|
+
/^(?:Group|Filter) (?:must have|rule must)/i,
|
|
164
|
+
/^(?:Group) '.+' already exists/i,
|
|
165
|
+
/^.+(?:is required|are required)/i, // validation messages: "X is required"
|
|
166
|
+
/^Only .+ (?:is |are )?supported/i, // capability constraints
|
|
167
|
+
/^No API key linked/i, // OAuth setup validation
|
|
168
|
+
/^code_challenge/i, // PKCE validation
|
|
169
|
+
];
|
|
170
|
+
for (const pattern of safePatterns) {
|
|
171
|
+
if (pattern.test(msg))
|
|
172
|
+
return msg;
|
|
173
|
+
}
|
|
174
|
+
return fallback;
|
|
175
|
+
}
|
|
138
176
|
/** Truncate user-supplied strings to MAX_STRING_FIELD to prevent log injection and memory abuse. */
|
|
139
177
|
function sanitizeString(value, maxLen = MAX_STRING_FIELD) {
|
|
140
178
|
if (!value)
|
|
@@ -565,6 +603,7 @@ class PayGateServer {
|
|
|
565
603
|
this.server.requestTimeout = this.config.requestTimeoutMs ?? 30_000; // 30s max per request (Node default: 0 = none)
|
|
566
604
|
this.server.headersTimeout = this.config.headersTimeoutMs ?? 10_000; // 10s to receive headers (Node default: 60s)
|
|
567
605
|
this.server.keepAliveTimeout = this.config.keepAliveTimeoutMs ?? 65_000; // 65s keep-alive (> typical 60s LB idle)
|
|
606
|
+
this.server.maxConnections = this.config.maxConnections ?? 10_000; // Cap concurrent TCP connections to prevent FD exhaustion
|
|
568
607
|
if (this.config.maxRequestsPerSocket) {
|
|
569
608
|
this.server.maxRequestsPerSocket = this.config.maxRequestsPerSocket; // Limit pipelined requests per socket
|
|
570
609
|
}
|
|
@@ -714,7 +753,8 @@ class PayGateServer {
|
|
|
714
753
|
return this.handleCreateKey(req, res);
|
|
715
754
|
if (req.method === 'GET')
|
|
716
755
|
return this.handleListKeys(req, res);
|
|
717
|
-
|
|
756
|
+
this.sendError(res, 405, 'Method not allowed. Use GET or POST.');
|
|
757
|
+
return;
|
|
718
758
|
case '/keys/revoke':
|
|
719
759
|
return this.handleRevokeKey(req, res);
|
|
720
760
|
case '/keys/suspend':
|
|
@@ -809,7 +849,8 @@ class PayGateServer {
|
|
|
809
849
|
return this.handleListTemplates(req, res);
|
|
810
850
|
if (req.method === 'POST')
|
|
811
851
|
return this.handleCreateTemplate(req, res);
|
|
812
|
-
|
|
852
|
+
this.sendError(res, 405, 'Method not allowed. Use GET or POST.');
|
|
853
|
+
return;
|
|
813
854
|
case '/keys/templates/delete':
|
|
814
855
|
return this.handleDeleteTemplate(req, res);
|
|
815
856
|
case '/topup':
|
|
@@ -882,14 +923,16 @@ class PayGateServer {
|
|
|
882
923
|
return this.handleGetAlerts(req, res);
|
|
883
924
|
if (req.method === 'POST')
|
|
884
925
|
return this.handleConfigureAlerts(req, res);
|
|
885
|
-
|
|
926
|
+
this.sendError(res, 405, 'Method not allowed. Use GET or POST.');
|
|
927
|
+
return;
|
|
886
928
|
// ─── Webhook admin endpoints ─────────────────────────────────────
|
|
887
929
|
case '/webhooks/dead-letter':
|
|
888
930
|
if (req.method === 'GET')
|
|
889
931
|
return this.handleGetDeadLetters(req, res);
|
|
890
932
|
if (req.method === 'DELETE')
|
|
891
933
|
return this.handleClearDeadLetters(req, res);
|
|
892
|
-
|
|
934
|
+
this.sendError(res, 405, 'Method not allowed. Use GET or DELETE.');
|
|
935
|
+
return;
|
|
893
936
|
case '/webhooks/replay':
|
|
894
937
|
return this.handleWebhookReplay(req, res);
|
|
895
938
|
case '/webhooks/stats':
|
|
@@ -907,7 +950,8 @@ class PayGateServer {
|
|
|
907
950
|
return this.handleListWebhookFilters(req, res);
|
|
908
951
|
if (req.method === 'POST')
|
|
909
952
|
return this.handleCreateWebhookFilter(req, res);
|
|
910
|
-
|
|
953
|
+
this.sendError(res, 405, 'Method not allowed. Use GET or POST.');
|
|
954
|
+
return;
|
|
911
955
|
case '/webhooks/filters/update':
|
|
912
956
|
return this.handleUpdateWebhookFilter(req, res);
|
|
913
957
|
case '/webhooks/filters/delete':
|
|
@@ -918,7 +962,8 @@ class PayGateServer {
|
|
|
918
962
|
return this.handleListTeams(req, res);
|
|
919
963
|
if (req.method === 'POST')
|
|
920
964
|
return this.handleCreateTeam(req, res);
|
|
921
|
-
|
|
965
|
+
this.sendError(res, 405, 'Method not allowed. Use GET or POST.');
|
|
966
|
+
return;
|
|
922
967
|
case '/teams/update':
|
|
923
968
|
return this.handleUpdateTeam(req, res);
|
|
924
969
|
case '/teams/delete':
|
|
@@ -936,7 +981,8 @@ class PayGateServer {
|
|
|
936
981
|
case '/tokens':
|
|
937
982
|
if (req.method === 'POST')
|
|
938
983
|
return this.handleCreateToken(req, res);
|
|
939
|
-
|
|
984
|
+
this.sendError(res, 405, 'Method not allowed. Use POST.');
|
|
985
|
+
return;
|
|
940
986
|
case '/tokens/revoke':
|
|
941
987
|
return this.handleRevokeToken(req, res);
|
|
942
988
|
case '/tokens/revoked':
|
|
@@ -947,7 +993,8 @@ class PayGateServer {
|
|
|
947
993
|
return this.handleCreateAdminKey(req, res);
|
|
948
994
|
if (req.method === 'GET')
|
|
949
995
|
return this.handleListAdminKeys(req, res);
|
|
950
|
-
|
|
996
|
+
this.sendError(res, 405, 'Method not allowed. Use GET or POST.');
|
|
997
|
+
return;
|
|
951
998
|
case '/admin/keys/revoke':
|
|
952
999
|
return this.handleRevokeAdminKey(req, res);
|
|
953
1000
|
case '/admin/keys/rotate-bootstrap':
|
|
@@ -1282,7 +1329,8 @@ class PayGateServer {
|
|
|
1282
1329
|
return this.handleListGroups(req, res);
|
|
1283
1330
|
if (req.method === 'POST')
|
|
1284
1331
|
return this.handleCreateGroup(req, res);
|
|
1285
|
-
|
|
1332
|
+
this.sendError(res, 405, 'Method not allowed. Use GET or POST.');
|
|
1333
|
+
return;
|
|
1286
1334
|
case '/groups/update':
|
|
1287
1335
|
return this.handleUpdateGroup(req, res);
|
|
1288
1336
|
case '/groups/delete':
|
|
@@ -1346,6 +1394,8 @@ class PayGateServer {
|
|
|
1346
1394
|
this.sendError(res, 405, 'Method not allowed');
|
|
1347
1395
|
return;
|
|
1348
1396
|
}
|
|
1397
|
+
if (!this.requireJsonContentType(req, res))
|
|
1398
|
+
return;
|
|
1349
1399
|
const body = await this.readBody(req);
|
|
1350
1400
|
let request;
|
|
1351
1401
|
try {
|
|
@@ -2518,7 +2568,8 @@ class PayGateServer {
|
|
|
2518
2568
|
}
|
|
2519
2569
|
}
|
|
2520
2570
|
catch (e) {
|
|
2521
|
-
|
|
2571
|
+
this.logger.warn('Bulk operation failed', { index: i, action: op.action, error: e.message });
|
|
2572
|
+
results.push({ index: i, action: op.action || 'unknown', success: false, error: safeErrorMessage(e, 'Operation failed') });
|
|
2522
2573
|
}
|
|
2523
2574
|
}
|
|
2524
2575
|
const succeeded = results.filter(r => r.success).length;
|
|
@@ -4187,6 +4238,8 @@ class PayGateServer {
|
|
|
4187
4238
|
this.sendError(res, 405, 'Method not allowed');
|
|
4188
4239
|
return;
|
|
4189
4240
|
}
|
|
4241
|
+
if (!this.requireJsonContentType(req, res))
|
|
4242
|
+
return;
|
|
4190
4243
|
const body = await this.readBody(req);
|
|
4191
4244
|
let params;
|
|
4192
4245
|
try {
|
|
@@ -4221,8 +4274,9 @@ class PayGateServer {
|
|
|
4221
4274
|
});
|
|
4222
4275
|
}
|
|
4223
4276
|
catch (err) {
|
|
4277
|
+
this.logger.warn('OAuth client registration failed', { error: err.message });
|
|
4224
4278
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
4225
|
-
res.end(JSON.stringify({ error: 'invalid_client_metadata', error_description: err
|
|
4279
|
+
res.end(JSON.stringify({ error: 'invalid_client_metadata', error_description: safeErrorMessage(err, 'Invalid client metadata') }));
|
|
4226
4280
|
}
|
|
4227
4281
|
}
|
|
4228
4282
|
/** GET/POST /oauth/authorize — Authorization endpoint */
|
|
@@ -4291,13 +4345,15 @@ class PayGateServer {
|
|
|
4291
4345
|
res.end();
|
|
4292
4346
|
}
|
|
4293
4347
|
catch (err) {
|
|
4294
|
-
const
|
|
4348
|
+
const rawMsg = err.message;
|
|
4349
|
+
this.logger.warn('OAuth authorization failed', { error: rawMsg });
|
|
4350
|
+
const safeMsg = safeErrorMessage(err, 'Authorization failed');
|
|
4295
4351
|
// If there's a redirect URI and client is valid, redirect with error
|
|
4296
4352
|
if (redirectUri) {
|
|
4297
4353
|
try {
|
|
4298
4354
|
const redirectUrl = new URL(redirectUri);
|
|
4299
4355
|
redirectUrl.searchParams.set('error', 'server_error');
|
|
4300
|
-
redirectUrl.searchParams.set('error_description',
|
|
4356
|
+
redirectUrl.searchParams.set('error_description', safeMsg);
|
|
4301
4357
|
if (state)
|
|
4302
4358
|
redirectUrl.searchParams.set('state', state);
|
|
4303
4359
|
res.writeHead(302, { Location: redirectUrl.toString() });
|
|
@@ -4307,7 +4363,7 @@ class PayGateServer {
|
|
|
4307
4363
|
catch { /* fall through to JSON error */ }
|
|
4308
4364
|
}
|
|
4309
4365
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
4310
|
-
res.end(JSON.stringify({ error: 'invalid_request', error_description:
|
|
4366
|
+
res.end(JSON.stringify({ error: 'invalid_request', error_description: safeMsg }));
|
|
4311
4367
|
}
|
|
4312
4368
|
}
|
|
4313
4369
|
/** POST /oauth/token — Token endpoint */
|
|
@@ -4320,13 +4376,15 @@ class PayGateServer {
|
|
|
4320
4376
|
this.sendError(res, 405, 'Method not allowed');
|
|
4321
4377
|
return;
|
|
4322
4378
|
}
|
|
4379
|
+
if (!this.requireJsonContentType(req, res))
|
|
4380
|
+
return;
|
|
4323
4381
|
const body = await this.readBody(req);
|
|
4324
4382
|
let params;
|
|
4325
4383
|
try {
|
|
4326
4384
|
params = safeJsonParse(body);
|
|
4327
4385
|
}
|
|
4328
4386
|
catch {
|
|
4329
|
-
// Try URL-encoded form data
|
|
4387
|
+
// Try URL-encoded form data (RFC 6749)
|
|
4330
4388
|
params = {};
|
|
4331
4389
|
const query = new URLSearchParams(body);
|
|
4332
4390
|
for (const [k, v] of query)
|
|
@@ -4379,10 +4437,12 @@ class PayGateServer {
|
|
|
4379
4437
|
}
|
|
4380
4438
|
}
|
|
4381
4439
|
catch (err) {
|
|
4382
|
-
const
|
|
4383
|
-
|
|
4440
|
+
const rawMsg = err.message;
|
|
4441
|
+
this.logger.warn('OAuth token exchange failed', { error: rawMsg });
|
|
4442
|
+
const errorCode = rawMsg.startsWith('invalid_grant') ? 'invalid_grant' : 'invalid_request';
|
|
4443
|
+
const safeMsg = safeErrorMessage(err, 'Token exchange failed');
|
|
4384
4444
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
4385
|
-
res.end(JSON.stringify({ error: errorCode, error_description:
|
|
4445
|
+
res.end(JSON.stringify({ error: errorCode, error_description: safeMsg }));
|
|
4386
4446
|
}
|
|
4387
4447
|
}
|
|
4388
4448
|
/** POST /oauth/revoke — Token revocation (RFC 7009) */
|
|
@@ -9646,7 +9706,8 @@ class PayGateServer {
|
|
|
9646
9706
|
fileConfig = JSON.parse(raw);
|
|
9647
9707
|
}
|
|
9648
9708
|
catch (err) {
|
|
9649
|
-
this.
|
|
9709
|
+
this.logger.error('Config file read/parse failed', { error: err.message, path: filePath });
|
|
9710
|
+
this.sendError(res, 400, 'Failed to read or parse config file');
|
|
9650
9711
|
return;
|
|
9651
9712
|
}
|
|
9652
9713
|
// Validate the loaded config
|
|
@@ -9936,7 +9997,8 @@ class PayGateServer {
|
|
|
9936
9997
|
}
|
|
9937
9998
|
});
|
|
9938
9999
|
reqObj.on('error', (err) => {
|
|
9939
|
-
|
|
10000
|
+
this.logger.warn('Webhook test delivery failed', { error: err.message, url: parsed.hostname });
|
|
10001
|
+
resolve({ success: false, error: 'Connection failed', responseTime: Date.now() - startTime });
|
|
9940
10002
|
});
|
|
9941
10003
|
reqObj.on('timeout', () => {
|
|
9942
10004
|
reqObj.destroy();
|
|
@@ -10003,7 +10065,8 @@ class PayGateServer {
|
|
|
10003
10065
|
this.sendJson(res, 201, rule);
|
|
10004
10066
|
}
|
|
10005
10067
|
catch (err) {
|
|
10006
|
-
this.
|
|
10068
|
+
this.logger.warn('Webhook filter creation failed', { error: err.message });
|
|
10069
|
+
this.sendError(res, 400, safeErrorMessage(err, 'Failed to create webhook filter'));
|
|
10007
10070
|
}
|
|
10008
10071
|
}
|
|
10009
10072
|
async handleUpdateWebhookFilter(req, res) {
|
|
@@ -10052,7 +10115,8 @@ class PayGateServer {
|
|
|
10052
10115
|
this.sendJson(res, 200, rule);
|
|
10053
10116
|
}
|
|
10054
10117
|
catch (err) {
|
|
10055
|
-
this.
|
|
10118
|
+
this.logger.warn('Webhook filter update failed', { error: err.message });
|
|
10119
|
+
this.sendError(res, 400, safeErrorMessage(err, 'Failed to update webhook filter'));
|
|
10056
10120
|
}
|
|
10057
10121
|
}
|
|
10058
10122
|
async handleDeleteWebhookFilter(req, res) {
|
|
@@ -10192,6 +10256,9 @@ class PayGateServer {
|
|
|
10192
10256
|
this.sendError(res, 403, 'Insufficient permissions', { requiredRole: minRole, currentRole: record.role });
|
|
10193
10257
|
return false;
|
|
10194
10258
|
}
|
|
10259
|
+
// Content-Type enforcement for POST requests (after auth, before body read)
|
|
10260
|
+
if (req.method === 'POST' && !this.requireJsonContentType(req, res))
|
|
10261
|
+
return false;
|
|
10195
10262
|
return true;
|
|
10196
10263
|
}
|
|
10197
10264
|
// ─── /teams — Team management ────────────────────────────────────────────
|
|
@@ -10593,7 +10660,8 @@ class PayGateServer {
|
|
|
10593
10660
|
this.sendJson(res, 201, group);
|
|
10594
10661
|
}
|
|
10595
10662
|
catch (err) {
|
|
10596
|
-
this.
|
|
10663
|
+
this.logger.warn('Group creation failed', { error: err.message });
|
|
10664
|
+
this.sendError(res, 400, safeErrorMessage(err, 'Failed to create group'));
|
|
10597
10665
|
}
|
|
10598
10666
|
}
|
|
10599
10667
|
async handleUpdateGroup(req, res) {
|
|
@@ -10643,7 +10711,8 @@ class PayGateServer {
|
|
|
10643
10711
|
this.sendJson(res, 200, group);
|
|
10644
10712
|
}
|
|
10645
10713
|
catch (err) {
|
|
10646
|
-
this.
|
|
10714
|
+
this.logger.warn('Group update failed', { error: err.message });
|
|
10715
|
+
this.sendError(res, 400, safeErrorMessage(err, 'Failed to update group'));
|
|
10647
10716
|
}
|
|
10648
10717
|
}
|
|
10649
10718
|
async handleDeleteGroup(req, res) {
|
|
@@ -10723,7 +10792,8 @@ class PayGateServer {
|
|
|
10723
10792
|
this.sendJson(res, 200, { ok: true, message: `Key assigned to group ${groupId}` });
|
|
10724
10793
|
}
|
|
10725
10794
|
catch (err) {
|
|
10726
|
-
this.
|
|
10795
|
+
this.logger.warn('Group key assignment failed', { error: err.message, groupId });
|
|
10796
|
+
this.sendError(res, 400, safeErrorMessage(err, 'Failed to assign key to group'));
|
|
10727
10797
|
}
|
|
10728
10798
|
}
|
|
10729
10799
|
async handleRemoveKeyFromGroup(req, res) {
|
|
@@ -11011,6 +11081,28 @@ class PayGateServer {
|
|
|
11011
11081
|
}
|
|
11012
11082
|
return '*';
|
|
11013
11083
|
}
|
|
11084
|
+
/**
|
|
11085
|
+
* Check Content-Type is JSON. Returns true if valid, false and sends 415 if not.
|
|
11086
|
+
* Exempt paths (like /oauth/token) accept form-urlencoded per RFC 6749.
|
|
11087
|
+
*/
|
|
11088
|
+
requireJsonContentType(req, res) {
|
|
11089
|
+
if (req.method !== 'POST')
|
|
11090
|
+
return true; // Only enforce for POST
|
|
11091
|
+
const ct = (req.headers['content-type'] || '').toLowerCase();
|
|
11092
|
+
const url = req.url?.split('?')[0] || '/';
|
|
11093
|
+
if (url === '/oauth/token') {
|
|
11094
|
+
// OAuth token endpoint accepts both JSON and form-encoded per RFC 6749
|
|
11095
|
+
if (ct.startsWith('application/json') || ct.startsWith('application/x-www-form-urlencoded'))
|
|
11096
|
+
return true;
|
|
11097
|
+
this.sendError(res, 415, 'Unsupported Media Type. Use application/json or application/x-www-form-urlencoded');
|
|
11098
|
+
return false;
|
|
11099
|
+
}
|
|
11100
|
+
if (!ct.startsWith('application/json')) {
|
|
11101
|
+
this.sendError(res, 415, 'Unsupported Media Type. Use application/json');
|
|
11102
|
+
return false;
|
|
11103
|
+
}
|
|
11104
|
+
return true;
|
|
11105
|
+
}
|
|
11014
11106
|
readBody(req) {
|
|
11015
11107
|
return new Promise((resolve, reject) => {
|
|
11016
11108
|
let body = '';
|