@fonderie/auth 7.17.1 → 7.18.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/brain/outcomes.md +1 -0
- package/brain/signatures.md +23 -1
- package/dist/index.cjs +107 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +101 -17
- package/dist/index.js.map +1 -1
- package/dist/middlewares/index.cjs +12 -7
- package/dist/middlewares/index.cjs.map +1 -1
- package/dist/middlewares/index.d.cts +1 -1
- package/dist/middlewares/index.d.ts +1 -1
- package/dist/middlewares/index.js +12 -7
- package/dist/middlewares/index.js.map +1 -1
- package/dist/migrations/sql/021_session_client_kind.sql +6 -0
- package/dist/{session-C-ptRylj.d.cts → session-B-IgXFu8.d.cts} +32 -2
- package/dist/{session-C-ptRylj.d.ts → session-B-IgXFu8.d.ts} +32 -2
- package/package.json +1 -1
package/brain/outcomes.md
CHANGED
package/brain/signatures.md
CHANGED
|
@@ -93,6 +93,7 @@ interface IAuthSecrets {
|
|
|
93
93
|
interface IAuthRuntimeConfig {
|
|
94
94
|
sessionDuration?: string;
|
|
95
95
|
sessionMaxAge?: string;
|
|
96
|
+
sessionPolicies?: Partial<Record<ClientKind, ISessionPolicy>>;
|
|
96
97
|
verificationCooldown?: number;
|
|
97
98
|
mfa?: boolean;
|
|
98
99
|
requireVerification?: boolean;
|
|
@@ -103,7 +104,7 @@ interface IDataExportContributor {
|
|
|
103
104
|
collect: (userId: string) => Promise<unknown> | unknown;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
const AUTH_CONFIG_KEYS: Record<keyof IAuthRuntimeConfig, string>
|
|
107
|
+
const AUTH_CONFIG_KEYS: Record<Exclude<keyof IAuthRuntimeConfig, 'sessionPolicies'>, string>
|
|
107
108
|
|
|
108
109
|
const MESSAGE_KEYS: { readonly emailRegistration: "email-registration"; readonly emailVerification: "email-verification"; readonly passwordReset: "password-reset"; readonly phoneOtp: "phone-otp"; readonly mfaEnabled: "mfa-enabled"; readonly mfaDisabled: "mfa-disabled"; readonly mfaBackupCodesRegenerated: "mfa-backup-codes-regenerated"; readonly emailChanged: "email-changed"; readonly phoneChanged: "phone-changed"; readonly passwordRevoked: "password-revoked"; readonly oauthRegistration: "oauth-registration"; readonly oauthLinked: "oauth-linked"; readonly oauthUnlinked: "oauth-unlinked"; }
|
|
109
110
|
|
|
@@ -159,6 +160,7 @@ interface ISessionDTO {
|
|
|
159
160
|
location: IRequestLocation | null;
|
|
160
161
|
createdAt: string;
|
|
161
162
|
expiresAt: string;
|
|
163
|
+
clientKind: string | null;
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
function sanitizeLocation(input: unknown): IRequestLocation | null
|
|
@@ -270,4 +272,24 @@ function describeAuthAdminRoutes(store: IStoreAdapter): IAdminRoute[]
|
|
|
270
272
|
function toAdminUserDTO(user: IUser): IAdminUserDTO
|
|
271
273
|
|
|
272
274
|
function toAdminUserPageDTO(page: IUserPage): IAdminUserPageDTO
|
|
275
|
+
|
|
276
|
+
const CLIENT_KINDS: readonly ["mobile", "desktop", "web"]
|
|
277
|
+
|
|
278
|
+
const SESSION_POLICY_PRESETS: Readonly<Record<ClientKind, ISessionPolicy>>
|
|
279
|
+
|
|
280
|
+
function clientKindOf(headers: Headers): "mobile" | "desktop" | "web" | null
|
|
281
|
+
|
|
282
|
+
function configForClient(config: IAuthConfig, runtime: Partial<IAuthRuntimeConfig> | undefined, kind: "mobile" | "desktop" | "web" | null): IAuthConfig
|
|
283
|
+
|
|
284
|
+
function readAuthRuntimeConfig(read: (key: string) => unknown): Partial<IAuthRuntimeConfig>
|
|
285
|
+
|
|
286
|
+
function sessionPolicyConfigKeys(kind: "mobile" | "desktop" | "web"): Record<keyof ISessionPolicy, string>
|
|
287
|
+
|
|
288
|
+
type ClientKind = (typeof CLIENT_KINDS)[number];
|
|
289
|
+
|
|
290
|
+
interface ISessionPolicy {
|
|
291
|
+
sessionDuration?: string;
|
|
292
|
+
sessionMaxAge?: string;
|
|
293
|
+
accessTokenDuration?: string;
|
|
294
|
+
}
|
|
273
295
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -70,19 +70,25 @@ var index_exports = {};
|
|
|
70
70
|
__export(index_exports, {
|
|
71
71
|
AUTH_CONFIG_KEYS: () => AUTH_CONFIG_KEYS,
|
|
72
72
|
AuthModule: () => AuthModule,
|
|
73
|
+
CLIENT_KINDS: () => CLIENT_KINDS,
|
|
73
74
|
DEFAULT_TEMPLATES: () => DEFAULT_TEMPLATES,
|
|
74
75
|
MESSAGE_KEYS: () => MESSAGE_KEYS,
|
|
76
|
+
SESSION_POLICY_PRESETS: () => SESSION_POLICY_PRESETS,
|
|
75
77
|
buildAuthAccountLimiter: () => buildAuthAccountLimiter,
|
|
76
78
|
buildAuthIpLimiter: () => buildAuthIpLimiter,
|
|
79
|
+
clientKindOf: () => clientKindOf,
|
|
80
|
+
configForClient: () => configForClient,
|
|
77
81
|
describeAuthAdminRoutes: () => describeAuthAdminRoutes,
|
|
78
82
|
importUser: () => importUser,
|
|
79
83
|
normalizeEmail: () => normalizeEmail,
|
|
80
84
|
normalizeEmailSafe: () => normalizeEmailSafe,
|
|
81
85
|
purgeSoftDeletedUsers: () => purgeSoftDeletedUsers,
|
|
86
|
+
readAuthRuntimeConfig: () => readAuthRuntimeConfig,
|
|
82
87
|
requireAuth: () => import_middlewares3.requireAuth,
|
|
83
88
|
resolveLocation: () => resolveLocation,
|
|
84
89
|
sanitizeLocation: () => sanitizeLocation,
|
|
85
90
|
schemas: () => schemas_exports,
|
|
91
|
+
sessionPolicyConfigKeys: () => sessionPolicyConfigKeys,
|
|
86
92
|
startUserRetention: () => startUserRetention,
|
|
87
93
|
toAdminUserDTO: () => toAdminUserDTO,
|
|
88
94
|
toAdminUserPageDTO: () => toAdminUserPageDTO,
|
|
@@ -413,6 +419,79 @@ function validDuration(value, fallback, name) {
|
|
|
413
419
|
return fallback;
|
|
414
420
|
}
|
|
415
421
|
|
|
422
|
+
// src/services/session-policy.ts
|
|
423
|
+
var CLIENT_KINDS = ["mobile", "desktop", "web"];
|
|
424
|
+
var SESSION_POLICY_PRESETS = {
|
|
425
|
+
// Personal device; the token lives in the Keychain / Keystore.
|
|
426
|
+
mobile: { sessionDuration: "90d", sessionMaxAge: "365d" },
|
|
427
|
+
// Personal, but often a shared workstation.
|
|
428
|
+
desktop: { sessionDuration: "30d", sessionMaxAge: "180d" },
|
|
429
|
+
// Shared and public computers; cookies.
|
|
430
|
+
web: { sessionDuration: "14d", sessionMaxAge: "90d" }
|
|
431
|
+
};
|
|
432
|
+
function clientKindOf(headers) {
|
|
433
|
+
const v = headers.get("x-client-kind")?.trim().toLowerCase();
|
|
434
|
+
return CLIENT_KINDS.includes(v ?? "") ? v : null;
|
|
435
|
+
}
|
|
436
|
+
var FIELDS = ["sessionDuration", "sessionMaxAge", "accessTokenDuration"];
|
|
437
|
+
function configForClient(config, runtime, kind) {
|
|
438
|
+
const rt = runtime ?? {};
|
|
439
|
+
const code = config;
|
|
440
|
+
const out = { ...config, ...rt };
|
|
441
|
+
for (const field of FIELDS) {
|
|
442
|
+
const value = (kind ? rt.sessionPolicies?.[kind]?.[field] : void 0) ?? rt[field] ?? (kind ? code.sessionPolicies?.[kind]?.[field] : void 0) ?? code[field] ?? (kind ? SESSION_POLICY_PRESETS[kind][field] : void 0);
|
|
443
|
+
if (value === void 0) delete out[field];
|
|
444
|
+
else out[field] = value;
|
|
445
|
+
}
|
|
446
|
+
return out;
|
|
447
|
+
}
|
|
448
|
+
function sessionPolicyConfigKeys(kind) {
|
|
449
|
+
return {
|
|
450
|
+
sessionDuration: `auth.session.duration.${kind}`,
|
|
451
|
+
sessionMaxAge: `auth.session.max_age.${kind}`,
|
|
452
|
+
accessTokenDuration: `auth.access.duration.${kind}`
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function readAuthRuntimeConfig(read) {
|
|
456
|
+
const text = (key) => {
|
|
457
|
+
const v = read(key);
|
|
458
|
+
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
459
|
+
};
|
|
460
|
+
const bool2 = (key) => {
|
|
461
|
+
const v = read(key);
|
|
462
|
+
if (typeof v === "boolean") return v;
|
|
463
|
+
if (typeof v === "string" && ["true", "false"].includes(v.trim().toLowerCase())) return v.trim().toLowerCase() === "true";
|
|
464
|
+
return void 0;
|
|
465
|
+
};
|
|
466
|
+
const num = (key) => {
|
|
467
|
+
const v = read(key);
|
|
468
|
+
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() ? Number(v) : NaN;
|
|
469
|
+
return Number.isFinite(n) ? n : void 0;
|
|
470
|
+
};
|
|
471
|
+
const out = {};
|
|
472
|
+
const set = (k, v) => {
|
|
473
|
+
if (v !== void 0) out[k] = v;
|
|
474
|
+
};
|
|
475
|
+
set("sessionDuration", text("auth.session.duration"));
|
|
476
|
+
set("sessionMaxAge", text("auth.session.max_age"));
|
|
477
|
+
set("accessTokenDuration", text("auth.access.duration"));
|
|
478
|
+
set("verificationCooldown", num("auth.verification.cooldown"));
|
|
479
|
+
set("mfa", bool2("auth.mfa.enabled"));
|
|
480
|
+
set("requireVerification", bool2("auth.verification.required"));
|
|
481
|
+
const policies = {};
|
|
482
|
+
for (const kind of CLIENT_KINDS) {
|
|
483
|
+
const keys = sessionPolicyConfigKeys(kind);
|
|
484
|
+
const policy = {};
|
|
485
|
+
for (const field of FIELDS) {
|
|
486
|
+
const v = text(keys[field]);
|
|
487
|
+
if (v !== void 0) policy[field] = v;
|
|
488
|
+
}
|
|
489
|
+
if (Object.keys(policy).length) policies[kind] = policy;
|
|
490
|
+
}
|
|
491
|
+
if (Object.keys(policies).length) out.sessionPolicies = policies;
|
|
492
|
+
return out;
|
|
493
|
+
}
|
|
494
|
+
|
|
416
495
|
// src/services/mfa.ts
|
|
417
496
|
var import_node_crypto2 = require("crypto");
|
|
418
497
|
var import_core2 = require("@fonderie/core");
|
|
@@ -1036,8 +1115,8 @@ var SessionModel = class {
|
|
|
1036
1115
|
async create(userId, token, expiresAt, sid, meta) {
|
|
1037
1116
|
const location = this.locate && meta?.headers ? await resolveLocation(this.locate, { ip: meta.ipAddress, headers: meta.headers }) : null;
|
|
1038
1117
|
await this.store.query(
|
|
1039
|
-
`INSERT INTO fonderie_sessions (user_id, token, expires_at, sid, user_agent, ip_address, location, last_used_at)
|
|
1040
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, now())
|
|
1118
|
+
`INSERT INTO fonderie_sessions (user_id, token, expires_at, sid, user_agent, ip_address, location, last_used_at, client_kind)
|
|
1119
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, now(), $8)
|
|
1041
1120
|
ON CONFLICT (token) DO NOTHING`,
|
|
1042
1121
|
[
|
|
1043
1122
|
userId,
|
|
@@ -1046,7 +1125,8 @@ var SessionModel = class {
|
|
|
1046
1125
|
sid ?? null,
|
|
1047
1126
|
meta?.userAgent ?? null,
|
|
1048
1127
|
meta?.ipAddress ?? null,
|
|
1049
|
-
location ? JSON.stringify(location) : null
|
|
1128
|
+
location ? JSON.stringify(location) : null,
|
|
1129
|
+
meta?.clientKind ?? null
|
|
1050
1130
|
]
|
|
1051
1131
|
);
|
|
1052
1132
|
}
|
|
@@ -1061,12 +1141,12 @@ var SessionModel = class {
|
|
|
1061
1141
|
async match(token) {
|
|
1062
1142
|
const hash = hashRefreshToken(token);
|
|
1063
1143
|
const [current] = await this.store.query(
|
|
1064
|
-
`SELECT id, user_id, sid, token, created_at FROM fonderie_sessions WHERE token IN ($1, $2) AND expires_at > now() LIMIT 1`,
|
|
1144
|
+
`SELECT id, user_id, sid, token, created_at, client_kind FROM fonderie_sessions WHERE token IN ($1, $2) AND expires_at > now() LIMIT 1`,
|
|
1065
1145
|
[hash, token]
|
|
1066
1146
|
);
|
|
1067
1147
|
if (current) return { kind: "current", row: toRow(current) };
|
|
1068
1148
|
const [previous] = await this.store.query(
|
|
1069
|
-
`SELECT id, user_id, sid, token, created_at, (previous_valid_until > now()) AS in_grace
|
|
1149
|
+
`SELECT id, user_id, sid, token, created_at, client_kind, (previous_valid_until > now()) AS in_grace
|
|
1070
1150
|
FROM fonderie_sessions WHERE previous_token_hash = $1 AND expires_at > now() LIMIT 1`,
|
|
1071
1151
|
[hash]
|
|
1072
1152
|
);
|
|
@@ -1132,7 +1212,7 @@ var SessionModel = class {
|
|
|
1132
1212
|
// No token — that never leaves the server.
|
|
1133
1213
|
async listLiveByUser(userId) {
|
|
1134
1214
|
return this.store.query(
|
|
1135
|
-
`SELECT id, sid,
|
|
1215
|
+
`SELECT id, sid, client_kind AS "clientKind",
|
|
1136
1216
|
user_agent AS "userAgent",
|
|
1137
1217
|
ip_address AS "ipAddress",
|
|
1138
1218
|
location,
|
|
@@ -1181,7 +1261,8 @@ var SessionModel = class {
|
|
|
1181
1261
|
}
|
|
1182
1262
|
};
|
|
1183
1263
|
function toRow(r) {
|
|
1184
|
-
|
|
1264
|
+
const kind = CLIENT_KINDS.includes(r.client_kind ?? "") ? r.client_kind : null;
|
|
1265
|
+
return { id: r.id, userId: r.user_id, sid: r.sid, stored: r.token, createdAt: r.created_at ? new Date(r.created_at) : /* @__PURE__ */ new Date(), clientKind: kind };
|
|
1185
1266
|
}
|
|
1186
1267
|
|
|
1187
1268
|
// src/models/backup-code.model.ts
|
|
@@ -1312,7 +1393,8 @@ function requestMeta(ctx) {
|
|
|
1312
1393
|
const meta = {
|
|
1313
1394
|
ipAddress: typeof ip === "string" && ip.length > 0 ? ip : null,
|
|
1314
1395
|
userAgent: ua && ua.length > 0 ? ua.slice(0, UA_MAX) : null,
|
|
1315
|
-
headers: ctx.request.headers
|
|
1396
|
+
headers: ctx.request.headers,
|
|
1397
|
+
clientKind: clientKindOf(ctx.request.headers)
|
|
1316
1398
|
};
|
|
1317
1399
|
if (meta.ipAddress === null && meta.userAgent === null) warnIfIdentityless(ctx);
|
|
1318
1400
|
return meta;
|
|
@@ -1427,7 +1509,7 @@ function mfaController(store, config, issuer, bus) {
|
|
|
1427
1509
|
await users.enableMfa(ctx.user.id);
|
|
1428
1510
|
}
|
|
1429
1511
|
}
|
|
1430
|
-
const { accessToken, refreshToken, sid } = issueTokenPair(ctx.user.id, config, {
|
|
1512
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(ctx.user.id, configForClient(config, config.resolve?.(ctx), clientKindOf(ctx.request.headers)), {
|
|
1431
1513
|
loginMethod: ctx.user.loginMethod,
|
|
1432
1514
|
phoneVerified: ctx.user.phoneVerified
|
|
1433
1515
|
});
|
|
@@ -1789,7 +1871,7 @@ function authController(store, config, bus, locales = (0, import_core6.defineLoc
|
|
|
1789
1871
|
},
|
|
1790
1872
|
reqOpts
|
|
1791
1873
|
));
|
|
1792
|
-
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, config, {
|
|
1874
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, configForClient(config, config.resolve?.(ctx), clientKindOf(ctx.request.headers)), {
|
|
1793
1875
|
loginMethod: "email"
|
|
1794
1876
|
});
|
|
1795
1877
|
const registerMeta = requestMeta(ctx);
|
|
@@ -1932,7 +2014,7 @@ function authController(store, config, bus, locales = (0, import_core6.defineLoc
|
|
|
1932
2014
|
mfaToken
|
|
1933
2015
|
});
|
|
1934
2016
|
}
|
|
1935
|
-
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, config, {
|
|
2017
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, configForClient(config, config.resolve?.(ctx), clientKindOf(ctx.request.headers)), {
|
|
1936
2018
|
loginMethod: "email"
|
|
1937
2019
|
});
|
|
1938
2020
|
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid, meta);
|
|
@@ -2025,8 +2107,7 @@ function authController(store, config, bus, locales = (0, import_core6.defineLoc
|
|
|
2025
2107
|
if (!payload || payload.type !== "refresh") {
|
|
2026
2108
|
return (0, import_core6.setApiResponse)(import_core6.HTTP.UNAUTHORIZED, "TOKEN_REFRESH_FAILED", "Invalid refresh token");
|
|
2027
2109
|
}
|
|
2028
|
-
const
|
|
2029
|
-
const maxAgeMs = resolvedRefresh.sessionMaxAge ? durationMs(String(resolvedRefresh.sessionMaxAge)) : null;
|
|
2110
|
+
const runtime = config.resolve?.(ctx);
|
|
2030
2111
|
let issued = null;
|
|
2031
2112
|
for (let attempt = 0; attempt < 3 && !issued; attempt++) {
|
|
2032
2113
|
const found = await sessions.match(token);
|
|
@@ -2040,6 +2121,8 @@ function authController(store, config, bus, locales = (0, import_core6.defineLoc
|
|
|
2040
2121
|
);
|
|
2041
2122
|
return (0, import_core6.setApiResponse)(import_core6.HTTP.UNAUTHORIZED, "TOKEN_REFRESH_FAILED", "Session expired or already revoked");
|
|
2042
2123
|
}
|
|
2124
|
+
const resolvedRefresh = configForClient(config, runtime, found.row.clientKind);
|
|
2125
|
+
const maxAgeMs = resolvedRefresh.sessionMaxAge ? durationMs(String(resolvedRefresh.sessionMaxAge)) : null;
|
|
2043
2126
|
if (maxAgeMs !== null && Date.now() - found.row.createdAt.getTime() > maxAgeMs) {
|
|
2044
2127
|
await sessions.revokeById(found.row.id);
|
|
2045
2128
|
return (0, import_core6.setApiResponse)(import_core6.HTTP.UNAUTHORIZED, "TOKEN_REFRESH_FAILED", "Session reached its maximum age \u2014 sign in again");
|
|
@@ -2221,7 +2304,7 @@ function authController(store, config, bus, locales = (0, import_core6.defineLoc
|
|
|
2221
2304
|
);
|
|
2222
2305
|
}
|
|
2223
2306
|
await phoneVerif.deleteByUser(ctx.user.id);
|
|
2224
|
-
const { accessToken, refreshToken, sid } = issueTokenPair(ctx.user.id, config, {
|
|
2307
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(ctx.user.id, configForClient(config, config.resolve?.(ctx), clientKindOf(ctx.request.headers)), {
|
|
2225
2308
|
loginMethod: "phone",
|
|
2226
2309
|
phoneVerified: true
|
|
2227
2310
|
});
|
|
@@ -2398,6 +2481,7 @@ function toSessionDTO(row, currentSid) {
|
|
|
2398
2481
|
ipAddress: row.ipAddress,
|
|
2399
2482
|
userAgent: row.userAgent,
|
|
2400
2483
|
location: sanitizeLocation(row.location),
|
|
2484
|
+
clientKind: row.clientKind ?? null,
|
|
2401
2485
|
createdAt: row.createdAt instanceof Date ? row.createdAt.toISOString() : String(row.createdAt),
|
|
2402
2486
|
expiresAt: row.expiresAt instanceof Date ? row.expiresAt.toISOString() : String(row.expiresAt)
|
|
2403
2487
|
};
|
|
@@ -2946,7 +3030,7 @@ function oauthController(store, config, bus) {
|
|
|
2946
3030
|
return (0, import_core9.setApiResponse)(import_core9.HTTP.SERVER_ERROR, "SERVER_ERROR", "Apple login failed");
|
|
2947
3031
|
}
|
|
2948
3032
|
await announceOAuthUpsert(ctx, upserted, "apple", fullUser);
|
|
2949
|
-
const { accessToken, refreshToken, sid } = issueTokenPair(upserted.id, config, { loginMethod: "apple" });
|
|
3033
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(upserted.id, configForClient(config, config.resolve?.(ctx), clientKindOf(ctx.request.headers)), { loginMethod: "apple" });
|
|
2950
3034
|
await sessions.create(upserted.id, refreshToken, refreshTokenExpiry(refreshToken), sid, meta);
|
|
2951
3035
|
await loginEvents.recordSafe({
|
|
2952
3036
|
userId: upserted.id,
|
|
@@ -3097,7 +3181,7 @@ function oauthController(store, config, bus) {
|
|
|
3097
3181
|
return (0, import_core9.setApiResponse)(import_core9.HTTP.SERVER_ERROR, "SERVER_ERROR", "OAuth login failed");
|
|
3098
3182
|
}
|
|
3099
3183
|
await announceOAuthUpsert(ctx, upserted, "google", fullUser);
|
|
3100
|
-
const { accessToken, refreshToken, sid } = issueTokenPair(upserted.id, config, {
|
|
3184
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(upserted.id, configForClient(config, config.resolve?.(ctx), clientKindOf(ctx.request.headers)), {
|
|
3101
3185
|
loginMethod: "google"
|
|
3102
3186
|
});
|
|
3103
3187
|
await sessions.create(upserted.id, refreshToken, refreshTokenExpiry(refreshToken), sid, meta);
|
|
@@ -3685,7 +3769,7 @@ var AuthModule = class {
|
|
|
3685
3769
|
name = "@fonderie/auth";
|
|
3686
3770
|
// Baked in at build time by tsup.base, so the operator's Modules page can
|
|
3687
3771
|
// say what is actually deployed rather than 'not reported'.
|
|
3688
|
-
version = "7.
|
|
3772
|
+
version = "7.18.0";
|
|
3689
3773
|
// Users, sessions, login history, suspend — only through @fonderie/admin.
|
|
3690
3774
|
describeAdmin() {
|
|
3691
3775
|
return { routes: describeAuthAdminRoutes(this.store) };
|
|
@@ -4281,19 +4365,25 @@ function startUserRetention(store, options) {
|
|
|
4281
4365
|
0 && (module.exports = {
|
|
4282
4366
|
AUTH_CONFIG_KEYS,
|
|
4283
4367
|
AuthModule,
|
|
4368
|
+
CLIENT_KINDS,
|
|
4284
4369
|
DEFAULT_TEMPLATES,
|
|
4285
4370
|
MESSAGE_KEYS,
|
|
4371
|
+
SESSION_POLICY_PRESETS,
|
|
4286
4372
|
buildAuthAccountLimiter,
|
|
4287
4373
|
buildAuthIpLimiter,
|
|
4374
|
+
clientKindOf,
|
|
4375
|
+
configForClient,
|
|
4288
4376
|
describeAuthAdminRoutes,
|
|
4289
4377
|
importUser,
|
|
4290
4378
|
normalizeEmail,
|
|
4291
4379
|
normalizeEmailSafe,
|
|
4292
4380
|
purgeSoftDeletedUsers,
|
|
4381
|
+
readAuthRuntimeConfig,
|
|
4293
4382
|
requireAuth,
|
|
4294
4383
|
resolveLocation,
|
|
4295
4384
|
sanitizeLocation,
|
|
4296
4385
|
schemas,
|
|
4386
|
+
sessionPolicyConfigKeys,
|
|
4297
4387
|
startUserRetention,
|
|
4298
4388
|
toAdminUserDTO,
|
|
4299
4389
|
toAdminUserPageDTO,
|