@openfin/cloud-interop-core-api 0.0.1-alpha.deb2382 → 0.0.1-alpha.dee71da
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 +2 -2
- package/bundle.d.ts +12 -11
- package/index.cjs +107 -14
- package/index.mjs +107 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# @openfin/cloud-interop-core-api
|
|
2
2
|
|
|
3
|
-
This package contains the core interop library that handles all interactions with the
|
|
3
|
+
This package contains the core interop library that handles all interactions with the HERE Cloud Interop Service.
|
|
4
4
|
|
|
5
5
|
It is callable via browser or node applications.
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
## Authentication
|
|
9
9
|
|
|
10
|
-
The library supports authentication with the
|
|
10
|
+
The library supports authentication with the HERE Cloud Interop Service using the following methods:
|
|
11
11
|
- Basic Authentication
|
|
12
12
|
- JWT Token Authentication
|
|
13
13
|
- Default Authentication i.e. Interactive session based authentication using cookies
|
package/bundle.d.ts
CHANGED
|
@@ -245,36 +245,36 @@ export declare class CloudInteropAPI {
|
|
|
245
245
|
/**
|
|
246
246
|
* Connects and creates a session on the Cloud Interop service
|
|
247
247
|
*
|
|
248
|
-
* @param
|
|
249
|
-
* @returns
|
|
248
|
+
* @param parameters - The parameters to use to connect
|
|
249
|
+
* @returns Promise that resolves when connection is established
|
|
250
250
|
* @memberof CloudInteropAPI
|
|
251
|
-
* @throws
|
|
252
|
-
* @throws
|
|
251
|
+
* @throws CloudInteropAPIError - If an error occurs during connection
|
|
252
|
+
* @throws AuthorizationError - If the connection is unauthorized
|
|
253
253
|
*/
|
|
254
254
|
connect(parameters: ConnectParameters): Promise<void>;
|
|
255
255
|
/**
|
|
256
256
|
* Disconnects from the Cloud Interop service
|
|
257
257
|
*
|
|
258
|
-
* @returns
|
|
258
|
+
* @returns Promise that resolves when disconnected
|
|
259
259
|
* @memberof CloudInteropAPI
|
|
260
|
-
* @throws
|
|
260
|
+
* @throws CloudInteropAPIError - If an error occurs during disconnection
|
|
261
261
|
*/
|
|
262
262
|
disconnect(): Promise<void>;
|
|
263
263
|
/**
|
|
264
264
|
* Publishes a new context for the given context group to the other connected sessions
|
|
265
265
|
*
|
|
266
|
-
* @param
|
|
267
|
-
* @param
|
|
268
|
-
* @returns
|
|
266
|
+
* @param contextGroup - The context group to publish to
|
|
267
|
+
* @param context - The context to publish
|
|
268
|
+
* @returns Promise that resolves when context is published
|
|
269
269
|
* @memberof CloudInteropAPI
|
|
270
270
|
*/
|
|
271
271
|
setContext(contextGroup: string, context: InferredContext): Promise<void>;
|
|
272
272
|
/**
|
|
273
273
|
* Starts an intent discovery operation
|
|
274
274
|
*
|
|
275
|
-
* @returns
|
|
275
|
+
* @returns Promise that resolves when intent discovery is started
|
|
276
276
|
* @memberof CloudInteropAPI
|
|
277
|
-
* @throws
|
|
277
|
+
* @throws CloudInteropAPIError - If an error occurs during intent discovery
|
|
278
278
|
*/
|
|
279
279
|
startIntentDiscovery(options: StartIntentDiscoveryOptions): Promise<void>;
|
|
280
280
|
raiseIntent(options: RaiseIntentAPIOptions): Promise<void>;
|
|
@@ -418,6 +418,7 @@ export declare type CreateSessionResponse = {
|
|
|
418
418
|
sub: string;
|
|
419
419
|
platformId: string;
|
|
420
420
|
sourceId: string;
|
|
421
|
+
localSessionExpiryHandling?: boolean;
|
|
421
422
|
};
|
|
422
423
|
|
|
423
424
|
declare const errorSchema: z.ZodObject<{
|
package/index.cjs
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
var buffer = require('buffer');
|
|
4
4
|
var mqtt = require('mqtt');
|
|
5
5
|
|
|
6
|
+
var l=t=>{let e=t.replaceAll("-","+").replaceAll("_","/");return e.padEnd(e.length+(4-e.length%4)%4,"=")};
|
|
7
|
+
|
|
6
8
|
class CloudInteropAPIError extends Error {
|
|
7
9
|
code;
|
|
8
10
|
constructor(message = 'An unexpected error has occurred', code = 'UNEXPECTED_ERROR', cause) {
|
|
@@ -353,6 +355,7 @@ class CloudInteropAPI {
|
|
|
353
355
|
#attemptingToReconnect = false;
|
|
354
356
|
#events = new EventController();
|
|
355
357
|
#intents;
|
|
358
|
+
#sessionTimer;
|
|
356
359
|
constructor(cloudInteropSettings) {
|
|
357
360
|
this.#cloudInteropSettings = cloudInteropSettings;
|
|
358
361
|
}
|
|
@@ -365,11 +368,11 @@ class CloudInteropAPI {
|
|
|
365
368
|
/**
|
|
366
369
|
* Connects and creates a session on the Cloud Interop service
|
|
367
370
|
*
|
|
368
|
-
* @param
|
|
369
|
-
* @returns
|
|
371
|
+
* @param parameters - The parameters to use to connect
|
|
372
|
+
* @returns Promise that resolves when connection is established
|
|
370
373
|
* @memberof CloudInteropAPI
|
|
371
|
-
* @throws
|
|
372
|
-
* @throws
|
|
374
|
+
* @throws CloudInteropAPIError - If an error occurs during connection
|
|
375
|
+
* @throws AuthorizationError - If the connection is unauthorized
|
|
373
376
|
*/
|
|
374
377
|
async connect(parameters) {
|
|
375
378
|
this.#validateConnectParams(parameters);
|
|
@@ -393,6 +396,11 @@ class CloudInteropAPI {
|
|
|
393
396
|
throw new CloudInteropAPIError(`Failed to connect to the Cloud Interop service: ${this.#cloudInteropSettings.url}`, 'ERR_CONNECT', new Error(createSessionResponse.statusText));
|
|
394
397
|
}
|
|
395
398
|
this.#sessionDetails = (await createSessionResponse.json());
|
|
399
|
+
// If local session expiry handling is enabled, start the session timer
|
|
400
|
+
if (this.#sessionDetails.localSessionExpiryHandling) {
|
|
401
|
+
this.#logger('debug', `Local session expiry handling is enabled`);
|
|
402
|
+
this.#startSessionTimer();
|
|
403
|
+
}
|
|
396
404
|
const sessionRootTopic = this.#sessionDetails.sessionRootTopic;
|
|
397
405
|
const clientOptions = {
|
|
398
406
|
keepalive: this.#keepAliveIntervalSeconds,
|
|
@@ -422,9 +430,7 @@ class CloudInteropAPI {
|
|
|
422
430
|
if (error instanceof mqtt.ErrorWithReasonCode) {
|
|
423
431
|
switch (error.code) {
|
|
424
432
|
case BadUserNamePasswordError: {
|
|
425
|
-
|
|
426
|
-
this.#logger('warn', `Session expired`);
|
|
427
|
-
this.#events.emitEvent('session-expired');
|
|
433
|
+
this.#handleSessionExpiry();
|
|
428
434
|
return;
|
|
429
435
|
}
|
|
430
436
|
default: {
|
|
@@ -478,9 +484,9 @@ class CloudInteropAPI {
|
|
|
478
484
|
/**
|
|
479
485
|
* Disconnects from the Cloud Interop service
|
|
480
486
|
*
|
|
481
|
-
* @returns
|
|
487
|
+
* @returns Promise that resolves when disconnected
|
|
482
488
|
* @memberof CloudInteropAPI
|
|
483
|
-
* @throws
|
|
489
|
+
* @throws CloudInteropAPIError - If an error occurs during disconnection
|
|
484
490
|
*/
|
|
485
491
|
async disconnect() {
|
|
486
492
|
await this.#disconnect(true);
|
|
@@ -488,9 +494,9 @@ class CloudInteropAPI {
|
|
|
488
494
|
/**
|
|
489
495
|
* Publishes a new context for the given context group to the other connected sessions
|
|
490
496
|
*
|
|
491
|
-
* @param
|
|
492
|
-
* @param
|
|
493
|
-
* @returns
|
|
497
|
+
* @param contextGroup - The context group to publish to
|
|
498
|
+
* @param context - The context to publish
|
|
499
|
+
* @returns Promise that resolves when context is published
|
|
494
500
|
* @memberof CloudInteropAPI
|
|
495
501
|
*/
|
|
496
502
|
async setContext(contextGroup, context) {
|
|
@@ -517,9 +523,9 @@ class CloudInteropAPI {
|
|
|
517
523
|
/**
|
|
518
524
|
* Starts an intent discovery operation
|
|
519
525
|
*
|
|
520
|
-
* @returns
|
|
526
|
+
* @returns Promise that resolves when intent discovery is started
|
|
521
527
|
* @memberof CloudInteropAPI
|
|
522
|
-
* @throws
|
|
528
|
+
* @throws CloudInteropAPIError - If an error occurs during intent discovery
|
|
523
529
|
*/
|
|
524
530
|
async startIntentDiscovery(options) {
|
|
525
531
|
this.#throwIfNotConnected();
|
|
@@ -560,6 +566,11 @@ class CloudInteropAPI {
|
|
|
560
566
|
if (!this.#connectionParams) {
|
|
561
567
|
throw new Error('Connect parameters must be provided');
|
|
562
568
|
}
|
|
569
|
+
// Cancel session timer if it's running
|
|
570
|
+
if (this.#sessionTimer) {
|
|
571
|
+
clearTimeout(this.#sessionTimer);
|
|
572
|
+
this.#sessionTimer = undefined;
|
|
573
|
+
}
|
|
563
574
|
const disconnectResponse = await fetch(`${this.#cloudInteropSettings.url}/api/sessions/${this.#sessionDetails.sessionId}`, {
|
|
564
575
|
method: 'DELETE',
|
|
565
576
|
headers: getRequestHeaders(this.#connectionParams),
|
|
@@ -658,6 +669,88 @@ class CloudInteropAPI {
|
|
|
658
669
|
throw new Error('MQTT client not connected');
|
|
659
670
|
}
|
|
660
671
|
}
|
|
672
|
+
/**
|
|
673
|
+
* Extracts the expiration timestamp from a JWT token.
|
|
674
|
+
*
|
|
675
|
+
* @param token - The JWT token string
|
|
676
|
+
* @returns The expiration timestamp in seconds, or null if extraction fails
|
|
677
|
+
*/
|
|
678
|
+
#extractExpirationFromJwt(token) {
|
|
679
|
+
try {
|
|
680
|
+
// JWT tokens have three parts separated by dots: header.payload.signature
|
|
681
|
+
// The exp claim is in the payload
|
|
682
|
+
const parts = token.split('.');
|
|
683
|
+
if (parts.length < 2) {
|
|
684
|
+
this.#logger('warn', 'Invalid JWT token format: expected at least 2 parts');
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
const payload = parts[1];
|
|
688
|
+
// Decode base64url encoded payload
|
|
689
|
+
const decodedBytes = buffer.Buffer.from(l(payload), 'base64');
|
|
690
|
+
const payloadJson = decodedBytes.toString('utf8');
|
|
691
|
+
// Parse JSON to get the exp claim
|
|
692
|
+
const claims = JSON.parse(payloadJson);
|
|
693
|
+
const exp = claims.exp;
|
|
694
|
+
if (exp === undefined || exp === null) {
|
|
695
|
+
this.#logger('warn', "JWT token does not contain 'exp' claim");
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
if (typeof exp !== 'number') {
|
|
699
|
+
this.#logger('warn', `JWT token 'exp' claim is not a number: ${exp}`);
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
return exp;
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
this.#logger('error', `Failed to extract expiration from JWT token: ${error instanceof Error ? error.message : error}`);
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Start a session timer that will expire at the time specified in the JWT token's exp claim.
|
|
711
|
+
* When the timer fires, it executes the same actions as the BadUserNamePasswordError case.
|
|
712
|
+
*/
|
|
713
|
+
#startSessionTimer() {
|
|
714
|
+
if (!this.#sessionDetails?.localSessionExpiryHandling) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const token = this.#sessionDetails.token;
|
|
718
|
+
if (!token) {
|
|
719
|
+
this.#logger('warn', 'Cannot start session timer: token not available');
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
// Extract expiration time from JWT token
|
|
723
|
+
const expTimestamp = this.#extractExpirationFromJwt(token);
|
|
724
|
+
if (expTimestamp === null) {
|
|
725
|
+
this.#logger('warn', 'Cannot start session timer: could not extract expiration from JWT token');
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const currentTimeSeconds = Math.floor(Date.now() / 1000);
|
|
729
|
+
const delaySeconds = expTimestamp - currentTimeSeconds;
|
|
730
|
+
if (delaySeconds <= 0) {
|
|
731
|
+
this.#logger('warn', 'JWT token has already expired or expires immediately');
|
|
732
|
+
// Execute the same actions as BadUserNamePasswordError case
|
|
733
|
+
this.#handleSessionExpiry();
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
// Clear any existing timer
|
|
737
|
+
if (this.#sessionTimer) {
|
|
738
|
+
clearTimeout(this.#sessionTimer);
|
|
739
|
+
}
|
|
740
|
+
const expirationTimeString = new Date(expTimestamp * 1000).toISOString();
|
|
741
|
+
this.#logger('debug', `Starting session timer to expire in ${delaySeconds} seconds (at ${expirationTimeString})`);
|
|
742
|
+
this.#sessionTimer = setTimeout(async () => {
|
|
743
|
+
this.#handleSessionExpiry();
|
|
744
|
+
}, delaySeconds * 1000);
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Handles session expiry by executing the same actions as the BadUserNamePasswordError case.
|
|
748
|
+
*/
|
|
749
|
+
async #handleSessionExpiry() {
|
|
750
|
+
await this.#disconnect(false);
|
|
751
|
+
this.#logger('warn', 'Session expired');
|
|
752
|
+
this.#events.emitEvent('session-expired');
|
|
753
|
+
}
|
|
661
754
|
}
|
|
662
755
|
|
|
663
756
|
exports.AuthorizationError = AuthorizationError;
|
package/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import mqtt from 'mqtt';
|
|
3
3
|
|
|
4
|
+
var l=t=>{let e=t.replaceAll("-","+").replaceAll("_","/");return e.padEnd(e.length+(4-e.length%4)%4,"=")};
|
|
5
|
+
|
|
4
6
|
class CloudInteropAPIError extends Error {
|
|
5
7
|
code;
|
|
6
8
|
constructor(message = 'An unexpected error has occurred', code = 'UNEXPECTED_ERROR', cause) {
|
|
@@ -351,6 +353,7 @@ class CloudInteropAPI {
|
|
|
351
353
|
#attemptingToReconnect = false;
|
|
352
354
|
#events = new EventController();
|
|
353
355
|
#intents;
|
|
356
|
+
#sessionTimer;
|
|
354
357
|
constructor(cloudInteropSettings) {
|
|
355
358
|
this.#cloudInteropSettings = cloudInteropSettings;
|
|
356
359
|
}
|
|
@@ -363,11 +366,11 @@ class CloudInteropAPI {
|
|
|
363
366
|
/**
|
|
364
367
|
* Connects and creates a session on the Cloud Interop service
|
|
365
368
|
*
|
|
366
|
-
* @param
|
|
367
|
-
* @returns
|
|
369
|
+
* @param parameters - The parameters to use to connect
|
|
370
|
+
* @returns Promise that resolves when connection is established
|
|
368
371
|
* @memberof CloudInteropAPI
|
|
369
|
-
* @throws
|
|
370
|
-
* @throws
|
|
372
|
+
* @throws CloudInteropAPIError - If an error occurs during connection
|
|
373
|
+
* @throws AuthorizationError - If the connection is unauthorized
|
|
371
374
|
*/
|
|
372
375
|
async connect(parameters) {
|
|
373
376
|
this.#validateConnectParams(parameters);
|
|
@@ -391,6 +394,11 @@ class CloudInteropAPI {
|
|
|
391
394
|
throw new CloudInteropAPIError(`Failed to connect to the Cloud Interop service: ${this.#cloudInteropSettings.url}`, 'ERR_CONNECT', new Error(createSessionResponse.statusText));
|
|
392
395
|
}
|
|
393
396
|
this.#sessionDetails = (await createSessionResponse.json());
|
|
397
|
+
// If local session expiry handling is enabled, start the session timer
|
|
398
|
+
if (this.#sessionDetails.localSessionExpiryHandling) {
|
|
399
|
+
this.#logger('debug', `Local session expiry handling is enabled`);
|
|
400
|
+
this.#startSessionTimer();
|
|
401
|
+
}
|
|
394
402
|
const sessionRootTopic = this.#sessionDetails.sessionRootTopic;
|
|
395
403
|
const clientOptions = {
|
|
396
404
|
keepalive: this.#keepAliveIntervalSeconds,
|
|
@@ -420,9 +428,7 @@ class CloudInteropAPI {
|
|
|
420
428
|
if (error instanceof mqtt.ErrorWithReasonCode) {
|
|
421
429
|
switch (error.code) {
|
|
422
430
|
case BadUserNamePasswordError: {
|
|
423
|
-
|
|
424
|
-
this.#logger('warn', `Session expired`);
|
|
425
|
-
this.#events.emitEvent('session-expired');
|
|
431
|
+
this.#handleSessionExpiry();
|
|
426
432
|
return;
|
|
427
433
|
}
|
|
428
434
|
default: {
|
|
@@ -476,9 +482,9 @@ class CloudInteropAPI {
|
|
|
476
482
|
/**
|
|
477
483
|
* Disconnects from the Cloud Interop service
|
|
478
484
|
*
|
|
479
|
-
* @returns
|
|
485
|
+
* @returns Promise that resolves when disconnected
|
|
480
486
|
* @memberof CloudInteropAPI
|
|
481
|
-
* @throws
|
|
487
|
+
* @throws CloudInteropAPIError - If an error occurs during disconnection
|
|
482
488
|
*/
|
|
483
489
|
async disconnect() {
|
|
484
490
|
await this.#disconnect(true);
|
|
@@ -486,9 +492,9 @@ class CloudInteropAPI {
|
|
|
486
492
|
/**
|
|
487
493
|
* Publishes a new context for the given context group to the other connected sessions
|
|
488
494
|
*
|
|
489
|
-
* @param
|
|
490
|
-
* @param
|
|
491
|
-
* @returns
|
|
495
|
+
* @param contextGroup - The context group to publish to
|
|
496
|
+
* @param context - The context to publish
|
|
497
|
+
* @returns Promise that resolves when context is published
|
|
492
498
|
* @memberof CloudInteropAPI
|
|
493
499
|
*/
|
|
494
500
|
async setContext(contextGroup, context) {
|
|
@@ -515,9 +521,9 @@ class CloudInteropAPI {
|
|
|
515
521
|
/**
|
|
516
522
|
* Starts an intent discovery operation
|
|
517
523
|
*
|
|
518
|
-
* @returns
|
|
524
|
+
* @returns Promise that resolves when intent discovery is started
|
|
519
525
|
* @memberof CloudInteropAPI
|
|
520
|
-
* @throws
|
|
526
|
+
* @throws CloudInteropAPIError - If an error occurs during intent discovery
|
|
521
527
|
*/
|
|
522
528
|
async startIntentDiscovery(options) {
|
|
523
529
|
this.#throwIfNotConnected();
|
|
@@ -558,6 +564,11 @@ class CloudInteropAPI {
|
|
|
558
564
|
if (!this.#connectionParams) {
|
|
559
565
|
throw new Error('Connect parameters must be provided');
|
|
560
566
|
}
|
|
567
|
+
// Cancel session timer if it's running
|
|
568
|
+
if (this.#sessionTimer) {
|
|
569
|
+
clearTimeout(this.#sessionTimer);
|
|
570
|
+
this.#sessionTimer = undefined;
|
|
571
|
+
}
|
|
561
572
|
const disconnectResponse = await fetch(`${this.#cloudInteropSettings.url}/api/sessions/${this.#sessionDetails.sessionId}`, {
|
|
562
573
|
method: 'DELETE',
|
|
563
574
|
headers: getRequestHeaders(this.#connectionParams),
|
|
@@ -656,6 +667,88 @@ class CloudInteropAPI {
|
|
|
656
667
|
throw new Error('MQTT client not connected');
|
|
657
668
|
}
|
|
658
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* Extracts the expiration timestamp from a JWT token.
|
|
672
|
+
*
|
|
673
|
+
* @param token - The JWT token string
|
|
674
|
+
* @returns The expiration timestamp in seconds, or null if extraction fails
|
|
675
|
+
*/
|
|
676
|
+
#extractExpirationFromJwt(token) {
|
|
677
|
+
try {
|
|
678
|
+
// JWT tokens have three parts separated by dots: header.payload.signature
|
|
679
|
+
// The exp claim is in the payload
|
|
680
|
+
const parts = token.split('.');
|
|
681
|
+
if (parts.length < 2) {
|
|
682
|
+
this.#logger('warn', 'Invalid JWT token format: expected at least 2 parts');
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
const payload = parts[1];
|
|
686
|
+
// Decode base64url encoded payload
|
|
687
|
+
const decodedBytes = Buffer.from(l(payload), 'base64');
|
|
688
|
+
const payloadJson = decodedBytes.toString('utf8');
|
|
689
|
+
// Parse JSON to get the exp claim
|
|
690
|
+
const claims = JSON.parse(payloadJson);
|
|
691
|
+
const exp = claims.exp;
|
|
692
|
+
if (exp === undefined || exp === null) {
|
|
693
|
+
this.#logger('warn', "JWT token does not contain 'exp' claim");
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
if (typeof exp !== 'number') {
|
|
697
|
+
this.#logger('warn', `JWT token 'exp' claim is not a number: ${exp}`);
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
return exp;
|
|
701
|
+
}
|
|
702
|
+
catch (error) {
|
|
703
|
+
this.#logger('error', `Failed to extract expiration from JWT token: ${error instanceof Error ? error.message : error}`);
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Start a session timer that will expire at the time specified in the JWT token's exp claim.
|
|
709
|
+
* When the timer fires, it executes the same actions as the BadUserNamePasswordError case.
|
|
710
|
+
*/
|
|
711
|
+
#startSessionTimer() {
|
|
712
|
+
if (!this.#sessionDetails?.localSessionExpiryHandling) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const token = this.#sessionDetails.token;
|
|
716
|
+
if (!token) {
|
|
717
|
+
this.#logger('warn', 'Cannot start session timer: token not available');
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
// Extract expiration time from JWT token
|
|
721
|
+
const expTimestamp = this.#extractExpirationFromJwt(token);
|
|
722
|
+
if (expTimestamp === null) {
|
|
723
|
+
this.#logger('warn', 'Cannot start session timer: could not extract expiration from JWT token');
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const currentTimeSeconds = Math.floor(Date.now() / 1000);
|
|
727
|
+
const delaySeconds = expTimestamp - currentTimeSeconds;
|
|
728
|
+
if (delaySeconds <= 0) {
|
|
729
|
+
this.#logger('warn', 'JWT token has already expired or expires immediately');
|
|
730
|
+
// Execute the same actions as BadUserNamePasswordError case
|
|
731
|
+
this.#handleSessionExpiry();
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
// Clear any existing timer
|
|
735
|
+
if (this.#sessionTimer) {
|
|
736
|
+
clearTimeout(this.#sessionTimer);
|
|
737
|
+
}
|
|
738
|
+
const expirationTimeString = new Date(expTimestamp * 1000).toISOString();
|
|
739
|
+
this.#logger('debug', `Starting session timer to expire in ${delaySeconds} seconds (at ${expirationTimeString})`);
|
|
740
|
+
this.#sessionTimer = setTimeout(async () => {
|
|
741
|
+
this.#handleSessionExpiry();
|
|
742
|
+
}, delaySeconds * 1000);
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Handles session expiry by executing the same actions as the BadUserNamePasswordError case.
|
|
746
|
+
*/
|
|
747
|
+
async #handleSessionExpiry() {
|
|
748
|
+
await this.#disconnect(false);
|
|
749
|
+
this.#logger('warn', 'Session expired');
|
|
750
|
+
this.#events.emitEvent('session-expired');
|
|
751
|
+
}
|
|
659
752
|
}
|
|
660
753
|
|
|
661
754
|
export { AuthorizationError, CloudInteropAPI, CloudInteropAPIError };
|