@openfin/cloud-interop-core-api 0.0.1-alpha.f5abe75 → 0.0.1-alpha.f5be6bc
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 +15 -14
- package/index.cjs +109 -14
- package/index.mjs +109 -14
- package/package.json +5 -8
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
|
@@ -51,7 +51,7 @@ declare const appIntentSchema: z.ZodObject<{
|
|
|
51
51
|
name: string;
|
|
52
52
|
displayName: string;
|
|
53
53
|
}>;
|
|
54
|
-
apps: z.ZodArray<z.ZodObject<
|
|
54
|
+
apps: z.ZodArray<z.ZodObject<{
|
|
55
55
|
description: z.ZodOptional<z.ZodString>;
|
|
56
56
|
icons: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
57
57
|
size: z.ZodOptional<z.ZodString>;
|
|
@@ -88,10 +88,10 @@ declare const appIntentSchema: z.ZodObject<{
|
|
|
88
88
|
title: z.ZodOptional<z.ZodString>;
|
|
89
89
|
tooltip: z.ZodOptional<z.ZodString>;
|
|
90
90
|
version: z.ZodOptional<z.ZodString>;
|
|
91
|
-
}
|
|
91
|
+
} & {
|
|
92
92
|
appId: z.ZodString;
|
|
93
93
|
instanceId: z.ZodOptional<z.ZodString>;
|
|
94
|
-
}
|
|
94
|
+
}, "strip", z.ZodTypeAny, {
|
|
95
95
|
appId: string;
|
|
96
96
|
instanceId?: string | undefined;
|
|
97
97
|
description?: string | undefined;
|
|
@@ -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
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var buffer = require('buffer');
|
|
4
|
+
require('fs');
|
|
5
|
+
require('path');
|
|
4
6
|
var mqtt = require('mqtt');
|
|
5
7
|
|
|
8
|
+
var u=n=>{let e=n.replaceAll("-","+").replaceAll("_","/");return e.padEnd(e.length+(4-e.length%4)%4,"=")};
|
|
9
|
+
|
|
6
10
|
class CloudInteropAPIError extends Error {
|
|
7
11
|
code;
|
|
8
12
|
constructor(message = 'An unexpected error has occurred', code = 'UNEXPECTED_ERROR', cause) {
|
|
@@ -353,6 +357,7 @@ class CloudInteropAPI {
|
|
|
353
357
|
#attemptingToReconnect = false;
|
|
354
358
|
#events = new EventController();
|
|
355
359
|
#intents;
|
|
360
|
+
#sessionTimer;
|
|
356
361
|
constructor(cloudInteropSettings) {
|
|
357
362
|
this.#cloudInteropSettings = cloudInteropSettings;
|
|
358
363
|
}
|
|
@@ -365,11 +370,11 @@ class CloudInteropAPI {
|
|
|
365
370
|
/**
|
|
366
371
|
* Connects and creates a session on the Cloud Interop service
|
|
367
372
|
*
|
|
368
|
-
* @param
|
|
369
|
-
* @returns
|
|
373
|
+
* @param parameters - The parameters to use to connect
|
|
374
|
+
* @returns Promise that resolves when connection is established
|
|
370
375
|
* @memberof CloudInteropAPI
|
|
371
|
-
* @throws
|
|
372
|
-
* @throws
|
|
376
|
+
* @throws CloudInteropAPIError - If an error occurs during connection
|
|
377
|
+
* @throws AuthorizationError - If the connection is unauthorized
|
|
373
378
|
*/
|
|
374
379
|
async connect(parameters) {
|
|
375
380
|
this.#validateConnectParams(parameters);
|
|
@@ -393,6 +398,11 @@ class CloudInteropAPI {
|
|
|
393
398
|
throw new CloudInteropAPIError(`Failed to connect to the Cloud Interop service: ${this.#cloudInteropSettings.url}`, 'ERR_CONNECT', new Error(createSessionResponse.statusText));
|
|
394
399
|
}
|
|
395
400
|
this.#sessionDetails = (await createSessionResponse.json());
|
|
401
|
+
// If local session expiry handling is enabled, start the session timer
|
|
402
|
+
if (this.#sessionDetails.localSessionExpiryHandling) {
|
|
403
|
+
this.#logger('debug', `Local session expiry handling is enabled`);
|
|
404
|
+
this.#startSessionTimer();
|
|
405
|
+
}
|
|
396
406
|
const sessionRootTopic = this.#sessionDetails.sessionRootTopic;
|
|
397
407
|
const clientOptions = {
|
|
398
408
|
keepalive: this.#keepAliveIntervalSeconds,
|
|
@@ -422,9 +432,7 @@ class CloudInteropAPI {
|
|
|
422
432
|
if (error instanceof mqtt.ErrorWithReasonCode) {
|
|
423
433
|
switch (error.code) {
|
|
424
434
|
case BadUserNamePasswordError: {
|
|
425
|
-
|
|
426
|
-
this.#logger('warn', `Session expired`);
|
|
427
|
-
this.#events.emitEvent('session-expired');
|
|
435
|
+
this.#handleSessionExpiry();
|
|
428
436
|
return;
|
|
429
437
|
}
|
|
430
438
|
default: {
|
|
@@ -478,9 +486,9 @@ class CloudInteropAPI {
|
|
|
478
486
|
/**
|
|
479
487
|
* Disconnects from the Cloud Interop service
|
|
480
488
|
*
|
|
481
|
-
* @returns
|
|
489
|
+
* @returns Promise that resolves when disconnected
|
|
482
490
|
* @memberof CloudInteropAPI
|
|
483
|
-
* @throws
|
|
491
|
+
* @throws CloudInteropAPIError - If an error occurs during disconnection
|
|
484
492
|
*/
|
|
485
493
|
async disconnect() {
|
|
486
494
|
await this.#disconnect(true);
|
|
@@ -488,9 +496,9 @@ class CloudInteropAPI {
|
|
|
488
496
|
/**
|
|
489
497
|
* Publishes a new context for the given context group to the other connected sessions
|
|
490
498
|
*
|
|
491
|
-
* @param
|
|
492
|
-
* @param
|
|
493
|
-
* @returns
|
|
499
|
+
* @param contextGroup - The context group to publish to
|
|
500
|
+
* @param context - The context to publish
|
|
501
|
+
* @returns Promise that resolves when context is published
|
|
494
502
|
* @memberof CloudInteropAPI
|
|
495
503
|
*/
|
|
496
504
|
async setContext(contextGroup, context) {
|
|
@@ -517,9 +525,9 @@ class CloudInteropAPI {
|
|
|
517
525
|
/**
|
|
518
526
|
* Starts an intent discovery operation
|
|
519
527
|
*
|
|
520
|
-
* @returns
|
|
528
|
+
* @returns Promise that resolves when intent discovery is started
|
|
521
529
|
* @memberof CloudInteropAPI
|
|
522
|
-
* @throws
|
|
530
|
+
* @throws CloudInteropAPIError - If an error occurs during intent discovery
|
|
523
531
|
*/
|
|
524
532
|
async startIntentDiscovery(options) {
|
|
525
533
|
this.#throwIfNotConnected();
|
|
@@ -560,6 +568,11 @@ class CloudInteropAPI {
|
|
|
560
568
|
if (!this.#connectionParams) {
|
|
561
569
|
throw new Error('Connect parameters must be provided');
|
|
562
570
|
}
|
|
571
|
+
// Cancel session timer if it's running
|
|
572
|
+
if (this.#sessionTimer) {
|
|
573
|
+
clearTimeout(this.#sessionTimer);
|
|
574
|
+
this.#sessionTimer = undefined;
|
|
575
|
+
}
|
|
563
576
|
const disconnectResponse = await fetch(`${this.#cloudInteropSettings.url}/api/sessions/${this.#sessionDetails.sessionId}`, {
|
|
564
577
|
method: 'DELETE',
|
|
565
578
|
headers: getRequestHeaders(this.#connectionParams),
|
|
@@ -658,6 +671,88 @@ class CloudInteropAPI {
|
|
|
658
671
|
throw new Error('MQTT client not connected');
|
|
659
672
|
}
|
|
660
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* Extracts the expiration timestamp from a JWT token.
|
|
676
|
+
*
|
|
677
|
+
* @param token - The JWT token string
|
|
678
|
+
* @returns The expiration timestamp in seconds, or null if extraction fails
|
|
679
|
+
*/
|
|
680
|
+
#extractExpirationFromJwt(token) {
|
|
681
|
+
try {
|
|
682
|
+
// JWT tokens have three parts separated by dots: header.payload.signature
|
|
683
|
+
// The exp claim is in the payload
|
|
684
|
+
const parts = token.split('.');
|
|
685
|
+
if (parts.length < 2) {
|
|
686
|
+
this.#logger('warn', 'Invalid JWT token format: expected at least 2 parts');
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
const payload = parts[1];
|
|
690
|
+
// Decode base64url encoded payload
|
|
691
|
+
const decodedBytes = buffer.Buffer.from(u(payload), 'base64');
|
|
692
|
+
const payloadJson = decodedBytes.toString('utf8');
|
|
693
|
+
// Parse JSON to get the exp claim
|
|
694
|
+
const claims = JSON.parse(payloadJson);
|
|
695
|
+
const exp = claims.exp;
|
|
696
|
+
if (exp === undefined || exp === null) {
|
|
697
|
+
this.#logger('warn', "JWT token does not contain 'exp' claim");
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
if (typeof exp !== 'number') {
|
|
701
|
+
this.#logger('warn', `JWT token 'exp' claim is not a number: ${exp}`);
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
return exp;
|
|
705
|
+
}
|
|
706
|
+
catch (error) {
|
|
707
|
+
this.#logger('error', `Failed to extract expiration from JWT token: ${error instanceof Error ? error.message : error}`);
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Start a session timer that will expire at the time specified in the JWT token's exp claim.
|
|
713
|
+
* When the timer fires, it executes the same actions as the BadUserNamePasswordError case.
|
|
714
|
+
*/
|
|
715
|
+
#startSessionTimer() {
|
|
716
|
+
if (!this.#sessionDetails?.localSessionExpiryHandling) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const token = this.#sessionDetails.token;
|
|
720
|
+
if (!token) {
|
|
721
|
+
this.#logger('warn', 'Cannot start session timer: token not available');
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
// Extract expiration time from JWT token
|
|
725
|
+
const expTimestamp = this.#extractExpirationFromJwt(token);
|
|
726
|
+
if (expTimestamp === null) {
|
|
727
|
+
this.#logger('warn', 'Cannot start session timer: could not extract expiration from JWT token');
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const currentTimeSeconds = Math.floor(Date.now() / 1000);
|
|
731
|
+
const delaySeconds = expTimestamp - currentTimeSeconds;
|
|
732
|
+
if (delaySeconds <= 0) {
|
|
733
|
+
this.#logger('warn', 'JWT token has already expired or expires immediately');
|
|
734
|
+
// Execute the same actions as BadUserNamePasswordError case
|
|
735
|
+
this.#handleSessionExpiry();
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
// Clear any existing timer
|
|
739
|
+
if (this.#sessionTimer) {
|
|
740
|
+
clearTimeout(this.#sessionTimer);
|
|
741
|
+
}
|
|
742
|
+
const expirationTimeString = new Date(expTimestamp * 1000).toISOString();
|
|
743
|
+
this.#logger('debug', `Starting session timer to expire in ${delaySeconds} seconds (at ${expirationTimeString})`);
|
|
744
|
+
this.#sessionTimer = setTimeout(async () => {
|
|
745
|
+
this.#handleSessionExpiry();
|
|
746
|
+
}, delaySeconds * 1000);
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Handles session expiry by executing the same actions as the BadUserNamePasswordError case.
|
|
750
|
+
*/
|
|
751
|
+
async #handleSessionExpiry() {
|
|
752
|
+
await this.#disconnect(false);
|
|
753
|
+
this.#logger('warn', 'Session expired');
|
|
754
|
+
this.#events.emitEvent('session-expired');
|
|
755
|
+
}
|
|
661
756
|
}
|
|
662
757
|
|
|
663
758
|
exports.AuthorizationError = AuthorizationError;
|
package/index.mjs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
|
+
import 'fs';
|
|
3
|
+
import 'path';
|
|
2
4
|
import mqtt from 'mqtt';
|
|
3
5
|
|
|
6
|
+
var u=n=>{let e=n.replaceAll("-","+").replaceAll("_","/");return e.padEnd(e.length+(4-e.length%4)%4,"=")};
|
|
7
|
+
|
|
4
8
|
class CloudInteropAPIError extends Error {
|
|
5
9
|
code;
|
|
6
10
|
constructor(message = 'An unexpected error has occurred', code = 'UNEXPECTED_ERROR', cause) {
|
|
@@ -351,6 +355,7 @@ class CloudInteropAPI {
|
|
|
351
355
|
#attemptingToReconnect = false;
|
|
352
356
|
#events = new EventController();
|
|
353
357
|
#intents;
|
|
358
|
+
#sessionTimer;
|
|
354
359
|
constructor(cloudInteropSettings) {
|
|
355
360
|
this.#cloudInteropSettings = cloudInteropSettings;
|
|
356
361
|
}
|
|
@@ -363,11 +368,11 @@ class CloudInteropAPI {
|
|
|
363
368
|
/**
|
|
364
369
|
* Connects and creates a session on the Cloud Interop service
|
|
365
370
|
*
|
|
366
|
-
* @param
|
|
367
|
-
* @returns
|
|
371
|
+
* @param parameters - The parameters to use to connect
|
|
372
|
+
* @returns Promise that resolves when connection is established
|
|
368
373
|
* @memberof CloudInteropAPI
|
|
369
|
-
* @throws
|
|
370
|
-
* @throws
|
|
374
|
+
* @throws CloudInteropAPIError - If an error occurs during connection
|
|
375
|
+
* @throws AuthorizationError - If the connection is unauthorized
|
|
371
376
|
*/
|
|
372
377
|
async connect(parameters) {
|
|
373
378
|
this.#validateConnectParams(parameters);
|
|
@@ -391,6 +396,11 @@ class CloudInteropAPI {
|
|
|
391
396
|
throw new CloudInteropAPIError(`Failed to connect to the Cloud Interop service: ${this.#cloudInteropSettings.url}`, 'ERR_CONNECT', new Error(createSessionResponse.statusText));
|
|
392
397
|
}
|
|
393
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
|
+
}
|
|
394
404
|
const sessionRootTopic = this.#sessionDetails.sessionRootTopic;
|
|
395
405
|
const clientOptions = {
|
|
396
406
|
keepalive: this.#keepAliveIntervalSeconds,
|
|
@@ -420,9 +430,7 @@ class CloudInteropAPI {
|
|
|
420
430
|
if (error instanceof mqtt.ErrorWithReasonCode) {
|
|
421
431
|
switch (error.code) {
|
|
422
432
|
case BadUserNamePasswordError: {
|
|
423
|
-
|
|
424
|
-
this.#logger('warn', `Session expired`);
|
|
425
|
-
this.#events.emitEvent('session-expired');
|
|
433
|
+
this.#handleSessionExpiry();
|
|
426
434
|
return;
|
|
427
435
|
}
|
|
428
436
|
default: {
|
|
@@ -476,9 +484,9 @@ class CloudInteropAPI {
|
|
|
476
484
|
/**
|
|
477
485
|
* Disconnects from the Cloud Interop service
|
|
478
486
|
*
|
|
479
|
-
* @returns
|
|
487
|
+
* @returns Promise that resolves when disconnected
|
|
480
488
|
* @memberof CloudInteropAPI
|
|
481
|
-
* @throws
|
|
489
|
+
* @throws CloudInteropAPIError - If an error occurs during disconnection
|
|
482
490
|
*/
|
|
483
491
|
async disconnect() {
|
|
484
492
|
await this.#disconnect(true);
|
|
@@ -486,9 +494,9 @@ class CloudInteropAPI {
|
|
|
486
494
|
/**
|
|
487
495
|
* Publishes a new context for the given context group to the other connected sessions
|
|
488
496
|
*
|
|
489
|
-
* @param
|
|
490
|
-
* @param
|
|
491
|
-
* @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
|
|
492
500
|
* @memberof CloudInteropAPI
|
|
493
501
|
*/
|
|
494
502
|
async setContext(contextGroup, context) {
|
|
@@ -515,9 +523,9 @@ class CloudInteropAPI {
|
|
|
515
523
|
/**
|
|
516
524
|
* Starts an intent discovery operation
|
|
517
525
|
*
|
|
518
|
-
* @returns
|
|
526
|
+
* @returns Promise that resolves when intent discovery is started
|
|
519
527
|
* @memberof CloudInteropAPI
|
|
520
|
-
* @throws
|
|
528
|
+
* @throws CloudInteropAPIError - If an error occurs during intent discovery
|
|
521
529
|
*/
|
|
522
530
|
async startIntentDiscovery(options) {
|
|
523
531
|
this.#throwIfNotConnected();
|
|
@@ -558,6 +566,11 @@ class CloudInteropAPI {
|
|
|
558
566
|
if (!this.#connectionParams) {
|
|
559
567
|
throw new Error('Connect parameters must be provided');
|
|
560
568
|
}
|
|
569
|
+
// Cancel session timer if it's running
|
|
570
|
+
if (this.#sessionTimer) {
|
|
571
|
+
clearTimeout(this.#sessionTimer);
|
|
572
|
+
this.#sessionTimer = undefined;
|
|
573
|
+
}
|
|
561
574
|
const disconnectResponse = await fetch(`${this.#cloudInteropSettings.url}/api/sessions/${this.#sessionDetails.sessionId}`, {
|
|
562
575
|
method: 'DELETE',
|
|
563
576
|
headers: getRequestHeaders(this.#connectionParams),
|
|
@@ -656,6 +669,88 @@ class CloudInteropAPI {
|
|
|
656
669
|
throw new Error('MQTT client not connected');
|
|
657
670
|
}
|
|
658
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.from(u(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
|
+
}
|
|
659
754
|
}
|
|
660
755
|
|
|
661
756
|
export { AuthorizationError, CloudInteropAPI, CloudInteropAPIError };
|
package/package.json
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfin/cloud-interop-core-api",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "0.0.1-alpha.f5be6bc",
|
|
5
4
|
"description": "",
|
|
5
|
+
"type": "module",
|
|
6
6
|
"main": "./index.cjs",
|
|
7
7
|
"browser": "./index.mjs",
|
|
8
8
|
"types": "./bundle.d.ts",
|
|
9
|
-
"author": "",
|
|
9
|
+
"author": "support@here.io",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
11
|
-
"optionalDependencies": {
|
|
12
|
-
"@rollup/rollup-linux-x64-gnu": "*"
|
|
13
|
-
},
|
|
14
11
|
"dependencies": {
|
|
15
|
-
"mqtt": "^5.
|
|
16
|
-
"zod": "
|
|
12
|
+
"mqtt": "^5.15.1",
|
|
13
|
+
"zod": "catalog:"
|
|
17
14
|
}
|
|
18
15
|
}
|