@rodit/rodit-auth-be 9.11.16 → 9.11.20
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/lib/auth/tokenservice.js +161 -19
- package/package.json +1 -1
package/lib/auth/tokenservice.js
CHANGED
|
@@ -973,6 +973,31 @@ function resolveCredentialExpirationUnix(now, sessionExpiration, own_rodit) {
|
|
|
973
973
|
keyCreationDuration,
|
|
974
974
|
});
|
|
975
975
|
|
|
976
|
+
const signatureStart = Date.now();
|
|
977
|
+
const timeString = await unixTimeToDateString(now);
|
|
978
|
+
const roditidandtimestamp = new TextEncoder().encode(
|
|
979
|
+
token.rodit_id + timeString
|
|
980
|
+
);
|
|
981
|
+
let privateKeyToUse = config_own_rodit.own_rodit_bytes_private_key;
|
|
982
|
+
if (Buffer.isBuffer(privateKeyToUse)) {
|
|
983
|
+
privateKeyToUse = new Uint8Array(privateKeyToUse);
|
|
984
|
+
} else if (!(privateKeyToUse instanceof Uint8Array)) {
|
|
985
|
+
privateKeyToUse = new Uint8Array(Array.from(privateKeyToUse));
|
|
986
|
+
}
|
|
987
|
+
const own_rodit_bytes_signature = nacl.sign.detached(
|
|
988
|
+
roditidandtimestamp,
|
|
989
|
+
privateKeyToUse
|
|
990
|
+
);
|
|
991
|
+
const renewed_rodit_idsignature = Buffer.from(
|
|
992
|
+
own_rodit_bytes_signature
|
|
993
|
+
).toString("base64url");
|
|
994
|
+
logger.debug("Regenerated rodit_idsignature for renewed credential", {
|
|
995
|
+
requestId,
|
|
996
|
+
signatureDuration: Date.now() - signatureStart,
|
|
997
|
+
roditId: token.rodit_id,
|
|
998
|
+
iat: now,
|
|
999
|
+
});
|
|
1000
|
+
|
|
976
1001
|
// Keep existing session ID and creation time
|
|
977
1002
|
const session_id = existingSessionId;
|
|
978
1003
|
const session_iat = token.session_iat;
|
|
@@ -1028,7 +1053,7 @@ function resolveCredentialExpirationUnix(now, sessionExpiration, own_rodit) {
|
|
|
1028
1053
|
rodit_id: token.rodit_id,
|
|
1029
1054
|
rodit_owner: token.rodit_owner,
|
|
1030
1055
|
rodit_allowediso3166list: token.rodit_allowediso3166list,
|
|
1031
|
-
rodit_idsignature:
|
|
1056
|
+
rodit_idsignature: renewed_rodit_idsignature,
|
|
1032
1057
|
rodit_maxrequests: token.rodit_maxrequests,
|
|
1033
1058
|
rodit_maxrqwindow: token.rodit_maxrqwindow,
|
|
1034
1059
|
rodit_permissionedroutes: token.rodit_permissionedroutes,
|
|
@@ -1665,14 +1690,35 @@ function resolveCredentialExpirationUnix(now, sessionExpiration, own_rodit) {
|
|
|
1665
1690
|
newToken = renewalResult.newToken;
|
|
1666
1691
|
|
|
1667
1692
|
if (isExpired && !newToken) {
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1693
|
+
const renewalNow = Math.floor(Date.now() / 1000);
|
|
1694
|
+
const sessionExpUnix =
|
|
1695
|
+
payload.session_exp != null ? Number(payload.session_exp) : null;
|
|
1696
|
+
const sessionStillActive =
|
|
1697
|
+
Number.isFinite(sessionExpUnix) && sessionExpUnix > renewalNow;
|
|
1698
|
+
|
|
1699
|
+
if (sessionStillActive) {
|
|
1700
|
+
logger.warn(
|
|
1701
|
+
"Credential expired and renewal failed but session still active; allowing request",
|
|
1702
|
+
{
|
|
1703
|
+
component: "JwtAuth",
|
|
1704
|
+
method: "validate_jwt_token_be",
|
|
1705
|
+
requestId,
|
|
1706
|
+
jti: payload.jti,
|
|
1707
|
+
sessionExp: sessionExpUnix,
|
|
1708
|
+
now: renewalNow,
|
|
1709
|
+
}
|
|
1710
|
+
);
|
|
1711
|
+
} else {
|
|
1712
|
+
logger.error("Token expired and renewal failed", {
|
|
1713
|
+
component: "JwtAuth",
|
|
1714
|
+
method: "validate_jwt_token_be",
|
|
1715
|
+
requestId,
|
|
1716
|
+
jti: payload.jti,
|
|
1717
|
+
sessionExp: sessionExpUnix,
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
throw new Error("Error 007: Token has expired and renewal failed");
|
|
1721
|
+
}
|
|
1676
1722
|
}
|
|
1677
1723
|
} else if (isExpired) {
|
|
1678
1724
|
logger.info("Allowing signature-valid expired token for special flow", {
|
|
@@ -2213,6 +2259,70 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2213
2259
|
}
|
|
2214
2260
|
}
|
|
2215
2261
|
|
|
2262
|
+
/**
|
|
2263
|
+
* Decide brief vs thorough renewal verification using existing SECURITY_OPTIONS constants.
|
|
2264
|
+
*
|
|
2265
|
+
* @param {Object} params
|
|
2266
|
+
* @returns {Object} Plan with shouldDoFullVerification, urgency, pThorough, newduration, floors
|
|
2267
|
+
*/
|
|
2268
|
+
function resolveRenewalVerificationPlan({
|
|
2269
|
+
forceRenewal = false,
|
|
2270
|
+
durationLeftpct = 0,
|
|
2271
|
+
currentDuration = 0,
|
|
2272
|
+
lapsedProportion = 0.8,
|
|
2273
|
+
thresholdValidationType = 0.1,
|
|
2274
|
+
durationRamp = 0.85,
|
|
2275
|
+
fallbackJwtDuration = 3600,
|
|
2276
|
+
roditMaxRqWindow,
|
|
2277
|
+
randomNumber = Math.random(),
|
|
2278
|
+
}) {
|
|
2279
|
+
const eligibilityTail = 1.0 - lapsedProportion;
|
|
2280
|
+
const safeCurrentDuration = Math.max(0, Number(currentDuration) || 0);
|
|
2281
|
+
const newduration = safeCurrentDuration * durationRamp;
|
|
2282
|
+
|
|
2283
|
+
let urgency;
|
|
2284
|
+
if (forceRenewal) {
|
|
2285
|
+
urgency = 1;
|
|
2286
|
+
} else if (eligibilityTail <= 0) {
|
|
2287
|
+
urgency = 1;
|
|
2288
|
+
} else {
|
|
2289
|
+
const tailFraction = durationLeftpct / 100 / eligibilityTail;
|
|
2290
|
+
urgency = Math.min(1, Math.max(0, 1 - tailFraction));
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
const pThorough =
|
|
2294
|
+
thresholdValidationType + urgency * (1 - thresholdValidationType);
|
|
2295
|
+
|
|
2296
|
+
const baselineDuration = Math.max(1, Number(fallbackJwtDuration) || 3600);
|
|
2297
|
+
const rampedFloor = baselineDuration * eligibilityTail * durationRamp;
|
|
2298
|
+
const maxRqWindow = Number(roditMaxRqWindow) || baselineDuration;
|
|
2299
|
+
const rqWindowFloor = maxRqWindow * eligibilityTail;
|
|
2300
|
+
|
|
2301
|
+
const stochasticThorough = urgency >= 1 || randomNumber < pThorough;
|
|
2302
|
+
const rampedFloorThorough = newduration <= rampedFloor;
|
|
2303
|
+
const rqWindowFloorThorough = newduration <= rqWindowFloor;
|
|
2304
|
+
const deterministicThorough = rampedFloorThorough || rqWindowFloorThorough;
|
|
2305
|
+
|
|
2306
|
+
let verificationReason = "brief";
|
|
2307
|
+
if (deterministicThorough) {
|
|
2308
|
+
verificationReason = rampedFloorThorough ? "ramped_floor" : "rq_window_floor";
|
|
2309
|
+
} else if (stochasticThorough) {
|
|
2310
|
+
verificationReason = "stochastic";
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
return {
|
|
2314
|
+
shouldDoFullVerification: stochasticThorough || deterministicThorough,
|
|
2315
|
+
urgency,
|
|
2316
|
+
pThorough,
|
|
2317
|
+
newduration,
|
|
2318
|
+
rampedFloor,
|
|
2319
|
+
rqWindowFloor,
|
|
2320
|
+
stochasticThorough,
|
|
2321
|
+
deterministicThorough,
|
|
2322
|
+
verificationReason,
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2216
2326
|
/**
|
|
2217
2327
|
* Check if a token needs renewal and renew if necessary
|
|
2218
2328
|
*
|
|
@@ -2236,12 +2346,16 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2236
2346
|
const DURATIONRAMP = parseFloat(
|
|
2237
2347
|
config.get('SECURITY_OPTIONS.DURATIONRAMP', '0.85')
|
|
2238
2348
|
);
|
|
2349
|
+
const FALLBACK_JWT_DURATION = parseInt(
|
|
2350
|
+
config.get('SECURITY_OPTIONS.FALLBACK_JWT_DURATION', '3600'),
|
|
2351
|
+
10
|
|
2352
|
+
);
|
|
2239
2353
|
|
|
2240
2354
|
const currentTime = Math.floor(Date.now() / 1000);
|
|
2241
2355
|
const timeLeft = payload.exp - currentTime;
|
|
2242
2356
|
const currentDuration = payload.exp - payload.iat;
|
|
2243
|
-
const durationLeftpct =
|
|
2244
|
-
|
|
2357
|
+
const durationLeftpct =
|
|
2358
|
+
currentDuration > 0 ? (timeLeft / currentDuration) * 100 : 0;
|
|
2245
2359
|
|
|
2246
2360
|
// Log session information
|
|
2247
2361
|
const sessionInfo = {
|
|
@@ -2301,13 +2415,40 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2301
2415
|
...sessionInfo,
|
|
2302
2416
|
});
|
|
2303
2417
|
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2418
|
+
const renewalPlan = resolveRenewalVerificationPlan({
|
|
2419
|
+
forceRenewal,
|
|
2420
|
+
durationLeftpct,
|
|
2421
|
+
currentDuration,
|
|
2422
|
+
lapsedProportion: LAPSED_LIFETIME_PROPORTION_4RENEWAL_ELIGIBILITY,
|
|
2423
|
+
thresholdValidationType: THRESHOLD_VALIDATION_TYPE,
|
|
2424
|
+
durationRamp: DURATIONRAMP,
|
|
2425
|
+
fallbackJwtDuration: FALLBACK_JWT_DURATION,
|
|
2426
|
+
roditMaxRqWindow: payload.rodit_maxrqwindow,
|
|
2427
|
+
});
|
|
2428
|
+
const {
|
|
2429
|
+
shouldDoFullVerification,
|
|
2430
|
+
urgency,
|
|
2431
|
+
pThorough,
|
|
2432
|
+
newduration,
|
|
2433
|
+
rampedFloor,
|
|
2434
|
+
rqWindowFloor,
|
|
2435
|
+
verificationReason,
|
|
2436
|
+
} = renewalPlan;
|
|
2437
|
+
|
|
2438
|
+
logger.debug("Renewal verification plan", {
|
|
2439
|
+
component: "TokenRenewalService",
|
|
2440
|
+
method: "checkandrenew_jwt_token",
|
|
2441
|
+
requestId,
|
|
2442
|
+
forceRenewal,
|
|
2443
|
+
durationLeftpct: durationLeftpct.toFixed(1),
|
|
2444
|
+
urgency: urgency.toFixed(3),
|
|
2445
|
+
pThorough: pThorough.toFixed(3),
|
|
2446
|
+
newduration: Math.floor(newduration),
|
|
2447
|
+
rampedFloor: Math.floor(rampedFloor),
|
|
2448
|
+
rqWindowFloor: Math.floor(rqWindowFloor),
|
|
2449
|
+
verificationReason,
|
|
2450
|
+
shouldDoFullVerification,
|
|
2451
|
+
});
|
|
2311
2452
|
|
|
2312
2453
|
const verificationStartTime = Date.now();
|
|
2313
2454
|
|
|
@@ -2392,7 +2533,7 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2392
2533
|
logInfo: {
|
|
2393
2534
|
newDuration: newduration,
|
|
2394
2535
|
reason: shouldDoFullVerification
|
|
2395
|
-
?
|
|
2536
|
+
? `Thorough verification (${verificationReason})`
|
|
2396
2537
|
: "Brief verification",
|
|
2397
2538
|
notAfter: notAfter,
|
|
2398
2539
|
renewalDuration,
|
|
@@ -2439,6 +2580,7 @@ module.exports = {
|
|
|
2439
2580
|
generate_jwt_token,
|
|
2440
2581
|
base64url2jwk_public_key,
|
|
2441
2582
|
checkandrenew_jwt_token,
|
|
2583
|
+
resolveRenewalVerificationPlan,
|
|
2442
2584
|
thorough_validate_jwt_token_be,
|
|
2443
2585
|
brief_validate_jwt_token_be,
|
|
2444
2586
|
generate_jwt_token_fromtoken,
|