@thunderid/node 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ThunderIDNodeClient.d.ts +10 -0
- package/dist/ThunderIDNodeClient.d.ts.map +1 -1
- package/dist/cjs/index.cjs +73 -46
- package/dist/index.js +71 -45
- package/dist/utils/NodeCryptoUtils.d.ts.map +1 -1
- package/dist/utils/SessionUtils.d.ts +2 -1
- package/dist/utils/SessionUtils.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -3,6 +3,7 @@ import { ThunderIDNodeConfig } from './models/config';
|
|
|
3
3
|
declare class ThunderIDNodeClient<T extends ThunderIDNodeConfig = ThunderIDNodeConfig> extends ThunderIDJavaScriptClient<T> {
|
|
4
4
|
private _nodeInstanceId;
|
|
5
5
|
private _handledLogoutTokens;
|
|
6
|
+
private _refreshesInFlight;
|
|
6
7
|
constructor(instanceId?: number);
|
|
7
8
|
initialize(config: T, storage?: Storage): Promise<boolean>;
|
|
8
9
|
getInstanceId(): number;
|
|
@@ -45,8 +46,17 @@ declare class ThunderIDNodeClient<T extends ThunderIDNodeConfig = ThunderIDNodeC
|
|
|
45
46
|
*/
|
|
46
47
|
private assertNotReplayed;
|
|
47
48
|
private rememberHandled;
|
|
49
|
+
/**
|
|
50
|
+
* Whether the session holds a valid access token. An expired access token is refreshed when the
|
|
51
|
+
* session holds a refresh token. A session whose access token expired and cannot be refreshed is
|
|
52
|
+
* removed.
|
|
53
|
+
*/
|
|
48
54
|
isSignedIn(userId?: string): Promise<boolean>;
|
|
49
55
|
getIdToken(userId?: string): Promise<string>;
|
|
56
|
+
/**
|
|
57
|
+
* Refreshes the session's tokens. Concurrent calls for one session share a single request, so a
|
|
58
|
+
* rotated refresh token is not sent twice.
|
|
59
|
+
*/
|
|
50
60
|
refreshAccessToken(userId?: string): Promise<TokenResponse | User>;
|
|
51
61
|
revokeAccessToken(userId?: string): Promise<Response | boolean>;
|
|
52
62
|
getDecodedIdToken(userId?: string, idToken?: string): Promise<IdToken>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThunderIDNodeClient.d.ts","sourceRoot":"","sources":["../src/ThunderIDNodeClient.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,uBAAuB,EACvB,iCAAiC,EAIjC,OAAO,EAIP,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,EAC3B,aAAa,EAGb,OAAO,EAEP,yBAAyB,EACzB,0BAA0B,EAC1B,aAAa,EAEb,IAAI,EACL,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAC,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ThunderIDNodeClient.d.ts","sourceRoot":"","sources":["../src/ThunderIDNodeClient.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -31,8 +31,10 @@ let base64url = require("base64url");
|
|
|
31
31
|
base64url = __toESM(base64url);
|
|
32
32
|
let fast_sha256 = require("fast-sha256");
|
|
33
33
|
fast_sha256 = __toESM(fast_sha256);
|
|
34
|
-
let
|
|
35
|
-
|
|
34
|
+
let jose_key_import = require("jose/key/import");
|
|
35
|
+
jose_key_import = __toESM(jose_key_import);
|
|
36
|
+
let jose_jwt_verify = require("jose/jwt/verify");
|
|
37
|
+
jose_jwt_verify = __toESM(jose_jwt_verify);
|
|
36
38
|
let secure_random_bytes = require("secure-random-bytes");
|
|
37
39
|
secure_random_bytes = __toESM(secure_random_bytes);
|
|
38
40
|
let uuid = require("uuid");
|
|
@@ -76,8 +78,7 @@ var NodeCryptoUtils = class {
|
|
|
76
78
|
return (0, secure_random_bytes.default)(length);
|
|
77
79
|
}
|
|
78
80
|
async verifyJwt(idToken, jwk, algorithms, clientId, issuer, subject, clockTolerance) {
|
|
79
|
-
|
|
80
|
-
return jose.jwtVerify(idToken, key, {
|
|
81
|
+
return (0, jose_jwt_verify.jwtVerify)(idToken, await (0, jose_key_import.importJWK)(jwk), {
|
|
81
82
|
algorithms,
|
|
82
83
|
audience: [clientId],
|
|
83
84
|
clockTolerance,
|
|
@@ -164,49 +165,12 @@ var SessionIndex = class {
|
|
|
164
165
|
};
|
|
165
166
|
var SessionIndex_default = SessionIndex;
|
|
166
167
|
|
|
167
|
-
//#endregion
|
|
168
|
-
//#region src/utils/SessionUtils.ts
|
|
169
|
-
const UUID_VERSION = 4;
|
|
170
|
-
/**
|
|
171
|
-
* Utility class for session validation and UUID management.
|
|
172
|
-
*/
|
|
173
|
-
var SessionUtils = class {
|
|
174
|
-
constructor() {}
|
|
175
|
-
/**
|
|
176
|
-
* Generates a new UUID v4 string.
|
|
177
|
-
*
|
|
178
|
-
* @returns A new UUID string.
|
|
179
|
-
*/
|
|
180
|
-
static createUUID() {
|
|
181
|
-
return (0, uuid.v4)();
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Returns `true` if the given string is a valid UUID v4.
|
|
185
|
-
*
|
|
186
|
-
* @param uuid - The UUID string to validate.
|
|
187
|
-
*/
|
|
188
|
-
static validateUUID(uuid$1) {
|
|
189
|
-
if ((0, uuid.validate)(uuid$1) && (0, uuid.version)(uuid$1) === UUID_VERSION) return Promise.resolve(true);
|
|
190
|
-
return Promise.resolve(false);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Returns `true` if the session token is still within its validity window.
|
|
194
|
-
*
|
|
195
|
-
* @param sessionData - The session data to check.
|
|
196
|
-
*/
|
|
197
|
-
static validateSession(sessionData) {
|
|
198
|
-
const currentTime = Date.now();
|
|
199
|
-
const expiryTimeStamp = sessionData.created_at + parseInt(sessionData.expires_in, 10) * 60 * 1e3;
|
|
200
|
-
return Promise.resolve(currentTime < expiryTimeStamp);
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
var SessionUtils_default = SessionUtils;
|
|
204
|
-
|
|
205
168
|
//#endregion
|
|
206
169
|
//#region src/ThunderIDNodeClient.ts
|
|
207
170
|
var ThunderIDNodeClient = class extends __thunderid_javascript.ThunderIDJavaScriptClient {
|
|
208
171
|
_nodeInstanceId = 0;
|
|
209
172
|
_handledLogoutTokens = /* @__PURE__ */ new Map();
|
|
173
|
+
_refreshesInFlight = /* @__PURE__ */ new Map();
|
|
210
174
|
constructor(instanceId = 0) {
|
|
211
175
|
super(void 0, new NodeCryptoUtils_default());
|
|
212
176
|
this._nodeInstanceId = instanceId;
|
|
@@ -382,13 +346,23 @@ var ThunderIDNodeClient = class extends __thunderid_javascript.ThunderIDJavaScri
|
|
|
382
346
|
rememberHandled(claims) {
|
|
383
347
|
this._handledLogoutTokens.set(claims.jti, claims.exp * 1e3);
|
|
384
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Whether the session holds a valid access token. An expired access token is refreshed when the
|
|
351
|
+
* session holds a refresh token. A session whose access token expired and cannot be refreshed is
|
|
352
|
+
* removed.
|
|
353
|
+
*/
|
|
385
354
|
async isSignedIn(userId) {
|
|
386
355
|
try {
|
|
387
|
-
if (
|
|
356
|
+
if (await super.isSignedIn(userId)) return true;
|
|
388
357
|
const sm = this.getStorageManager();
|
|
389
358
|
const sessionData = await sm.getSessionData(userId);
|
|
390
|
-
if (
|
|
391
|
-
if (
|
|
359
|
+
if (!sessionData?.access_token) return false;
|
|
360
|
+
if (sessionData.refresh_token) try {
|
|
361
|
+
await this.refreshAccessToken(userId);
|
|
362
|
+
return true;
|
|
363
|
+
} catch (error) {
|
|
364
|
+
__thunderid_javascript.logger.debug("The expired session could not be refreshed, so it is removed.", error);
|
|
365
|
+
}
|
|
392
366
|
await this.forgetSession(userId, sessionData);
|
|
393
367
|
await sm.removeSessionData(userId);
|
|
394
368
|
return false;
|
|
@@ -400,8 +374,22 @@ var ThunderIDNodeClient = class extends __thunderid_javascript.ThunderIDJavaScri
|
|
|
400
374
|
if (!await this.isSignedIn(userId)) return Promise.reject(new __thunderid_javascript.ThunderIDAuthException("NODE-AUTH_CLIENT-GIT-NF01", "The user is not logged in.", "No session was found for the requested user."));
|
|
401
375
|
return super.getIdToken(userId);
|
|
402
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Refreshes the session's tokens. Concurrent calls for one session share a single request, so a
|
|
379
|
+
* rotated refresh token is not sent twice.
|
|
380
|
+
*/
|
|
403
381
|
async refreshAccessToken(userId) {
|
|
404
|
-
|
|
382
|
+
const key = userId ?? "";
|
|
383
|
+
const inFlight = this._refreshesInFlight.get(key);
|
|
384
|
+
if (inFlight) return inFlight;
|
|
385
|
+
const refresh = (async () => {
|
|
386
|
+
await this.loadOpenIDProviderConfiguration(false);
|
|
387
|
+
return super.refreshAccessToken(userId);
|
|
388
|
+
})().finally(() => {
|
|
389
|
+
this._refreshesInFlight.delete(key);
|
|
390
|
+
});
|
|
391
|
+
this._refreshesInFlight.set(key, refresh);
|
|
392
|
+
return refresh;
|
|
405
393
|
}
|
|
406
394
|
async revokeAccessToken(userId) {
|
|
407
395
|
await this.forgetSession(userId);
|
|
@@ -589,6 +577,45 @@ var CookieChunking = class CookieChunking {
|
|
|
589
577
|
};
|
|
590
578
|
var CookieChunking_default = CookieChunking;
|
|
591
579
|
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/utils/SessionUtils.ts
|
|
582
|
+
const UUID_VERSION = 4;
|
|
583
|
+
/**
|
|
584
|
+
* Utility class for session validation and UUID management.
|
|
585
|
+
*/
|
|
586
|
+
var SessionUtils = class {
|
|
587
|
+
constructor() {}
|
|
588
|
+
/**
|
|
589
|
+
* Generates a new UUID v4 string.
|
|
590
|
+
*
|
|
591
|
+
* @returns A new UUID string.
|
|
592
|
+
*/
|
|
593
|
+
static createUUID() {
|
|
594
|
+
return (0, uuid.v4)();
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Returns `true` if the given string is a valid UUID v4.
|
|
598
|
+
*
|
|
599
|
+
* @param uuid - The UUID string to validate.
|
|
600
|
+
*/
|
|
601
|
+
static validateUUID(uuid$1) {
|
|
602
|
+
if ((0, uuid.validate)(uuid$1) && (0, uuid.version)(uuid$1) === UUID_VERSION) return Promise.resolve(true);
|
|
603
|
+
return Promise.resolve(false);
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Returns `true` if the session token is still within its validity window.
|
|
607
|
+
*
|
|
608
|
+
* @param sessionData - The session data to check. `expires_in` is in seconds, as the token
|
|
609
|
+
* endpoint returns it, and `created_at` in milliseconds.
|
|
610
|
+
*/
|
|
611
|
+
static validateSession(sessionData) {
|
|
612
|
+
const currentTime = Date.now();
|
|
613
|
+
const expiryTimeStamp = sessionData.created_at + parseInt(sessionData.expires_in, 10) * 1e3;
|
|
614
|
+
return Promise.resolve(currentTime < expiryTimeStamp);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
var SessionUtils_default = SessionUtils;
|
|
618
|
+
|
|
592
619
|
//#endregion
|
|
593
620
|
//#region src/utils/LoggedOutSessions.ts
|
|
594
621
|
/**
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,8 @@ import { BackchannelLogoutConstants, InvalidLogoutTokenError, ThunderIDAuthExcep
|
|
|
3
3
|
import cache from "memory-cache";
|
|
4
4
|
import base64url from "base64url";
|
|
5
5
|
import sha256 from "fast-sha256";
|
|
6
|
-
import
|
|
6
|
+
import { importJWK } from "jose/key/import";
|
|
7
|
+
import { jwtVerify } from "jose/jwt/verify";
|
|
7
8
|
import randombytes from "secure-random-bytes";
|
|
8
9
|
import { v4, validate, version } from "uuid";
|
|
9
10
|
|
|
@@ -47,8 +48,7 @@ var NodeCryptoUtils = class {
|
|
|
47
48
|
return randombytes(length);
|
|
48
49
|
}
|
|
49
50
|
async verifyJwt(idToken, jwk, algorithms, clientId, issuer, subject, clockTolerance) {
|
|
50
|
-
|
|
51
|
-
return jose.jwtVerify(idToken, key, {
|
|
51
|
+
return jwtVerify(idToken, await importJWK(jwk), {
|
|
52
52
|
algorithms,
|
|
53
53
|
audience: [clientId],
|
|
54
54
|
clockTolerance,
|
|
@@ -135,49 +135,12 @@ var SessionIndex = class {
|
|
|
135
135
|
};
|
|
136
136
|
var SessionIndex_default = SessionIndex;
|
|
137
137
|
|
|
138
|
-
//#endregion
|
|
139
|
-
//#region src/utils/SessionUtils.ts
|
|
140
|
-
const UUID_VERSION = 4;
|
|
141
|
-
/**
|
|
142
|
-
* Utility class for session validation and UUID management.
|
|
143
|
-
*/
|
|
144
|
-
var SessionUtils = class {
|
|
145
|
-
constructor() {}
|
|
146
|
-
/**
|
|
147
|
-
* Generates a new UUID v4 string.
|
|
148
|
-
*
|
|
149
|
-
* @returns A new UUID string.
|
|
150
|
-
*/
|
|
151
|
-
static createUUID() {
|
|
152
|
-
return v4();
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Returns `true` if the given string is a valid UUID v4.
|
|
156
|
-
*
|
|
157
|
-
* @param uuid - The UUID string to validate.
|
|
158
|
-
*/
|
|
159
|
-
static validateUUID(uuid) {
|
|
160
|
-
if (validate(uuid) && version(uuid) === UUID_VERSION) return Promise.resolve(true);
|
|
161
|
-
return Promise.resolve(false);
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Returns `true` if the session token is still within its validity window.
|
|
165
|
-
*
|
|
166
|
-
* @param sessionData - The session data to check.
|
|
167
|
-
*/
|
|
168
|
-
static validateSession(sessionData) {
|
|
169
|
-
const currentTime = Date.now();
|
|
170
|
-
const expiryTimeStamp = sessionData.created_at + parseInt(sessionData.expires_in, 10) * 60 * 1e3;
|
|
171
|
-
return Promise.resolve(currentTime < expiryTimeStamp);
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
var SessionUtils_default = SessionUtils;
|
|
175
|
-
|
|
176
138
|
//#endregion
|
|
177
139
|
//#region src/ThunderIDNodeClient.ts
|
|
178
140
|
var ThunderIDNodeClient = class extends ThunderIDJavaScriptClient {
|
|
179
141
|
_nodeInstanceId = 0;
|
|
180
142
|
_handledLogoutTokens = /* @__PURE__ */ new Map();
|
|
143
|
+
_refreshesInFlight = /* @__PURE__ */ new Map();
|
|
181
144
|
constructor(instanceId = 0) {
|
|
182
145
|
super(void 0, new NodeCryptoUtils_default());
|
|
183
146
|
this._nodeInstanceId = instanceId;
|
|
@@ -353,13 +316,23 @@ var ThunderIDNodeClient = class extends ThunderIDJavaScriptClient {
|
|
|
353
316
|
rememberHandled(claims) {
|
|
354
317
|
this._handledLogoutTokens.set(claims.jti, claims.exp * 1e3);
|
|
355
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Whether the session holds a valid access token. An expired access token is refreshed when the
|
|
321
|
+
* session holds a refresh token. A session whose access token expired and cannot be refreshed is
|
|
322
|
+
* removed.
|
|
323
|
+
*/
|
|
356
324
|
async isSignedIn(userId) {
|
|
357
325
|
try {
|
|
358
|
-
if (
|
|
326
|
+
if (await super.isSignedIn(userId)) return true;
|
|
359
327
|
const sm = this.getStorageManager();
|
|
360
328
|
const sessionData = await sm.getSessionData(userId);
|
|
361
|
-
if (
|
|
362
|
-
if (
|
|
329
|
+
if (!sessionData?.access_token) return false;
|
|
330
|
+
if (sessionData.refresh_token) try {
|
|
331
|
+
await this.refreshAccessToken(userId);
|
|
332
|
+
return true;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
logger.debug("The expired session could not be refreshed, so it is removed.", error);
|
|
335
|
+
}
|
|
363
336
|
await this.forgetSession(userId, sessionData);
|
|
364
337
|
await sm.removeSessionData(userId);
|
|
365
338
|
return false;
|
|
@@ -371,8 +344,22 @@ var ThunderIDNodeClient = class extends ThunderIDJavaScriptClient {
|
|
|
371
344
|
if (!await this.isSignedIn(userId)) return Promise.reject(new ThunderIDAuthException("NODE-AUTH_CLIENT-GIT-NF01", "The user is not logged in.", "No session was found for the requested user."));
|
|
372
345
|
return super.getIdToken(userId);
|
|
373
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Refreshes the session's tokens. Concurrent calls for one session share a single request, so a
|
|
349
|
+
* rotated refresh token is not sent twice.
|
|
350
|
+
*/
|
|
374
351
|
async refreshAccessToken(userId) {
|
|
375
|
-
|
|
352
|
+
const key = userId ?? "";
|
|
353
|
+
const inFlight = this._refreshesInFlight.get(key);
|
|
354
|
+
if (inFlight) return inFlight;
|
|
355
|
+
const refresh = (async () => {
|
|
356
|
+
await this.loadOpenIDProviderConfiguration(false);
|
|
357
|
+
return super.refreshAccessToken(userId);
|
|
358
|
+
})().finally(() => {
|
|
359
|
+
this._refreshesInFlight.delete(key);
|
|
360
|
+
});
|
|
361
|
+
this._refreshesInFlight.set(key, refresh);
|
|
362
|
+
return refresh;
|
|
376
363
|
}
|
|
377
364
|
async revokeAccessToken(userId) {
|
|
378
365
|
await this.forgetSession(userId);
|
|
@@ -560,6 +547,45 @@ var CookieChunking = class CookieChunking {
|
|
|
560
547
|
};
|
|
561
548
|
var CookieChunking_default = CookieChunking;
|
|
562
549
|
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/utils/SessionUtils.ts
|
|
552
|
+
const UUID_VERSION = 4;
|
|
553
|
+
/**
|
|
554
|
+
* Utility class for session validation and UUID management.
|
|
555
|
+
*/
|
|
556
|
+
var SessionUtils = class {
|
|
557
|
+
constructor() {}
|
|
558
|
+
/**
|
|
559
|
+
* Generates a new UUID v4 string.
|
|
560
|
+
*
|
|
561
|
+
* @returns A new UUID string.
|
|
562
|
+
*/
|
|
563
|
+
static createUUID() {
|
|
564
|
+
return v4();
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Returns `true` if the given string is a valid UUID v4.
|
|
568
|
+
*
|
|
569
|
+
* @param uuid - The UUID string to validate.
|
|
570
|
+
*/
|
|
571
|
+
static validateUUID(uuid) {
|
|
572
|
+
if (validate(uuid) && version(uuid) === UUID_VERSION) return Promise.resolve(true);
|
|
573
|
+
return Promise.resolve(false);
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Returns `true` if the session token is still within its validity window.
|
|
577
|
+
*
|
|
578
|
+
* @param sessionData - The session data to check. `expires_in` is in seconds, as the token
|
|
579
|
+
* endpoint returns it, and `created_at` in milliseconds.
|
|
580
|
+
*/
|
|
581
|
+
static validateSession(sessionData) {
|
|
582
|
+
const currentTime = Date.now();
|
|
583
|
+
const expiryTimeStamp = sessionData.created_at + parseInt(sessionData.expires_in, 10) * 1e3;
|
|
584
|
+
return Promise.resolve(currentTime < expiryTimeStamp);
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
var SessionUtils_default = SessionUtils;
|
|
588
|
+
|
|
563
589
|
//#endregion
|
|
564
590
|
//#region src/utils/LoggedOutSessions.ts
|
|
565
591
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeCryptoUtils.d.ts","sourceRoot":"","sources":["../../src/utils/NodeCryptoUtils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,MAAM,EAAE,YAAY,EAAC,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"NodeCryptoUtils.d.ts","sourceRoot":"","sources":["../../src/utils/NodeCryptoUtils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,MAAM,EAAE,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAS3D;;;GAGG;AACH,cAAM,eAAgB,YAAW,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IAC/C,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAI/C,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAItC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAIzC,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAI9C,SAAS,CACpB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,OAAO,CAAC,YAAY,CAAC,EAC1B,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC;CAUpB;AAED,eAAe,eAAe,CAAC"}
|
|
@@ -19,7 +19,8 @@ declare class SessionUtils {
|
|
|
19
19
|
/**
|
|
20
20
|
* Returns `true` if the session token is still within its validity window.
|
|
21
21
|
*
|
|
22
|
-
* @param sessionData - The session data to check.
|
|
22
|
+
* @param sessionData - The session data to check. `expires_in` is in seconds, as the token
|
|
23
|
+
* endpoint returns it, and `created_at` in milliseconds.
|
|
23
24
|
*/
|
|
24
25
|
static validateSession(sessionData: SessionData): Promise<boolean>;
|
|
25
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionUtils.d.ts","sourceRoot":"","sources":["../../src/utils/SessionUtils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAKlD;;GAEG;AACH,cAAM,YAAY;IAChB,OAAO;IAEP;;;;OAIG;WACW,UAAU,IAAI,MAAM;IAIlC;;;;OAIG;WACW,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO1D
|
|
1
|
+
{"version":3,"file":"SessionUtils.d.ts","sourceRoot":"","sources":["../../src/utils/SessionUtils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAKlD;;GAEG;AACH,cAAM,YAAY;IAChB,OAAO;IAEP;;;;OAIG;WACW,UAAU,IAAI,MAAM;IAIlC;;;;OAIG;WACW,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO1D;;;;;OAKG;WACW,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;CAK1E;AAED,eAAe,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thunderid/node",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Node.js runtime SDK for ThunderID",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"thunderid",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"vitest": "4.1.11"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@thunderid/javascript": "^1.4.
|
|
47
|
+
"@thunderid/javascript": "^1.4.1",
|
|
48
48
|
"base64url": "3.0.1",
|
|
49
49
|
"cross-fetch": "4.1.0",
|
|
50
50
|
"fast-sha256": "1.3.0",
|