@app-connect/core 1.7.20 → 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/contact.js +7 -2
- package/handlers/log.js +4 -4
- package/handlers/managedAuth.js +446 -0
- package/index.js +263 -38
- 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 +1 -1
- package/releaseNotes.json +20 -0
- package/test/handlers/admin.test.js +34 -0
- package/test/handlers/auth.test.js +402 -6
- package/test/handlers/contact.test.js +162 -0
- 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,17 +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
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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;
|
|
974
1174
|
}
|
|
975
|
-
const { userInfo, returnMessage } = await authCore.onApiKeyLogin({
|
|
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
|
+
});
|
|
976
1187
|
if (userInfo) {
|
|
977
1188
|
const jwtToken = jwt.generateJwt({
|
|
978
1189
|
id: userInfo.id.toString(),
|
|
@@ -1015,7 +1226,7 @@ function createCoreRouter() {
|
|
|
1015
1226
|
let success = false;
|
|
1016
1227
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1017
1228
|
try {
|
|
1018
|
-
const jwtToken = req.query.jwtToken;
|
|
1229
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1019
1230
|
if (jwtToken) {
|
|
1020
1231
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
1021
1232
|
platformName = unAuthData?.platform ?? 'Unknown';
|
|
@@ -1082,7 +1293,7 @@ function createCoreRouter() {
|
|
|
1082
1293
|
let extraData = {};
|
|
1083
1294
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1084
1295
|
try {
|
|
1085
|
-
const jwtToken = req.query.jwtToken;
|
|
1296
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1086
1297
|
if (jwtToken) {
|
|
1087
1298
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1088
1299
|
tracer?.trace('findContact:jwtDecoded', { decodedToken });
|
|
@@ -1160,7 +1371,7 @@ function createCoreRouter() {
|
|
|
1160
1371
|
let extraData = {};
|
|
1161
1372
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1162
1373
|
try {
|
|
1163
|
-
const jwtToken = req.query.jwtToken;
|
|
1374
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1164
1375
|
if (jwtToken) {
|
|
1165
1376
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1166
1377
|
if (!decodedToken) {
|
|
@@ -1223,7 +1434,7 @@ function createCoreRouter() {
|
|
|
1223
1434
|
let extraData = {};
|
|
1224
1435
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1225
1436
|
try {
|
|
1226
|
-
const jwtToken = req.query.jwtToken;
|
|
1437
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1227
1438
|
if (jwtToken) {
|
|
1228
1439
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1229
1440
|
if (!decodedToken) {
|
|
@@ -1274,7 +1485,7 @@ function createCoreRouter() {
|
|
|
1274
1485
|
let extraData = {};
|
|
1275
1486
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1276
1487
|
try {
|
|
1277
|
-
const jwtToken = req.query.jwtToken;
|
|
1488
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1278
1489
|
if (jwtToken) {
|
|
1279
1490
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1280
1491
|
if (!decodedToken) {
|
|
@@ -1338,7 +1549,7 @@ function createCoreRouter() {
|
|
|
1338
1549
|
let extraData = {};
|
|
1339
1550
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1340
1551
|
try {
|
|
1341
|
-
const jwtToken = req.query.jwtToken;
|
|
1552
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1342
1553
|
if (jwtToken) {
|
|
1343
1554
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1344
1555
|
if (!decodedToken) {
|
|
@@ -1401,7 +1612,7 @@ function createCoreRouter() {
|
|
|
1401
1612
|
let extraData = {};
|
|
1402
1613
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1403
1614
|
try {
|
|
1404
|
-
const jwtToken = req.query.jwtToken;
|
|
1615
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1405
1616
|
if (jwtToken) {
|
|
1406
1617
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1407
1618
|
if (!decodedToken) {
|
|
@@ -1458,9 +1669,15 @@ function createCoreRouter() {
|
|
|
1458
1669
|
let extraData = {};
|
|
1459
1670
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1460
1671
|
try {
|
|
1461
|
-
const jwtToken = req.query.jwtToken;
|
|
1672
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1462
1673
|
if (jwtToken) {
|
|
1463
|
-
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;
|
|
1464
1681
|
platformName = platform;
|
|
1465
1682
|
if (!userId) {
|
|
1466
1683
|
tracer?.trace('upsertCallDisposition:invalidToken', {});
|
|
@@ -1527,7 +1744,7 @@ function createCoreRouter() {
|
|
|
1527
1744
|
let extraData = {};
|
|
1528
1745
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1529
1746
|
try {
|
|
1530
|
-
const jwtToken = req.query.jwtToken;
|
|
1747
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1531
1748
|
if (jwtToken) {
|
|
1532
1749
|
const decodedToken = jwt.decodeJwt(jwtToken);
|
|
1533
1750
|
if (!decodedToken) {
|
|
@@ -1591,7 +1808,7 @@ function createCoreRouter() {
|
|
|
1591
1808
|
let statusCode = 200;
|
|
1592
1809
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1593
1810
|
try {
|
|
1594
|
-
const jwtToken = req.query.jwtToken;
|
|
1811
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1595
1812
|
if (!jwtToken) {
|
|
1596
1813
|
tracer?.trace('scheduleCallDown:noToken', {});
|
|
1597
1814
|
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
@@ -1639,7 +1856,7 @@ function createCoreRouter() {
|
|
|
1639
1856
|
let statusCode = 200;
|
|
1640
1857
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1641
1858
|
try {
|
|
1642
|
-
const jwtToken = req.query.jwtToken;
|
|
1859
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1643
1860
|
if (!jwtToken) {
|
|
1644
1861
|
tracer?.trace('getCallDownList:noToken', {});
|
|
1645
1862
|
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
@@ -1680,7 +1897,7 @@ function createCoreRouter() {
|
|
|
1680
1897
|
let statusCode = 200;
|
|
1681
1898
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1682
1899
|
try {
|
|
1683
|
-
const jwtToken = req.query.jwtToken;
|
|
1900
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1684
1901
|
const id = req.query.id;
|
|
1685
1902
|
if (!jwtToken) {
|
|
1686
1903
|
tracer?.trace('deleteCallDownItem:noToken', {});
|
|
@@ -1728,7 +1945,7 @@ function createCoreRouter() {
|
|
|
1728
1945
|
let statusCode = 200;
|
|
1729
1946
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1730
1947
|
try {
|
|
1731
|
-
const jwtToken = req.query.jwtToken;
|
|
1948
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1732
1949
|
if (!jwtToken) {
|
|
1733
1950
|
tracer?.trace('markCallDownCalled:noToken', {});
|
|
1734
1951
|
res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
|
|
@@ -1775,9 +1992,16 @@ function createCoreRouter() {
|
|
|
1775
1992
|
let statusCode = 200;
|
|
1776
1993
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1777
1994
|
try {
|
|
1778
|
-
const jwtToken = req.query.jwtToken;
|
|
1995
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1779
1996
|
if (jwtToken) {
|
|
1780
|
-
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;
|
|
1781
2005
|
platformName = platform;
|
|
1782
2006
|
const { successful, returnMessage, contact, isRevokeUserSession } = await contactCore.findContactWithName({ platform, userId, name: req.query.name });
|
|
1783
2007
|
if (isRevokeUserSession) {
|
|
@@ -1827,7 +2051,7 @@ function createCoreRouter() {
|
|
|
1827
2051
|
let platformName = null;
|
|
1828
2052
|
let success = false;
|
|
1829
2053
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1830
|
-
const jwtToken = req.query.jwtToken;
|
|
2054
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1831
2055
|
try {
|
|
1832
2056
|
if (jwtToken) {
|
|
1833
2057
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
@@ -1873,7 +2097,7 @@ function createCoreRouter() {
|
|
|
1873
2097
|
let platformName = null;
|
|
1874
2098
|
let success = false;
|
|
1875
2099
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1876
|
-
const jwtToken = req.query.jwtToken;
|
|
2100
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1877
2101
|
try {
|
|
1878
2102
|
if (jwtToken) {
|
|
1879
2103
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
@@ -1914,7 +2138,7 @@ function createCoreRouter() {
|
|
|
1914
2138
|
router.get('/ringcentral/oauth/callback', async function (req, res) {
|
|
1915
2139
|
const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
|
|
1916
2140
|
tracer?.trace('onRingcentralOAuthCallback:start', { query: req.query });
|
|
1917
|
-
const jwtToken = req.query.jwtToken;
|
|
2141
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1918
2142
|
if (jwtToken) {
|
|
1919
2143
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
1920
2144
|
const { code } = req.query;
|
|
@@ -1938,7 +2162,7 @@ function createCoreRouter() {
|
|
|
1938
2162
|
let platformName = null;
|
|
1939
2163
|
let success = false;
|
|
1940
2164
|
const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
|
|
1941
|
-
const jwtToken = req.query.jwtToken;
|
|
2165
|
+
const jwtToken = req.jwtToken || req.query.jwtToken;
|
|
1942
2166
|
if (jwtToken) {
|
|
1943
2167
|
const unAuthData = jwt.decodeJwt(jwtToken);
|
|
1944
2168
|
const uploadUrl = await s3ErrorLogReport.getUploadUrl({ userId: unAuthData?.id, platform: unAuthData?.platform });
|
|
@@ -2219,7 +2443,8 @@ function createCoreMiddleware() {
|
|
|
2219
2443
|
}
|
|
2220
2444
|
}),
|
|
2221
2445
|
cors({
|
|
2222
|
-
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE']
|
|
2446
|
+
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'],
|
|
2447
|
+
exposedHeaders: ['x-refreshed-jwt-token']
|
|
2223
2448
|
})
|
|
2224
2449
|
];
|
|
2225
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;
|