@nxgt/janus 0.10.0 → 0.12.0
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 +118 -18
- package/dist/auth/config/janus-config.d.ts +2 -0
- package/dist/auth/config/janus-config.d.ts.map +1 -1
- package/dist/auth/config/resolved-config.d.ts +1 -0
- package/dist/auth/config/resolved-config.d.ts.map +1 -1
- package/dist/auth/index.d.ts +2 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/port/assert-stores.d.ts.map +1 -1
- package/dist/auth/port/memory/sessions.d.ts.map +1 -1
- package/dist/auth/port/types/sessions.d.ts +16 -1
- package/dist/auth/port/types/sessions.d.ts.map +1 -1
- package/dist/auth/port/types/tokens.d.ts +7 -2
- package/dist/auth/port/types/tokens.d.ts.map +1 -1
- package/dist/auth/second-factor/{regenerate-attempts.d.ts → app-code-attempts.d.ts} +13 -10
- package/dist/auth/second-factor/app-code-attempts.d.ts.map +1 -0
- package/dist/auth/second-factor/codes-left.d.ts +14 -0
- package/dist/auth/second-factor/codes-left.d.ts.map +1 -0
- package/dist/auth/second-factor/flows.d.ts +1 -0
- package/dist/auth/second-factor/flows.d.ts.map +1 -1
- package/dist/auth/sessions/authenticate.d.ts.map +1 -1
- package/dist/auth/sessions/fresh.d.ts +26 -0
- package/dist/auth/sessions/fresh.d.ts.map +1 -0
- package/dist/auth/sessions/standing.d.ts +20 -0
- package/dist/auth/sessions/standing.d.ts.map +1 -0
- package/dist/auth/step-up/codes.d.ts +29 -0
- package/dist/auth/step-up/codes.d.ts.map +1 -0
- package/dist/auth/step-up/flows.d.ts +19 -0
- package/dist/auth/step-up/flows.d.ts.map +1 -0
- package/dist/auth/step-up/open.d.ts +23 -0
- package/dist/auth/step-up/open.d.ts.map +1 -0
- package/dist/auth/step-up/request.d.ts +12 -0
- package/dist/auth/step-up/request.d.ts.map +1 -0
- package/dist/auth/types/index.d.ts +1 -0
- package/dist/auth/types/index.d.ts.map +1 -1
- package/dist/auth/types/janus.d.ts +2 -1
- package/dist/auth/types/janus.d.ts.map +1 -1
- package/dist/auth/types/second-factor.d.ts +9 -0
- package/dist/auth/types/second-factor.d.ts.map +1 -1
- package/dist/auth/types/step-up.d.ts +79 -0
- package/dist/auth/types/step-up.d.ts.map +1 -0
- package/dist/auth/users/any-type-api.d.ts +2 -2
- package/dist/auth/users/any-type-api.d.ts.map +1 -1
- package/dist/auth/users/type-api.d.ts +1 -1
- package/dist/auth/users/type-api.d.ts.map +1 -1
- package/dist/chunks/{index-5e2fw7he.js → index-j2gd8fry.js} +12 -3
- package/dist/chunks/{index-5e2fw7he.js.map → index-j2gd8fry.js.map} +4 -4
- package/dist/chunks/{index-5tm04t5p.js → index-jp2kka6d.js} +2 -2
- package/dist/chunks/{index-6f6wja93.js → index-km7ffctx.js} +2 -2
- package/dist/chunks/{index-4m49cye4.js → index-rd2rddvv.js} +15 -12
- package/dist/chunks/{index-4m49cye4.js.map → index-rd2rddvv.js.map} +3 -3
- package/dist/conformance/cases/outage.d.ts.map +1 -1
- package/dist/conformance/cases/sessions/index.d.ts.map +1 -1
- package/dist/conformance/cases/sessions/reauthentication.d.ts +4 -0
- package/dist/conformance/cases/sessions/reauthentication.d.ts.map +1 -0
- package/dist/conformance/cases/tokens/index.d.ts.map +1 -1
- package/dist/conformance/cases/tokens/kinds.d.ts +4 -0
- package/dist/conformance/cases/tokens/kinds.d.ts.map +1 -0
- package/dist/conformance/index.js +147 -60
- package/dist/conformance/index.js.map +8 -6
- package/dist/errors/janus-error/codes.d.ts +7 -0
- package/dist/errors/janus-error/codes.d.ts.map +1 -1
- package/dist/errors/janus-error/index.d.ts +1 -1
- package/dist/errors/janus-error/index.d.ts.map +1 -1
- package/dist/errors/janus-error/refusals.d.ts +8 -0
- package/dist/errors/janus-error/refusals.d.ts.map +1 -1
- package/dist/errors/janus-error/status.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +184 -19
- package/dist/index.js.map +18 -11
- package/dist/permissions/index.js +3 -3
- package/docs/README.md +1 -0
- package/docs/guide/adapters.md +38 -4
- package/docs/guide/errors.md +1 -0
- package/docs/guide/events.md +21 -8
- package/docs/guide/second-factor.md +45 -16
- package/docs/guide/sessions.md +7 -0
- package/docs/guide/step-up.md +178 -0
- package/docs/guide/users.md +1 -0
- package/docs/guide/vocabulary.md +3 -2
- package/docs/roadmap.md +18 -23
- package/docs/troubleshooting.md +183 -8
- package/package.json +1 -1
- package/dist/auth/second-factor/regenerate-attempts.d.ts.map +0 -1
- /package/dist/chunks/{index-5tm04t5p.js.map → index-jp2kka6d.js.map} +0 -0
- /package/dist/chunks/{index-6f6wja93.js.map → index-km7ffctx.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
MAX_PAGE_SIZE2,
|
|
8
8
|
pageLimit2,
|
|
9
9
|
invalidCursor2
|
|
10
|
-
} from "./chunks/index-
|
|
10
|
+
} from "./chunks/index-jp2kka6d.js";
|
|
11
11
|
import {
|
|
12
12
|
mintId2,
|
|
13
13
|
isId2,
|
|
14
14
|
mintedAt2,
|
|
15
15
|
createMemoryStores2
|
|
16
|
-
} from "./chunks/index-
|
|
16
|
+
} from "./chunks/index-rd2rddvv.js";
|
|
17
17
|
import {
|
|
18
18
|
JanusError2,
|
|
19
19
|
UserInvalidError2,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
UserInactiveError2,
|
|
22
22
|
TokenError2,
|
|
23
23
|
SecondFactorError2,
|
|
24
|
+
StepUpRequiredError2,
|
|
24
25
|
InvalidCursorError2,
|
|
25
26
|
UnsupportedError2,
|
|
26
27
|
PermissionDepthError2,
|
|
@@ -37,7 +38,7 @@ import {
|
|
|
37
38
|
formatTuple2,
|
|
38
39
|
parseTuple2,
|
|
39
40
|
parseSubject2
|
|
40
|
-
} from "./chunks/index-
|
|
41
|
+
} from "./chunks/index-j2gd8fry.js";
|
|
41
42
|
|
|
42
43
|
// src/auth/hashers.ts
|
|
43
44
|
import { randomBytes, scrypt, timingSafeEqual } from "node:crypto";
|
|
@@ -382,7 +383,8 @@ function resolveTokenTtls(config, where) {
|
|
|
382
383
|
return {
|
|
383
384
|
verifyEmail: parseDuration(config.tokens?.verifyEmail ?? "24h", `${where}: tokens.verifyEmail`),
|
|
384
385
|
resetPassword: parseDuration(config.tokens?.resetPassword ?? "1h", `${where}: tokens.resetPassword`),
|
|
385
|
-
signInCode: parseDuration(config.tokens?.signInCode ?? "10m", `${where}: tokens.signInCode`)
|
|
386
|
+
signInCode: parseDuration(config.tokens?.signInCode ?? "10m", `${where}: tokens.signInCode`),
|
|
387
|
+
stepUp: parseDuration(config.tokens?.stepUp ?? "10m", `${where}: tokens.stepUp`)
|
|
386
388
|
};
|
|
387
389
|
}
|
|
388
390
|
// src/auth/context/create-context.ts
|
|
@@ -662,6 +664,7 @@ var REQUIRED = {
|
|
|
662
664
|
"insertSession",
|
|
663
665
|
"findSessionByTokenHash",
|
|
664
666
|
"extendSession",
|
|
667
|
+
"reauthenticateSession",
|
|
665
668
|
"revokeSession",
|
|
666
669
|
"revokeUserSessions",
|
|
667
670
|
"deleteUserSessions"
|
|
@@ -774,17 +777,24 @@ function headersOf(request) {
|
|
|
774
777
|
return inner;
|
|
775
778
|
return request;
|
|
776
779
|
}
|
|
780
|
+
// src/auth/sessions/standing.ts
|
|
781
|
+
async function standingSession(context, request) {
|
|
782
|
+
const token = presentedToken(request, context.config.cookie.name);
|
|
783
|
+
if (token === null)
|
|
784
|
+
return null;
|
|
785
|
+
const session = await context.store.sessions.findSessionByTokenHash(hashSecret(token));
|
|
786
|
+
const now = context.clock.now().getTime();
|
|
787
|
+
return session === null || session.revokedAt !== null || session.expiresAt.getTime() <= now ? null : { token, session };
|
|
788
|
+
}
|
|
789
|
+
|
|
777
790
|
// src/auth/sessions/authenticate.ts
|
|
778
791
|
async function authenticate(context, request, options) {
|
|
779
792
|
const { store, clock, config } = context;
|
|
780
|
-
const
|
|
781
|
-
if (
|
|
793
|
+
const presented = await standingSession(context, request);
|
|
794
|
+
if (presented === null)
|
|
782
795
|
return null;
|
|
783
|
-
const session =
|
|
796
|
+
const { token, session } = presented;
|
|
784
797
|
const now = clock.now().getTime();
|
|
785
|
-
if (session === null || session.revokedAt !== null || session.expiresAt.getTime() <= now) {
|
|
786
|
-
return null;
|
|
787
|
-
}
|
|
788
798
|
const user = await store.users.findUser(session.userId);
|
|
789
799
|
if (user === null || !user.active || options?.type !== undefined && user.type !== options.type) {
|
|
790
800
|
return null;
|
|
@@ -1260,6 +1270,13 @@ async function confirmChallenge(context, type, challenge, code, at) {
|
|
|
1260
1270
|
return openSession(context, type, written);
|
|
1261
1271
|
}
|
|
1262
1272
|
|
|
1273
|
+
// src/auth/second-factor/codes-left.ts
|
|
1274
|
+
async function recoveryCodesLeftOf(context, type, user, where) {
|
|
1275
|
+
const record = await getRecord(context, idOf(user), type.name, where);
|
|
1276
|
+
const factor = record.secondFactor;
|
|
1277
|
+
return isActive(factor) ? factor.recoveryCodes.length : null;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1263
1280
|
// src/auth/second-factor/recovery-codes.ts
|
|
1264
1281
|
import { createHmac as createHmac2, hkdfSync, randomInt as randomInt2, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
|
|
1265
1282
|
var RECOVERY_CODES = 10;
|
|
@@ -1404,10 +1421,10 @@ function accountOf(type, record, where) {
|
|
|
1404
1421
|
return String(record.fields[type.password.login]);
|
|
1405
1422
|
}
|
|
1406
1423
|
|
|
1407
|
-
// src/auth/second-factor/
|
|
1424
|
+
// src/auth/second-factor/app-code-attempts.ts
|
|
1408
1425
|
import { createHmac as createHmac3, hkdfSync as hkdfSync2 } from "node:crypto";
|
|
1409
|
-
var
|
|
1410
|
-
var WINDOW_MINUTES =
|
|
1426
|
+
var APP_CODE_WINDOW_MS = 15 * 60000;
|
|
1427
|
+
var WINDOW_MINUTES = APP_CODE_WINDOW_MS / 60000;
|
|
1411
1428
|
var PURPOSE2 = "janus/second-factor/regenerate-attempts/v1";
|
|
1412
1429
|
var MAX_LINKS = 32;
|
|
1413
1430
|
var derived2 = new WeakMap;
|
|
@@ -1429,10 +1446,10 @@ function linkHash(sealer, record, window, link) {
|
|
|
1429
1446
|
];
|
|
1430
1447
|
return hashSecret(createHmac3("sha256", counterKey(sealer)).update(parts.join("\x00")).digest("base64url"));
|
|
1431
1448
|
}
|
|
1432
|
-
async function
|
|
1449
|
+
async function countAppCodeAttempt(context, sealer, record, where) {
|
|
1433
1450
|
const now = context.clock.now().getTime();
|
|
1434
|
-
const window = Math.floor(now /
|
|
1435
|
-
const times = { now, expiresAt: (window + 2) *
|
|
1451
|
+
const window = Math.floor(now / APP_CODE_WINDOW_MS);
|
|
1452
|
+
const times = { now, expiresAt: (window + 2) * APP_CODE_WINDOW_MS };
|
|
1436
1453
|
let spent = 0;
|
|
1437
1454
|
for (let link = 0;link < MAX_LINKS; link += 1) {
|
|
1438
1455
|
const hash = linkHash(sealer, record, window, link);
|
|
@@ -1486,7 +1503,7 @@ async function regenerateRecoveryCodes(context, type, user, code, options, where
|
|
|
1486
1503
|
if (!isActive(factor)) {
|
|
1487
1504
|
throw refusal(type, "SECOND_FACTOR_NOT_ENROLLED", "the user has no active second factor — recovery codes come with one", where, record);
|
|
1488
1505
|
}
|
|
1489
|
-
const left = await
|
|
1506
|
+
const left = await countAppCodeAttempt(context, configured.sealer, record, where);
|
|
1490
1507
|
const accepted = acceptCode(configured, record, factor, String(code), now, where);
|
|
1491
1508
|
if (accepted === null) {
|
|
1492
1509
|
throw codeInvalid(where, record.id, type.name, left);
|
|
@@ -1549,7 +1566,8 @@ function secondFactorFlows(context, type, at) {
|
|
|
1549
1566
|
const api = {
|
|
1550
1567
|
...lifecycleFlows(context, type, at),
|
|
1551
1568
|
confirm: challenges.confirm,
|
|
1552
|
-
recover: (challenge, code) => recoverWithCode(context, type, challenge, code, at)
|
|
1569
|
+
recover: (challenge, code) => recoverWithCode(context, type, challenge, code, at),
|
|
1570
|
+
recoveryCodesLeft: (user) => recoveryCodesLeftOf(context, type, user, at("secondFactor.recoveryCodesLeft"))
|
|
1553
1571
|
};
|
|
1554
1572
|
return {
|
|
1555
1573
|
api,
|
|
@@ -1616,6 +1634,142 @@ async function confirmCode(context, type, finish, challenge, code, at) {
|
|
|
1616
1634
|
return finish(proved, where);
|
|
1617
1635
|
}
|
|
1618
1636
|
|
|
1637
|
+
// src/auth/step-up/codes.ts
|
|
1638
|
+
var spend = (context, secret, where) => spendOneTime(context, secret, "stepUp", where, "challenge");
|
|
1639
|
+
async function checkEmailedCode(context, type, user, counted, code, where) {
|
|
1640
|
+
const { token, secret, attemptsLeft } = counted;
|
|
1641
|
+
if (isActive(user.secondFactor)) {
|
|
1642
|
+
await spend(context, secret, where);
|
|
1643
|
+
throw new SecondFactorError2("SECOND_FACTOR_ACTIVE", `${where}: the user's second factor became active since the code was sent — request a step-up again`, { operation: where, userId: user.id, userType: type.name });
|
|
1644
|
+
}
|
|
1645
|
+
if (!codeMatches(token, secret, code)) {
|
|
1646
|
+
if (attemptsLeft === 0)
|
|
1647
|
+
await burnOneTime(context, secret, "stepUp");
|
|
1648
|
+
throw codeInvalid(where, user.id, type.name, attemptsLeft);
|
|
1649
|
+
}
|
|
1650
|
+
await spend(context, secret, where);
|
|
1651
|
+
refuseStale(type, user, token, where, "code");
|
|
1652
|
+
}
|
|
1653
|
+
async function checkAppCode(context, type, user, counted, code, where) {
|
|
1654
|
+
const { secret, attemptsLeft } = counted;
|
|
1655
|
+
const factor = user.secondFactor;
|
|
1656
|
+
if (!isActive(factor)) {
|
|
1657
|
+
await spend(context, secret, where);
|
|
1658
|
+
throw new SecondFactorError2("SECOND_FACTOR_NOT_ENROLLED", `${where}: the user no longer has a second factor — request a step-up again`, { operation: where, userId: user.id, userType: type.name });
|
|
1659
|
+
}
|
|
1660
|
+
const configured = requireSettings(context, where, "the user's second factor is active");
|
|
1661
|
+
const windowLeft = await countAppCodeAttempt(context, configured.sealer, user, where);
|
|
1662
|
+
const now = context.clock.now();
|
|
1663
|
+
const accepted = acceptCode(configured, user, factor, code, now, where);
|
|
1664
|
+
if (accepted === null) {
|
|
1665
|
+
if (attemptsLeft === 0)
|
|
1666
|
+
await burnOneTime(context, secret, "stepUp");
|
|
1667
|
+
throw codeInvalid(where, user.id, type.name, Math.min(attemptsLeft, windowLeft));
|
|
1668
|
+
}
|
|
1669
|
+
await context.store.users.updateUser(user.id, { secondFactor: accepted, updatedAt: now }, user.version);
|
|
1670
|
+
await spend(context, secret, where);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
// src/auth/step-up/open.ts
|
|
1674
|
+
async function openStepUp(context, type, request, token, secret, where) {
|
|
1675
|
+
const session = (await standingSession(context, request))?.session ?? null;
|
|
1676
|
+
if (session === null || session.userId !== token.userId) {
|
|
1677
|
+
throw await unknownChallenge(context, token, secret, where);
|
|
1678
|
+
}
|
|
1679
|
+
const user = await findRecord(context, token.userId, type.name);
|
|
1680
|
+
if (user === null) {
|
|
1681
|
+
throw await unknownChallenge(context, token, secret, where);
|
|
1682
|
+
}
|
|
1683
|
+
if (!user.active) {
|
|
1684
|
+
await spendOneTime(context, secret, "stepUp", where, "challenge");
|
|
1685
|
+
throw new UserInactiveError2(`${where}: the user is inactive`, {
|
|
1686
|
+
userId: user.id,
|
|
1687
|
+
userType: type.name
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
return { session, user };
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
// src/auth/step-up/request.ts
|
|
1694
|
+
async function requestStepUp(context, type, user, where) {
|
|
1695
|
+
const record = await getRecord(context, idOf(user), type.name, where);
|
|
1696
|
+
if (!record.active) {
|
|
1697
|
+
throw new UserInactiveError2(`${where}: the user is inactive`, {
|
|
1698
|
+
userId: record.id,
|
|
1699
|
+
userType: type.name
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
const issued = isActive(record.secondFactor) ? await byApp(context, record, where) : await byEmail(context, type, record, where);
|
|
1703
|
+
await context.store.tokens.spendUserTokens(record.id, "stepUp", context.clock.now(), hashSecret(issued.challenge));
|
|
1704
|
+
return issued;
|
|
1705
|
+
}
|
|
1706
|
+
async function byApp(context, record, where) {
|
|
1707
|
+
requireSettings(context, where, "the user's second factor is active");
|
|
1708
|
+
const { secret, expiresAt } = await issueOneTime(context, {
|
|
1709
|
+
kind: "stepUp",
|
|
1710
|
+
userId: record.id,
|
|
1711
|
+
address: "",
|
|
1712
|
+
ttlMs: context.config.tokenTtlMs.stepUp
|
|
1713
|
+
});
|
|
1714
|
+
return {
|
|
1715
|
+
via: "secondFactor",
|
|
1716
|
+
challenge: secret,
|
|
1717
|
+
expiresAt,
|
|
1718
|
+
user: toUser(record)
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
async function byEmail(context, type, record, where) {
|
|
1722
|
+
const email = emailOf(type, record.fields);
|
|
1723
|
+
if (email === null) {
|
|
1724
|
+
throw new NotFoundError2(`${where}: the user has no e-mail`, {
|
|
1725
|
+
userId: record.id,
|
|
1726
|
+
userType: type.name,
|
|
1727
|
+
operation: where
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
const { secret, code, expiresAt } = await issueCode(context, {
|
|
1731
|
+
kind: "stepUp",
|
|
1732
|
+
userId: record.id,
|
|
1733
|
+
address: email,
|
|
1734
|
+
ttlMs: context.config.tokenTtlMs.stepUp
|
|
1735
|
+
});
|
|
1736
|
+
return {
|
|
1737
|
+
via: "email",
|
|
1738
|
+
code,
|
|
1739
|
+
challenge: secret,
|
|
1740
|
+
email,
|
|
1741
|
+
expiresAt,
|
|
1742
|
+
user: toUser(record)
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// src/auth/step-up/flows.ts
|
|
1747
|
+
function stepUpFlows(context, type, at) {
|
|
1748
|
+
return {
|
|
1749
|
+
async request(user) {
|
|
1750
|
+
return requestStepUp(context, type, user, at("stepUp.request"));
|
|
1751
|
+
},
|
|
1752
|
+
async confirm(request, challenge, code) {
|
|
1753
|
+
return confirmStepUp(context, type, request, String(challenge), String(code), at("stepUp.confirm"));
|
|
1754
|
+
}
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
async function confirmStepUp(context, type, request, secret, code, where) {
|
|
1758
|
+
const { token, attemptsLeft } = await countCodeAttempt(context, secret, "stepUp", where, type.name);
|
|
1759
|
+
const { session, user } = await openStepUp(context, type, request, token, secret, where);
|
|
1760
|
+
const counted = { token, secret, attemptsLeft };
|
|
1761
|
+
if (token.codeHash === null) {
|
|
1762
|
+
await checkAppCode(context, type, user, counted, code, where);
|
|
1763
|
+
} else {
|
|
1764
|
+
await checkEmailedCode(context, type, user, counted, code, where);
|
|
1765
|
+
}
|
|
1766
|
+
const confirmed = await context.store.sessions.reauthenticateSession(session.id, context.clock.now());
|
|
1767
|
+
if (confirmed === null) {
|
|
1768
|
+
throw new TokenError2("TOKEN_UNKNOWN", `${where}: the session was signed out while the code was checked`, { operation: where });
|
|
1769
|
+
}
|
|
1770
|
+
return toSession(confirmed);
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1619
1773
|
// src/auth/users/by-login.ts
|
|
1620
1774
|
var byLogin = async (context, type, login) => isStorable(login) ? context.store.users.findUserByLogin(type.name, login) : null;
|
|
1621
1775
|
|
|
@@ -1829,6 +1983,7 @@ function typeApi(context, type) {
|
|
|
1829
1983
|
...passwordFlows(context, type, at, secondFactor.finish),
|
|
1830
1984
|
secondFactor: secondFactor.api,
|
|
1831
1985
|
signInCode,
|
|
1986
|
+
stepUp: stepUpFlows(context, type, at),
|
|
1832
1987
|
...emailFlows(context, type, at)
|
|
1833
1988
|
};
|
|
1834
1989
|
}
|
|
@@ -1865,6 +2020,14 @@ function janus(config) {
|
|
|
1865
2020
|
const surface = resolved.single ? { ...shared, ...types.user } : { ...shared, ...types };
|
|
1866
2021
|
return surface;
|
|
1867
2022
|
}
|
|
2023
|
+
// src/auth/sessions/fresh.ts
|
|
2024
|
+
function assertFresh(session, maxAge, clock = systemClock) {
|
|
2025
|
+
const maxAgeMs = parseDuration(maxAge, "assertFresh: maxAge");
|
|
2026
|
+
const age = clock.now().getTime() - session.authenticatedAt.getTime();
|
|
2027
|
+
if (age >= maxAgeMs) {
|
|
2028
|
+
throw new StepUpRequiredError2("assertFresh: the session proved who it is longer ago than maxAge — confirm with a step-up", { userId: session.userId });
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
1868
2031
|
export {
|
|
1869
2032
|
CredentialError2 as CredentialError,
|
|
1870
2033
|
DEFAULT_PAGE_SIZE2 as DEFAULT_PAGE_SIZE,
|
|
@@ -1874,12 +2037,14 @@ export {
|
|
|
1874
2037
|
NotFoundError2 as NotFoundError,
|
|
1875
2038
|
PermissionDepthError2 as PermissionDepthError,
|
|
1876
2039
|
SecondFactorError2 as SecondFactorError,
|
|
2040
|
+
StepUpRequiredError2 as StepUpRequiredError,
|
|
1877
2041
|
StoreConflict2 as StoreConflict,
|
|
1878
2042
|
StoreFailure2 as StoreFailure,
|
|
1879
2043
|
TokenError2 as TokenError,
|
|
1880
2044
|
UnsupportedError2 as UnsupportedError,
|
|
1881
2045
|
UserInactiveError2 as UserInactiveError,
|
|
1882
2046
|
UserInvalidError2 as UserInvalidError,
|
|
2047
|
+
assertFresh,
|
|
1883
2048
|
assertStores,
|
|
1884
2049
|
bunHasher,
|
|
1885
2050
|
createMemoryStores2 as createMemoryStores,
|
|
@@ -1905,5 +2070,5 @@ export {
|
|
|
1905
2070
|
systemClock
|
|
1906
2071
|
};
|
|
1907
2072
|
|
|
1908
|
-
//# debugId=
|
|
2073
|
+
//# debugId=301D5A6A554F106664756E2164756E21
|
|
1909
2074
|
//# sourceMappingURL=index.js.map
|