@app-connect/core 1.7.21 → 1.7.22
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/README.md +8 -1
- package/connector/developerPortal.js +4 -4
- package/docs/README.md +50 -0
- package/docs/architecture.md +93 -0
- package/docs/connectors.md +117 -0
- package/docs/handlers.md +125 -0
- package/docs/libraries.md +101 -0
- package/docs/models.md +144 -0
- package/docs/routes.md +115 -0
- package/docs/tests.md +73 -0
- package/handlers/admin.js +22 -2
- package/handlers/auth.js +51 -10
- package/handlers/log.js +4 -4
- package/handlers/managedAuth.js +446 -0
- package/index.js +264 -34
- package/lib/jwt.js +1 -1
- package/mcp/tools/createCallLog.js +5 -1
- package/mcp/tools/createContact.js +5 -1
- package/mcp/tools/createMessageLog.js +5 -1
- package/mcp/tools/findContactByName.js +5 -1
- package/mcp/tools/findContactByPhone.js +6 -2
- package/mcp/tools/getCallLog.js +5 -1
- package/mcp/tools/rcGetCallLogs.js +6 -2
- package/mcp/tools/updateCallLog.js +5 -1
- package/mcp/ui/App/lib/developerPortal.ts +1 -1
- package/package.json +72 -72
- package/releaseNotes.json +8 -0
- package/test/handlers/admin.test.js +34 -0
- package/test/handlers/auth.test.js +402 -6
- package/test/handlers/managedAuth.test.js +458 -0
- package/test/index.test.js +105 -0
- package/test/lib/jwt.test.js +15 -0
- package/test/mcp/tools/createCallLog.test.js +11 -0
- package/test/mcp/tools/createContact.test.js +58 -0
- package/test/mcp/tools/createMessageLog.test.js +15 -0
- package/test/mcp/tools/findContactByName.test.js +12 -0
- package/test/mcp/tools/findContactByPhone.test.js +12 -0
- package/test/mcp/tools/getCallLog.test.js +12 -0
- package/test/mcp/tools/rcGetCallLogs.test.js +56 -0
- package/test/mcp/tools/updateCallLog.test.js +14 -0
- package/test/routes/managedAuthRoutes.test.js +132 -0
- package/test/setup.js +2 -0
package/index.js
CHANGED
|
@@ -35,6 +35,7 @@ const s3ErrorLogReport = require('./lib/s3ErrorLogReport');
|
|
|
35
35
|
const pluginCore = require('./handlers/plugin');
|
|
36
36
|
const { handleDatabaseError } = require('./lib/errorHandler');
|
|
37
37
|
const { updateAuthSession } = require('./lib/authSession');
|
|
38
|
+
const managedAuthCore = require('./handlers/managedAuth');
|
|
38
39
|
|
|
39
40
|
let packageJson = null;
|
|
40
41
|
try {
|
|
@@ -108,9 +109,79 @@ function getAnalyticsVariablesInReqHeaders({ headers }) {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
const JWT_REFRESH_THRESHOLD_SECONDS = 7 * 24 * 60 * 60; // 1 week
|
|
113
|
+
const JWT_LEGACY_LONG_LIVED_THRESHOLD_SECONDS = 365 * 24 * 60 * 60; // 1 year
|
|
114
|
+
|
|
115
|
+
function getBearerTokenFromRequest(req) {
|
|
116
|
+
const authHeader = req.headers?.authorization || req.headers?.Authorization;
|
|
117
|
+
if (!authHeader || typeof authHeader !== 'string') {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const [scheme, token] = authHeader.split(' ');
|
|
121
|
+
if (!scheme || scheme.toLowerCase() !== 'bearer' || !token) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return token;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function normalizeJwtFromRequest(req, res, next) {
|
|
128
|
+
if (req.path?.startsWith('/mcp')) {
|
|
129
|
+
return next();
|
|
130
|
+
}
|
|
131
|
+
const originalBearerToken = getBearerTokenFromRequest(req);
|
|
132
|
+
const queryToken = req.query?.jwtToken;
|
|
133
|
+
let bearerToken = originalBearerToken;
|
|
134
|
+
|
|
135
|
+
// Backward compatibility: promote query jwtToken to Authorization Bearer.
|
|
136
|
+
if (!bearerToken && queryToken) {
|
|
137
|
+
req.headers.authorization = `Bearer ${queryToken}`;
|
|
138
|
+
bearerToken = queryToken;
|
|
139
|
+
// Don't refresh JWT because old version cannot update its local token storage to support refreshed token.
|
|
140
|
+
return next();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const token = bearerToken;
|
|
144
|
+
|
|
145
|
+
if (!token) {
|
|
146
|
+
return next();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const decodedToken = jwt.decodeJwt(token);
|
|
150
|
+
if (!decodedToken?.id) {
|
|
151
|
+
req.invalidJwtToken = true;
|
|
152
|
+
if (req.query?.jwtToken) {
|
|
153
|
+
delete req.query.jwtToken;
|
|
154
|
+
}
|
|
155
|
+
return next();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
req.jwtToken = token;
|
|
159
|
+
req.jwtAuth = decodedToken;
|
|
160
|
+
|
|
161
|
+
if (typeof decodedToken.exp === 'number') {
|
|
162
|
+
const now = Math.floor(Date.now() / 1000);
|
|
163
|
+
const timeLeft = decodedToken.exp - now;
|
|
164
|
+
const isBearerAuth = !!originalBearerToken;
|
|
165
|
+
const shouldRefreshNearExpiry = timeLeft <= JWT_REFRESH_THRESHOLD_SECONDS;
|
|
166
|
+
// Rotate legacy 120y tokens only for Bearer auth clients (they can persist refreshed headers).
|
|
167
|
+
const shouldRefreshLegacyLongLivedBearer = isBearerAuth && timeLeft > JWT_LEGACY_LONG_LIVED_THRESHOLD_SECONDS;
|
|
168
|
+
if (shouldRefreshNearExpiry || shouldRefreshLegacyLongLivedBearer) {
|
|
169
|
+
const refreshedToken = jwt.generateJwt({
|
|
170
|
+
id: decodedToken.id.toString(),
|
|
171
|
+
platform: decodedToken.platform
|
|
172
|
+
});
|
|
173
|
+
res.setHeader('x-refreshed-jwt-token', refreshedToken);
|
|
174
|
+
req.jwtToken = refreshedToken;
|
|
175
|
+
req.jwtAuth = jwt.decodeJwt(refreshedToken) || decodedToken;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return next();
|
|
179
|
+
}
|
|
180
|
+
|
|
111
181
|
// Create a router with all core routes
|
|
112
182
|
function createCoreRouter() {
|
|
113
183
|
const router = express.Router();
|
|
184
|
+
router.use(normalizeJwtFromRequest);
|
|
114
185
|
|
|
115
186
|
// Move all app.get, app.post, etc. to router.get, router.post, etc.
|
|
116
187
|
router.get('/releaseNotes', async function (req, res) {
|
|
@@ -219,9 +290,16 @@ function createCoreRouter() {
|
|
|
219
290
|
let extraData = {};
|
|
220
291
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
221
292
|
try {
|
|
222
|
-
const jwtToken = req.query.jwtToken;
|
|
293
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
223
294
|
if (jwtToken) {
|
|
224
|
-
const
|
|
295
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
296
|
+
if (!decodedToken) {
|
|
297
|
+
tracer?.trace('licenseStatus:invalidJwtToken', {});
|
|
298
|
+
res.status(400).send(tracer ? tracer.wrapResponse('Invalid JWT token') : 'Invalid JWT token');
|
|
299
|
+
success = false;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const { id: userId, platform } = decodedToken;
|
|
225
303
|
platformName = platform;
|
|
226
304
|
if (!userId) {
|
|
227
305
|
tracer?.trace('licenseStatus:noUserId', {});
|
|
@@ -286,7 +364,7 @@ function createCoreRouter() {
|
|
|
286
364
|
let statusCode = 200;
|
|
287
365
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
288
366
|
try {
|
|
289
|
-
const jwtToken = req.query.jwtToken;
|
|
367
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
290
368
|
if (jwtToken) {
|
|
291
369
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
292
370
|
if (!decodedToken) {
|
|
@@ -336,6 +414,36 @@ function createCoreRouter() {
|
|
|
336
414
|
eventAddedVia
|
|
337
415
|
});
|
|
338
416
|
});
|
|
417
|
+
router.get('/apiKeyManagedAuthState', async function (req, res) {
|
|
418
|
+
const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
|
|
419
|
+
tracer?.trace('apiKeyManagedAuthState:start', { query: req.query });
|
|
420
|
+
try {
|
|
421
|
+
const platform = req.query.platform;
|
|
422
|
+
const rcAccessToken = req.query.rcAccessToken;
|
|
423
|
+
if (!platform) {
|
|
424
|
+
res.status(400).send(tracer ? tracer.wrapResponse('Missing platform name') : 'Missing platform name');
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (!rcAccessToken) {
|
|
428
|
+
res.status(400).send(tracer ? tracer.wrapResponse('Missing RingCentral access token') : 'Missing RingCentral access token');
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const { rcAccountId, rcExtensionId } = await adminCore.validateRcUserToken({ rcAccessToken });
|
|
432
|
+
const managedAuthState = await managedAuthCore.getManagedAuthState({
|
|
433
|
+
platform,
|
|
434
|
+
rcAccountId,
|
|
435
|
+
rcExtensionId,
|
|
436
|
+
connectorId: req.query.connectorId,
|
|
437
|
+
isPrivate: req.query.isPrivate === 'true'
|
|
438
|
+
});
|
|
439
|
+
res.status(200).send(tracer ? tracer.wrapResponse(managedAuthState) : managedAuthState);
|
|
440
|
+
}
|
|
441
|
+
catch (e) {
|
|
442
|
+
logger.error('Get API key managed auth state failed', { stack: e.stack });
|
|
443
|
+
tracer?.traceError('apiKeyManagedAuthState:error', e);
|
|
444
|
+
res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
|
|
445
|
+
}
|
|
446
|
+
});
|
|
339
447
|
// Obsolete
|
|
340
448
|
router.get('/serverVersionInfo', (req, res) => {
|
|
341
449
|
const defaultCrmManifest = connectorRegistry.getManifest('default');
|
|
@@ -389,7 +497,7 @@ function createCoreRouter() {
|
|
|
389
497
|
let success = false;
|
|
390
498
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
391
499
|
try {
|
|
392
|
-
const jwtToken = req.query.jwtToken;
|
|
500
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
393
501
|
if (jwtToken) {
|
|
394
502
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
395
503
|
platformName = unAuthData?.platform ?? 'Unknown';
|
|
@@ -449,6 +557,86 @@ function createCoreRouter() {
|
|
|
449
557
|
eventAddedVia
|
|
450
558
|
});
|
|
451
559
|
});
|
|
560
|
+
router.get('/admin/managedAuth', async function (req, res) {
|
|
561
|
+
const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
|
|
562
|
+
tracer?.trace('getAdminManagedAuth:start', { query: req.query });
|
|
563
|
+
try {
|
|
564
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
565
|
+
if (!jwtToken) {
|
|
566
|
+
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
570
|
+
const user = await UserModel.findByPk(unAuthData?.id);
|
|
571
|
+
if (!user) {
|
|
572
|
+
res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
const { isValidated, rcAccountId } = await adminCore.validateAdminRole({ rcAccessToken: req.query.rcAccessToken });
|
|
576
|
+
if (!isValidated) {
|
|
577
|
+
res.status(403).send(tracer ? tracer.wrapResponse('Admin validation failed') : 'Admin validation failed');
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const managedAuthSettings = await managedAuthCore.getManagedAuthAdminSettings({
|
|
581
|
+
platform: user.platform,
|
|
582
|
+
rcAccountId,
|
|
583
|
+
connectorId: req.query.connectorId,
|
|
584
|
+
isPrivate: req.query.isPrivate === 'true'
|
|
585
|
+
});
|
|
586
|
+
res.status(200).send(tracer ? tracer.wrapResponse(managedAuthSettings) : managedAuthSettings);
|
|
587
|
+
}
|
|
588
|
+
catch (e) {
|
|
589
|
+
logger.error('Get managed auth settings failed', { stack: e.stack });
|
|
590
|
+
tracer?.traceError('getAdminManagedAuth:error', e);
|
|
591
|
+
res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
router.post('/admin/managedAuth', async function (req, res) {
|
|
595
|
+
const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
|
|
596
|
+
tracer?.trace('setAdminManagedAuth:start', { body: { scope: req.body?.scope, rcExtensionId: req.body?.rcExtensionId } });
|
|
597
|
+
try {
|
|
598
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
599
|
+
if (!jwtToken) {
|
|
600
|
+
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
604
|
+
const user = await UserModel.findByPk(unAuthData?.id);
|
|
605
|
+
if (!user) {
|
|
606
|
+
res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
const { isValidated, rcAccountId } = await adminCore.validateAdminRole({ rcAccessToken: req.query.rcAccessToken });
|
|
610
|
+
if (!isValidated) {
|
|
611
|
+
res.status(403).send(tracer ? tracer.wrapResponse('Admin validation failed') : 'Admin validation failed');
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (req.body?.scope === 'user') {
|
|
615
|
+
await managedAuthCore.upsertUserManagedAuthValues({
|
|
616
|
+
rcAccountId,
|
|
617
|
+
platform: user.platform,
|
|
618
|
+
rcExtensionId: req.body?.rcExtensionId,
|
|
619
|
+
rcUserName: req.body?.rcUserName,
|
|
620
|
+
values: req.body?.values ?? {},
|
|
621
|
+
fieldsToRemove: req.body?.fieldsToRemove ?? []
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
await managedAuthCore.upsertOrgManagedAuthValues({
|
|
626
|
+
rcAccountId,
|
|
627
|
+
platform: user.platform,
|
|
628
|
+
values: req.body?.values ?? {},
|
|
629
|
+
fieldsToRemove: req.body?.fieldsToRemove ?? []
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
res.status(200).send(tracer ? tracer.wrapResponse('Shared authentication updated') : 'Shared authentication updated');
|
|
633
|
+
}
|
|
634
|
+
catch (e) {
|
|
635
|
+
logger.error('Set managed auth settings failed', { stack: e.stack });
|
|
636
|
+
tracer?.traceError('setAdminManagedAuth:error', e);
|
|
637
|
+
res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
|
|
638
|
+
}
|
|
639
|
+
});
|
|
452
640
|
router.post('/admin/userMapping', async function (req, res) {
|
|
453
641
|
const requestStartTime = new Date().getTime();
|
|
454
642
|
const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
|
|
@@ -457,7 +645,7 @@ function createCoreRouter() {
|
|
|
457
645
|
let success = false;
|
|
458
646
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
459
647
|
try {
|
|
460
|
-
const jwtToken = req.query.jwtToken;
|
|
648
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
461
649
|
if (jwtToken) {
|
|
462
650
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
463
651
|
platformName = unAuthData?.platform ?? 'Unknown';
|
|
@@ -520,7 +708,7 @@ function createCoreRouter() {
|
|
|
520
708
|
let success = false;
|
|
521
709
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
522
710
|
try {
|
|
523
|
-
const jwtToken = req.query.jwtToken;
|
|
711
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
524
712
|
if (jwtToken) {
|
|
525
713
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
526
714
|
platformName = unAuthData?.platform ?? 'Unknown';
|
|
@@ -581,7 +769,7 @@ function createCoreRouter() {
|
|
|
581
769
|
tracer?.trace('getServerLoggingSettings:start', { query: req.query });
|
|
582
770
|
let platformName = null;
|
|
583
771
|
let success = false;
|
|
584
|
-
const jwtToken = req.query.jwtToken;
|
|
772
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
585
773
|
if (!jwtToken) {
|
|
586
774
|
tracer?.trace('getServerLoggingSettings:noToken', {});
|
|
587
775
|
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
@@ -632,7 +820,7 @@ function createCoreRouter() {
|
|
|
632
820
|
tracer?.trace('setServerLoggingSettings:start', { body: req.body });
|
|
633
821
|
let platformName = null;
|
|
634
822
|
let success = false;
|
|
635
|
-
const jwtToken = req.query.jwtToken;
|
|
823
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
636
824
|
if (!jwtToken) {
|
|
637
825
|
tracer?.trace('setServerLoggingSettings:noToken', {});
|
|
638
826
|
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
@@ -713,7 +901,7 @@ function createCoreRouter() {
|
|
|
713
901
|
let success = false;
|
|
714
902
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
715
903
|
try {
|
|
716
|
-
const jwtToken = req.query.jwtToken;
|
|
904
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
717
905
|
if (jwtToken) {
|
|
718
906
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
719
907
|
platformName = unAuthData?.platform ?? 'Unknown';
|
|
@@ -765,7 +953,7 @@ function createCoreRouter() {
|
|
|
765
953
|
let success = false;
|
|
766
954
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
767
955
|
try {
|
|
768
|
-
const jwtToken = req.query.jwtToken;
|
|
956
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
769
957
|
if (jwtToken) {
|
|
770
958
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
771
959
|
platformName = unAuthData?.platform;
|
|
@@ -814,7 +1002,7 @@ function createCoreRouter() {
|
|
|
814
1002
|
const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
|
|
815
1003
|
tracer?.trace('hostname:start', { query: req.query });
|
|
816
1004
|
try {
|
|
817
|
-
const jwtToken = req.query.jwtToken;
|
|
1005
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
818
1006
|
if (jwtToken) {
|
|
819
1007
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
820
1008
|
const user = await UserModel.findByPk(unAuthData?.id);
|
|
@@ -951,7 +1139,14 @@ function createCoreRouter() {
|
|
|
951
1139
|
router.post('/apiKeyLogin', async function (req, res) {
|
|
952
1140
|
const requestStartTime = new Date().getTime();
|
|
953
1141
|
const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
|
|
954
|
-
tracer?.trace('apiKeyLogin:start', {
|
|
1142
|
+
tracer?.trace('apiKeyLogin:start', {
|
|
1143
|
+
body: {
|
|
1144
|
+
platform: req.body?.platform,
|
|
1145
|
+
hostname: req.body?.hostname,
|
|
1146
|
+
proxyId: req.body?.proxyId,
|
|
1147
|
+
hasAdditionalInfo: !!req.body?.additionalInfo
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
955
1150
|
let platformName = null;
|
|
956
1151
|
let success = false;
|
|
957
1152
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
@@ -962,12 +1157,33 @@ function createCoreRouter() {
|
|
|
962
1157
|
const hostname = req.body.hostname;
|
|
963
1158
|
const proxyId = req.body.proxyId;
|
|
964
1159
|
const additionalInfo = req.body.additionalInfo;
|
|
1160
|
+
const rcAccessToken = req.body.rcAccessToken;
|
|
1161
|
+
const connectorId = req.body.connectorId;
|
|
1162
|
+
const isPrivate = !!req.body.isPrivate;
|
|
965
1163
|
if (!platform) {
|
|
966
1164
|
tracer?.trace('apiKeyLogin:missingPlatform', {});
|
|
967
1165
|
res.status(400).send(tracer ? tracer.wrapResponse('Missing platform name') : 'Missing platform name');
|
|
968
1166
|
return;
|
|
969
1167
|
}
|
|
970
|
-
|
|
1168
|
+
let rcAccountId = null;
|
|
1169
|
+
let rcExtensionId = null;
|
|
1170
|
+
if (rcAccessToken) {
|
|
1171
|
+
const rcUserTokenResult = await adminCore.validateRcUserToken({ rcAccessToken });
|
|
1172
|
+
rcAccountId = rcUserTokenResult.rcAccountId;
|
|
1173
|
+
rcExtensionId = rcUserTokenResult.rcExtensionId;
|
|
1174
|
+
}
|
|
1175
|
+
const { userInfo, returnMessage } = await authCore.onApiKeyLogin({
|
|
1176
|
+
platform,
|
|
1177
|
+
hostname,
|
|
1178
|
+
apiKey,
|
|
1179
|
+
proxyId,
|
|
1180
|
+
rcAccountId,
|
|
1181
|
+
rcExtensionId,
|
|
1182
|
+
connectorId,
|
|
1183
|
+
isPrivate,
|
|
1184
|
+
hashedRcExtensionId: hashedExtensionId,
|
|
1185
|
+
additionalInfo
|
|
1186
|
+
});
|
|
971
1187
|
if (userInfo) {
|
|
972
1188
|
const jwtToken = jwt.generateJwt({
|
|
973
1189
|
id: userInfo.id.toString(),
|
|
@@ -1010,7 +1226,7 @@ function createCoreRouter() {
|
|
|
1010
1226
|
let success = false;
|
|
1011
1227
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1012
1228
|
try {
|
|
1013
|
-
const jwtToken = req.query.jwtToken;
|
|
1229
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1014
1230
|
if (jwtToken) {
|
|
1015
1231
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
1016
1232
|
platformName = unAuthData?.platform ?? 'Unknown';
|
|
@@ -1077,7 +1293,7 @@ function createCoreRouter() {
|
|
|
1077
1293
|
let extraData = {};
|
|
1078
1294
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1079
1295
|
try {
|
|
1080
|
-
const jwtToken = req.query.jwtToken;
|
|
1296
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1081
1297
|
if (jwtToken) {
|
|
1082
1298
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1083
1299
|
tracer?.trace('findContact:jwtDecoded', { decodedToken });
|
|
@@ -1155,7 +1371,7 @@ function createCoreRouter() {
|
|
|
1155
1371
|
let extraData = {};
|
|
1156
1372
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1157
1373
|
try {
|
|
1158
|
-
const jwtToken = req.query.jwtToken;
|
|
1374
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1159
1375
|
if (jwtToken) {
|
|
1160
1376
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1161
1377
|
if (!decodedToken) {
|
|
@@ -1218,7 +1434,7 @@ function createCoreRouter() {
|
|
|
1218
1434
|
let extraData = {};
|
|
1219
1435
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1220
1436
|
try {
|
|
1221
|
-
const jwtToken = req.query.jwtToken;
|
|
1437
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1222
1438
|
if (jwtToken) {
|
|
1223
1439
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1224
1440
|
if (!decodedToken) {
|
|
@@ -1269,7 +1485,7 @@ function createCoreRouter() {
|
|
|
1269
1485
|
let extraData = {};
|
|
1270
1486
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1271
1487
|
try {
|
|
1272
|
-
const jwtToken = req.query.jwtToken;
|
|
1488
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1273
1489
|
if (jwtToken) {
|
|
1274
1490
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1275
1491
|
if (!decodedToken) {
|
|
@@ -1333,7 +1549,7 @@ function createCoreRouter() {
|
|
|
1333
1549
|
let extraData = {};
|
|
1334
1550
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1335
1551
|
try {
|
|
1336
|
-
const jwtToken = req.query.jwtToken;
|
|
1552
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1337
1553
|
if (jwtToken) {
|
|
1338
1554
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1339
1555
|
if (!decodedToken) {
|
|
@@ -1396,7 +1612,7 @@ function createCoreRouter() {
|
|
|
1396
1612
|
let extraData = {};
|
|
1397
1613
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1398
1614
|
try {
|
|
1399
|
-
const jwtToken = req.query.jwtToken;
|
|
1615
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1400
1616
|
if (jwtToken) {
|
|
1401
1617
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1402
1618
|
if (!decodedToken) {
|
|
@@ -1453,9 +1669,15 @@ function createCoreRouter() {
|
|
|
1453
1669
|
let extraData = {};
|
|
1454
1670
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1455
1671
|
try {
|
|
1456
|
-
const jwtToken = req.query.jwtToken;
|
|
1672
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1457
1673
|
if (jwtToken) {
|
|
1458
|
-
const
|
|
1674
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1675
|
+
if (!decodedToken) {
|
|
1676
|
+
tracer?.trace('upsertCallDisposition:invalidJwtToken', {});
|
|
1677
|
+
res.status(400).send(tracer ? tracer.wrapResponse('Invalid JWT token') : 'Invalid JWT token');
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
const { id: userId, platform } = decodedToken;
|
|
1459
1681
|
platformName = platform;
|
|
1460
1682
|
if (!userId) {
|
|
1461
1683
|
tracer?.trace('upsertCallDisposition:invalidToken', {});
|
|
@@ -1522,7 +1744,7 @@ function createCoreRouter() {
|
|
|
1522
1744
|
let extraData = {};
|
|
1523
1745
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1524
1746
|
try {
|
|
1525
|
-
const jwtToken = req.query.jwtToken;
|
|
1747
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1526
1748
|
if (jwtToken) {
|
|
1527
1749
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1528
1750
|
if (!decodedToken) {
|
|
@@ -1586,7 +1808,7 @@ function createCoreRouter() {
|
|
|
1586
1808
|
let statusCode = 200;
|
|
1587
1809
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1588
1810
|
try {
|
|
1589
|
-
const jwtToken = req.query.jwtToken;
|
|
1811
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1590
1812
|
if (!jwtToken) {
|
|
1591
1813
|
tracer?.trace('scheduleCallDown:noToken', {});
|
|
1592
1814
|
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
@@ -1634,7 +1856,7 @@ function createCoreRouter() {
|
|
|
1634
1856
|
let statusCode = 200;
|
|
1635
1857
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1636
1858
|
try {
|
|
1637
|
-
const jwtToken = req.query.jwtToken;
|
|
1859
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1638
1860
|
if (!jwtToken) {
|
|
1639
1861
|
tracer?.trace('getCallDownList:noToken', {});
|
|
1640
1862
|
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
@@ -1675,7 +1897,7 @@ function createCoreRouter() {
|
|
|
1675
1897
|
let statusCode = 200;
|
|
1676
1898
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1677
1899
|
try {
|
|
1678
|
-
const jwtToken = req.query.jwtToken;
|
|
1900
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1679
1901
|
const id = req.query.id;
|
|
1680
1902
|
if (!jwtToken) {
|
|
1681
1903
|
tracer?.trace('deleteCallDownItem:noToken', {});
|
|
@@ -1723,7 +1945,7 @@ function createCoreRouter() {
|
|
|
1723
1945
|
let statusCode = 200;
|
|
1724
1946
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1725
1947
|
try {
|
|
1726
|
-
const jwtToken = req.query.jwtToken;
|
|
1948
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1727
1949
|
if (!jwtToken) {
|
|
1728
1950
|
tracer?.trace('markCallDownCalled:noToken', {});
|
|
1729
1951
|
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
@@ -1770,9 +1992,16 @@ function createCoreRouter() {
|
|
|
1770
1992
|
let statusCode = 200;
|
|
1771
1993
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1772
1994
|
try {
|
|
1773
|
-
const jwtToken = req.query.jwtToken;
|
|
1995
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1774
1996
|
if (jwtToken) {
|
|
1775
|
-
const
|
|
1997
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1998
|
+
if (!decodedToken) {
|
|
1999
|
+
tracer?.trace('findContactWithName:invalidJwtToken', {});
|
|
2000
|
+
res.status(400).send(tracer ? tracer.wrapResponse('Invalid JWT token') : 'Invalid JWT token');
|
|
2001
|
+
success = false;
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
const { id: userId, platform } = decodedToken;
|
|
1776
2005
|
platformName = platform;
|
|
1777
2006
|
const { successful, returnMessage, contact, isRevokeUserSession } = await contactCore.findContactWithName({ platform, userId, name: req.query.name });
|
|
1778
2007
|
if (isRevokeUserSession) {
|
|
@@ -1822,7 +2051,7 @@ function createCoreRouter() {
|
|
|
1822
2051
|
let platformName = null;
|
|
1823
2052
|
let success = false;
|
|
1824
2053
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1825
|
-
const jwtToken = req.query.jwtToken;
|
|
2054
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1826
2055
|
try {
|
|
1827
2056
|
if (jwtToken) {
|
|
1828
2057
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
@@ -1868,7 +2097,7 @@ function createCoreRouter() {
|
|
|
1868
2097
|
let platformName = null;
|
|
1869
2098
|
let success = false;
|
|
1870
2099
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1871
|
-
const jwtToken = req.query.jwtToken;
|
|
2100
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1872
2101
|
try {
|
|
1873
2102
|
if (jwtToken) {
|
|
1874
2103
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
@@ -1909,7 +2138,7 @@ function createCoreRouter() {
|
|
|
1909
2138
|
router.get('/ringcentral/oauth/callback', async function (req, res) {
|
|
1910
2139
|
const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
|
|
1911
2140
|
tracer?.trace('onRingcentralOAuthCallback:start', { query: req.query });
|
|
1912
|
-
const jwtToken = req.query.jwtToken;
|
|
2141
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1913
2142
|
if (jwtToken) {
|
|
1914
2143
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
1915
2144
|
const { code } = req.query;
|
|
@@ -1933,7 +2162,7 @@ function createCoreRouter() {
|
|
|
1933
2162
|
let platformName = null;
|
|
1934
2163
|
let success = false;
|
|
1935
2164
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1936
|
-
const jwtToken = req.query.jwtToken;
|
|
2165
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1937
2166
|
if (jwtToken) {
|
|
1938
2167
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
1939
2168
|
const uploadUrl = await s3ErrorLogReport.getUploadUrl({ userId: unAuthData?.id, platform: unAuthData?.platform });
|
|
@@ -2214,7 +2443,8 @@ function createCoreMiddleware() {
|
|
|
2214
2443
|
}
|
|
2215
2444
|
}),
|
|
2216
2445
|
cors({
|
|
2217
|
-
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE']
|
|
2446
|
+
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'],
|
|
2447
|
+
exposedHeaders: ['x-refreshed-jwt-token']
|
|
2218
2448
|
})
|
|
2219
2449
|
];
|
|
2220
2450
|
}
|
package/lib/jwt.js
CHANGED
|
@@ -2,7 +2,7 @@ const { sign, verify } = require('jsonwebtoken');
|
|
|
2
2
|
const logger = require('./logger');
|
|
3
3
|
|
|
4
4
|
function generateJwt(data) {
|
|
5
|
-
return sign(data, process.env.APP_SERVER_SECRET_KEY, { expiresIn: '
|
|
5
|
+
return sign(data, process.env.APP_SERVER_SECRET_KEY, { expiresIn: '2w' })
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function decodeJwt(token) {
|
|
@@ -220,7 +220,11 @@ async function execute(args) {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
// Decode JWT to get userId and platform
|
|
223
|
-
const
|
|
223
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
224
|
+
if (!decodedToken) {
|
|
225
|
+
throw new Error('Invalid JWT token');
|
|
226
|
+
}
|
|
227
|
+
const { id: userId, platform } = decodedToken;
|
|
224
228
|
|
|
225
229
|
if (!userId) {
|
|
226
230
|
throw new Error('Invalid JWT token: userId not found');
|
|
@@ -57,7 +57,11 @@ async function execute(args) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Decode JWT to get userId and platform
|
|
60
|
-
const
|
|
60
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
61
|
+
if (!decodedToken) {
|
|
62
|
+
throw new Error('Invalid JWT token');
|
|
63
|
+
}
|
|
64
|
+
const { id: userId, platform } = decodedToken;
|
|
61
65
|
|
|
62
66
|
if (!userId) {
|
|
63
67
|
throw new Error('Invalid JWT token: userId not found');
|
|
@@ -228,7 +228,11 @@ async function execute(args) {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// Decode JWT to get userId and platform
|
|
231
|
-
const
|
|
231
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
232
|
+
if (!decodedToken) {
|
|
233
|
+
throw new Error('Invalid JWT token');
|
|
234
|
+
}
|
|
235
|
+
const { id: userId, platform } = decodedToken;
|
|
232
236
|
|
|
233
237
|
if (!userId) {
|
|
234
238
|
throw new Error('Invalid JWT token: userId not found');
|
|
@@ -42,7 +42,11 @@ async function execute(args) {
|
|
|
42
42
|
throw new Error('Not authenticated. Please connect to your CRM first.');
|
|
43
43
|
}
|
|
44
44
|
// Decode JWT to get userId and platform
|
|
45
|
-
const
|
|
45
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
46
|
+
if (!decodedToken) {
|
|
47
|
+
throw new Error('Invalid JWT token');
|
|
48
|
+
}
|
|
49
|
+
const { id: userId, platform } = decodedToken;
|
|
46
50
|
|
|
47
51
|
if (!userId) {
|
|
48
52
|
throw new Error('Invalid JWT token: userId not found');
|
|
@@ -51,7 +51,11 @@ async function execute(args) {
|
|
|
51
51
|
const { jwtToken, phoneNumber, overridingFormat, isExtension } = args;
|
|
52
52
|
|
|
53
53
|
// Decode JWT to get userId and platform
|
|
54
|
-
const
|
|
54
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
55
|
+
if (!decodedToken) {
|
|
56
|
+
throw new Error('Invalid JWT token');
|
|
57
|
+
}
|
|
58
|
+
const { id: userId, platform } = decodedToken;
|
|
55
59
|
|
|
56
60
|
if (!userId) {
|
|
57
61
|
throw new Error('Invalid JWT token: userId not found');
|
|
@@ -94,4 +98,4 @@ async function execute(args) {
|
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
exports.definition = toolDefinition;
|
|
97
|
-
exports.execute = execute;
|
|
101
|
+
exports.execute = execute;
|
package/mcp/tools/getCallLog.js
CHANGED
|
@@ -45,7 +45,11 @@ async function execute(args) {
|
|
|
45
45
|
const { jwtToken, sessionIds, requireDetails = false } = args;
|
|
46
46
|
|
|
47
47
|
// Decode JWT to get userId and platform
|
|
48
|
-
const
|
|
48
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
49
|
+
if (!decodedToken) {
|
|
50
|
+
throw new Error('Invalid JWT token');
|
|
51
|
+
}
|
|
52
|
+
const { id: userId, platform } = decodedToken;
|
|
49
53
|
|
|
50
54
|
if (!userId) {
|
|
51
55
|
throw new Error('Invalid JWT token: userId not found');
|
|
@@ -32,7 +32,11 @@ async function execute(args) {
|
|
|
32
32
|
if (!rcAccessToken) {
|
|
33
33
|
throw new Error('RingCentral access token not found');
|
|
34
34
|
}
|
|
35
|
-
const
|
|
35
|
+
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
36
|
+
if (!decodedToken) {
|
|
37
|
+
throw new Error('Invalid JWT token');
|
|
38
|
+
}
|
|
39
|
+
const { id: userId } = decodedToken;
|
|
36
40
|
if (!userId) {
|
|
37
41
|
throw new Error('Invalid JWT token: userId not found');
|
|
38
42
|
}
|
|
@@ -58,4 +62,4 @@ async function execute(args) {
|
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
exports.definition = toolDefinition;
|
|
61
|
-
exports.execute = execute;
|
|
65
|
+
exports.execute = execute;
|