@digilogiclabs/platform-core 1.6.0 → 1.8.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/dist/auth.d.mts +174 -1
- package/dist/auth.d.ts +174 -1
- package/dist/auth.js +233 -3
- package/dist/auth.js.map +1 -1
- package/dist/auth.mjs +220 -2
- package/dist/auth.mjs.map +1 -1
- package/dist/{index-CkyVz0hQ.d.mts → env-jqNJdZVt.d.mts} +360 -2
- package/dist/{index-CkyVz0hQ.d.ts → env-jqNJdZVt.d.ts} +360 -2
- package/dist/{index-CepDdu7h.d.mts → index-DzQ0Js5Z.d.mts} +13 -1
- package/dist/{index-CepDdu7h.d.ts → index-DzQ0Js5Z.d.ts} +13 -1
- package/dist/index.d.mts +98 -3
- package/dist/index.d.ts +98 -3
- package/dist/index.js +969 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +955 -15
- package/dist/index.mjs.map +1 -1
- package/dist/migrations/index.d.mts +1 -1
- package/dist/migrations/index.d.ts +1 -1
- package/dist/migrations/index.js +72 -1
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +72 -1
- package/dist/migrations/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/auth.js
CHANGED
|
@@ -50,20 +50,27 @@ __export(auth_exports, {
|
|
|
50
50
|
checkEnvVars: () => checkEnvVars,
|
|
51
51
|
checkRateLimit: () => checkRateLimit,
|
|
52
52
|
classifyError: () => classifyError,
|
|
53
|
+
clearStoredBetaCode: () => clearStoredBetaCode,
|
|
53
54
|
constantTimeEqual: () => constantTimeEqual,
|
|
54
55
|
containsHtml: () => containsHtml,
|
|
55
56
|
containsUrls: () => containsUrls,
|
|
56
57
|
createAuditActor: () => createAuditActor,
|
|
57
58
|
createAuditLogger: () => createAuditLogger,
|
|
59
|
+
createBetaClient: () => createBetaClient,
|
|
58
60
|
createFeatureFlags: () => createFeatureFlags,
|
|
59
61
|
createMemoryRateLimitStore: () => createMemoryRateLimitStore,
|
|
62
|
+
createRedisRateLimitStore: () => createRedisRateLimitStore,
|
|
60
63
|
createSafeTextSchema: () => createSafeTextSchema,
|
|
61
64
|
detectStage: () => detectStage,
|
|
65
|
+
enforceRateLimit: () => enforceRateLimit,
|
|
66
|
+
errorResponse: () => errorResponse,
|
|
62
67
|
escapeHtml: () => escapeHtml,
|
|
63
68
|
extractAuditIp: () => extractAuditIp,
|
|
64
69
|
extractAuditRequestId: () => extractAuditRequestId,
|
|
65
70
|
extractAuditUserAgent: () => extractAuditUserAgent,
|
|
71
|
+
extractBearerToken: () => extractBearerToken,
|
|
66
72
|
extractClientIp: () => extractClientIp,
|
|
73
|
+
fetchBetaSettings: () => fetchBetaSettings,
|
|
67
74
|
getBoolEnv: () => getBoolEnv,
|
|
68
75
|
getCorrelationId: () => getCorrelationId,
|
|
69
76
|
getEndSessionEndpoint: () => getEndSessionEndpoint,
|
|
@@ -72,6 +79,7 @@ __export(auth_exports, {
|
|
|
72
79
|
getOptionalEnv: () => getOptionalEnv,
|
|
73
80
|
getRateLimitStatus: () => getRateLimitStatus,
|
|
74
81
|
getRequiredEnv: () => getRequiredEnv,
|
|
82
|
+
getStoredBetaCode: () => getStoredBetaCode,
|
|
75
83
|
getTokenEndpoint: () => getTokenEndpoint,
|
|
76
84
|
hasAllRoles: () => hasAllRoles,
|
|
77
85
|
hasAnyRole: () => hasAnyRole,
|
|
@@ -79,14 +87,18 @@ __export(auth_exports, {
|
|
|
79
87
|
isAllowlisted: () => isAllowlisted,
|
|
80
88
|
isApiError: () => isApiError,
|
|
81
89
|
isTokenExpired: () => isTokenExpired,
|
|
90
|
+
isValidBearerToken: () => isValidBearerToken,
|
|
82
91
|
parseKeycloakRoles: () => parseKeycloakRoles,
|
|
83
92
|
refreshKeycloakToken: () => refreshKeycloakToken,
|
|
84
93
|
resetRateLimitForKey: () => resetRateLimitForKey,
|
|
85
94
|
resolveIdentifier: () => resolveIdentifier,
|
|
86
95
|
resolveRateLimitIdentifier: () => resolveRateLimitIdentifier,
|
|
87
96
|
sanitizeApiError: () => sanitizeApiError,
|
|
97
|
+
storeBetaCode: () => storeBetaCode,
|
|
88
98
|
stripHtml: () => stripHtml,
|
|
89
|
-
|
|
99
|
+
validateBetaCode: () => validateBetaCode,
|
|
100
|
+
validateEnvVars: () => validateEnvVars,
|
|
101
|
+
zodErrorResponse: () => zodErrorResponse
|
|
90
102
|
});
|
|
91
103
|
module.exports = __toCommonJS(auth_exports);
|
|
92
104
|
|
|
@@ -260,7 +272,11 @@ function buildKeycloakCallbacks(config) {
|
|
|
260
272
|
* Compatible with Auth.js v5 JWT callback signature.
|
|
261
273
|
*/
|
|
262
274
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
263
|
-
async jwt({
|
|
275
|
+
async jwt({
|
|
276
|
+
token,
|
|
277
|
+
user,
|
|
278
|
+
account
|
|
279
|
+
}) {
|
|
264
280
|
if (user) {
|
|
265
281
|
token.id = token.sub ?? user.id;
|
|
266
282
|
}
|
|
@@ -821,6 +837,44 @@ function resolveIdentifier(session, clientIp) {
|
|
|
821
837
|
return { identifier: `ip:${clientIp ?? "unknown"}`, isAuthenticated: false };
|
|
822
838
|
}
|
|
823
839
|
|
|
840
|
+
// src/auth/rate-limit-store-redis.ts
|
|
841
|
+
function createRedisRateLimitStore(redis, options = {}) {
|
|
842
|
+
const prefix = options.keyPrefix ?? "";
|
|
843
|
+
return {
|
|
844
|
+
async increment(key, windowMs, now) {
|
|
845
|
+
const fullKey = `${prefix}${key}`;
|
|
846
|
+
const windowStart = now - windowMs;
|
|
847
|
+
const windowSeconds = Math.ceil(windowMs / 1e3) + 60;
|
|
848
|
+
await redis.zremrangebyscore(fullKey, 0, windowStart);
|
|
849
|
+
const current = await redis.zcard(fullKey);
|
|
850
|
+
const member = `${now}:${Math.random().toString(36).slice(2, 10)}`;
|
|
851
|
+
await redis.zadd(fullKey, now, member);
|
|
852
|
+
await redis.expire(fullKey, windowSeconds);
|
|
853
|
+
return { count: current + 1 };
|
|
854
|
+
},
|
|
855
|
+
async isBlocked(key) {
|
|
856
|
+
const fullKey = `${prefix}${key}`;
|
|
857
|
+
const value = await redis.get(fullKey);
|
|
858
|
+
if (!value) {
|
|
859
|
+
return { blocked: false, ttlMs: 0 };
|
|
860
|
+
}
|
|
861
|
+
const ttlSeconds = await redis.ttl(fullKey);
|
|
862
|
+
if (ttlSeconds <= 0) {
|
|
863
|
+
return { blocked: false, ttlMs: 0 };
|
|
864
|
+
}
|
|
865
|
+
return { blocked: true, ttlMs: ttlSeconds * 1e3 };
|
|
866
|
+
},
|
|
867
|
+
async setBlock(key, durationSeconds) {
|
|
868
|
+
const fullKey = `${prefix}${key}`;
|
|
869
|
+
await redis.setex(fullKey, durationSeconds, "1");
|
|
870
|
+
},
|
|
871
|
+
async reset(key) {
|
|
872
|
+
const fullKey = `${prefix}${key}`;
|
|
873
|
+
await redis.del(fullKey);
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
|
|
824
878
|
// src/auth/audit.ts
|
|
825
879
|
var StandardAuditActions = {
|
|
826
880
|
// Authentication
|
|
@@ -1105,6 +1159,170 @@ function buildPagination(page, limit, total) {
|
|
|
1105
1159
|
};
|
|
1106
1160
|
}
|
|
1107
1161
|
|
|
1162
|
+
// src/auth/nextjs-api.ts
|
|
1163
|
+
async function enforceRateLimit(request, operation, rule, options) {
|
|
1164
|
+
const identifier = options?.identifier ?? (options?.userId ? `user:${options.userId}` : void 0) ?? `ip:${extractClientIp((name) => request.headers.get(name))}`;
|
|
1165
|
+
const isAuthenticated = !!options?.userId;
|
|
1166
|
+
const result = await checkRateLimit(operation, identifier, rule, {
|
|
1167
|
+
...options?.rateLimitOptions,
|
|
1168
|
+
isAuthenticated
|
|
1169
|
+
});
|
|
1170
|
+
if (!result.allowed) {
|
|
1171
|
+
const headers = buildRateLimitResponseHeaders(result);
|
|
1172
|
+
return new Response(
|
|
1173
|
+
JSON.stringify({
|
|
1174
|
+
error: "Rate limit exceeded. Please try again later.",
|
|
1175
|
+
retryAfter: result.retryAfterSeconds
|
|
1176
|
+
}),
|
|
1177
|
+
{
|
|
1178
|
+
status: 429,
|
|
1179
|
+
headers: { "Content-Type": "application/json", ...headers }
|
|
1180
|
+
}
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
function errorResponse(error, options) {
|
|
1186
|
+
const isDev = options?.isDevelopment ?? process.env.NODE_ENV === "development";
|
|
1187
|
+
const { status, body } = classifyError(error, isDev);
|
|
1188
|
+
return new Response(JSON.stringify(body), {
|
|
1189
|
+
status,
|
|
1190
|
+
headers: { "Content-Type": "application/json" }
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
function zodErrorResponse(error) {
|
|
1194
|
+
const firstIssue = error.issues[0];
|
|
1195
|
+
const message = firstIssue ? `${firstIssue.path.join(".") || "input"}: ${firstIssue.message}` : "Validation error";
|
|
1196
|
+
return new Response(JSON.stringify({ error: message }), {
|
|
1197
|
+
status: 400,
|
|
1198
|
+
headers: { "Content-Type": "application/json" }
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
function extractBearerToken(request) {
|
|
1202
|
+
const auth = request.headers.get("authorization");
|
|
1203
|
+
if (!auth?.startsWith("Bearer ")) return null;
|
|
1204
|
+
return auth.slice(7).trim() || null;
|
|
1205
|
+
}
|
|
1206
|
+
function isValidBearerToken(request, secret) {
|
|
1207
|
+
if (!secret) return false;
|
|
1208
|
+
const token = extractBearerToken(request);
|
|
1209
|
+
if (!token) return false;
|
|
1210
|
+
return constantTimeEqual(token, secret);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// src/auth/beta-client.ts
|
|
1214
|
+
var DEFAULT_CONFIG = {
|
|
1215
|
+
baseUrl: "",
|
|
1216
|
+
settingsEndpoint: "/api/beta-settings",
|
|
1217
|
+
validateEndpoint: "/api/validate-beta-code",
|
|
1218
|
+
storageKey: "beta_code",
|
|
1219
|
+
failSafeDefaults: {
|
|
1220
|
+
betaMode: true,
|
|
1221
|
+
requireInviteCode: true,
|
|
1222
|
+
betaMessage: ""
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
function createBetaClient(config = {}) {
|
|
1226
|
+
const cfg = {
|
|
1227
|
+
...DEFAULT_CONFIG,
|
|
1228
|
+
...config,
|
|
1229
|
+
failSafeDefaults: {
|
|
1230
|
+
...DEFAULT_CONFIG.failSafeDefaults,
|
|
1231
|
+
...config.failSafeDefaults
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
return {
|
|
1235
|
+
fetchSettings: () => fetchBetaSettings(cfg),
|
|
1236
|
+
validateCode: (code) => validateBetaCode(code, cfg),
|
|
1237
|
+
storeCode: (code) => storeBetaCode(code, cfg),
|
|
1238
|
+
getStoredCode: () => getStoredBetaCode(cfg),
|
|
1239
|
+
clearStoredCode: () => clearStoredBetaCode(cfg)
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
async function fetchBetaSettings(config = {}) {
|
|
1243
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
1244
|
+
try {
|
|
1245
|
+
const response = await fetch(
|
|
1246
|
+
`${cfg.baseUrl}${cfg.settingsEndpoint}`,
|
|
1247
|
+
{
|
|
1248
|
+
method: "GET",
|
|
1249
|
+
headers: { "Content-Type": "application/json" },
|
|
1250
|
+
cache: "no-store"
|
|
1251
|
+
}
|
|
1252
|
+
);
|
|
1253
|
+
if (!response.ok) {
|
|
1254
|
+
throw new Error(`Failed to fetch beta settings: ${response.status}`);
|
|
1255
|
+
}
|
|
1256
|
+
const data = await response.json();
|
|
1257
|
+
return {
|
|
1258
|
+
betaMode: data.betaMode ?? cfg.failSafeDefaults.betaMode ?? true,
|
|
1259
|
+
requireInviteCode: data.requireInviteCode ?? cfg.failSafeDefaults.requireInviteCode ?? true,
|
|
1260
|
+
betaMessage: data.betaMessage ?? cfg.failSafeDefaults.betaMessage ?? ""
|
|
1261
|
+
};
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
console.error("Error fetching beta settings:", error);
|
|
1264
|
+
return {
|
|
1265
|
+
betaMode: cfg.failSafeDefaults.betaMode ?? true,
|
|
1266
|
+
requireInviteCode: cfg.failSafeDefaults.requireInviteCode ?? true,
|
|
1267
|
+
betaMessage: cfg.failSafeDefaults.betaMessage ?? ""
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
async function validateBetaCode(code, config = {}) {
|
|
1272
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
1273
|
+
if (!code || code.trim().length < 3) {
|
|
1274
|
+
return {
|
|
1275
|
+
valid: false,
|
|
1276
|
+
message: "Please enter a valid invite code."
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
try {
|
|
1280
|
+
const response = await fetch(
|
|
1281
|
+
`${cfg.baseUrl}${cfg.validateEndpoint}`,
|
|
1282
|
+
{
|
|
1283
|
+
method: "POST",
|
|
1284
|
+
headers: { "Content-Type": "application/json" },
|
|
1285
|
+
body: JSON.stringify({ code: code.trim().toUpperCase() })
|
|
1286
|
+
}
|
|
1287
|
+
);
|
|
1288
|
+
if (response.status === 429) {
|
|
1289
|
+
return {
|
|
1290
|
+
valid: false,
|
|
1291
|
+
message: "Too many attempts. Please try again later."
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
if (!response.ok) {
|
|
1295
|
+
throw new Error(`Validation request failed: ${response.status}`);
|
|
1296
|
+
}
|
|
1297
|
+
return await response.json();
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
console.error("Error validating invite code:", error);
|
|
1300
|
+
return {
|
|
1301
|
+
valid: false,
|
|
1302
|
+
message: "Unable to validate code. Please try again."
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
function storeBetaCode(code, config = {}) {
|
|
1307
|
+
const key = config.storageKey ?? DEFAULT_CONFIG.storageKey;
|
|
1308
|
+
if (typeof window !== "undefined") {
|
|
1309
|
+
sessionStorage.setItem(key, code.trim().toUpperCase());
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function getStoredBetaCode(config = {}) {
|
|
1313
|
+
const key = config.storageKey ?? DEFAULT_CONFIG.storageKey;
|
|
1314
|
+
if (typeof window !== "undefined") {
|
|
1315
|
+
return sessionStorage.getItem(key);
|
|
1316
|
+
}
|
|
1317
|
+
return null;
|
|
1318
|
+
}
|
|
1319
|
+
function clearStoredBetaCode(config = {}) {
|
|
1320
|
+
const key = config.storageKey ?? DEFAULT_CONFIG.storageKey;
|
|
1321
|
+
if (typeof window !== "undefined") {
|
|
1322
|
+
sessionStorage.removeItem(key);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1108
1326
|
// src/env.ts
|
|
1109
1327
|
function getRequiredEnv(key) {
|
|
1110
1328
|
const value = process.env[key];
|
|
@@ -1227,20 +1445,27 @@ function getEnvSummary(keys) {
|
|
|
1227
1445
|
checkEnvVars,
|
|
1228
1446
|
checkRateLimit,
|
|
1229
1447
|
classifyError,
|
|
1448
|
+
clearStoredBetaCode,
|
|
1230
1449
|
constantTimeEqual,
|
|
1231
1450
|
containsHtml,
|
|
1232
1451
|
containsUrls,
|
|
1233
1452
|
createAuditActor,
|
|
1234
1453
|
createAuditLogger,
|
|
1454
|
+
createBetaClient,
|
|
1235
1455
|
createFeatureFlags,
|
|
1236
1456
|
createMemoryRateLimitStore,
|
|
1457
|
+
createRedisRateLimitStore,
|
|
1237
1458
|
createSafeTextSchema,
|
|
1238
1459
|
detectStage,
|
|
1460
|
+
enforceRateLimit,
|
|
1461
|
+
errorResponse,
|
|
1239
1462
|
escapeHtml,
|
|
1240
1463
|
extractAuditIp,
|
|
1241
1464
|
extractAuditRequestId,
|
|
1242
1465
|
extractAuditUserAgent,
|
|
1466
|
+
extractBearerToken,
|
|
1243
1467
|
extractClientIp,
|
|
1468
|
+
fetchBetaSettings,
|
|
1244
1469
|
getBoolEnv,
|
|
1245
1470
|
getCorrelationId,
|
|
1246
1471
|
getEndSessionEndpoint,
|
|
@@ -1249,6 +1474,7 @@ function getEnvSummary(keys) {
|
|
|
1249
1474
|
getOptionalEnv,
|
|
1250
1475
|
getRateLimitStatus,
|
|
1251
1476
|
getRequiredEnv,
|
|
1477
|
+
getStoredBetaCode,
|
|
1252
1478
|
getTokenEndpoint,
|
|
1253
1479
|
hasAllRoles,
|
|
1254
1480
|
hasAnyRole,
|
|
@@ -1256,13 +1482,17 @@ function getEnvSummary(keys) {
|
|
|
1256
1482
|
isAllowlisted,
|
|
1257
1483
|
isApiError,
|
|
1258
1484
|
isTokenExpired,
|
|
1485
|
+
isValidBearerToken,
|
|
1259
1486
|
parseKeycloakRoles,
|
|
1260
1487
|
refreshKeycloakToken,
|
|
1261
1488
|
resetRateLimitForKey,
|
|
1262
1489
|
resolveIdentifier,
|
|
1263
1490
|
resolveRateLimitIdentifier,
|
|
1264
1491
|
sanitizeApiError,
|
|
1492
|
+
storeBetaCode,
|
|
1265
1493
|
stripHtml,
|
|
1266
|
-
|
|
1494
|
+
validateBetaCode,
|
|
1495
|
+
validateEnvVars,
|
|
1496
|
+
zodErrorResponse
|
|
1267
1497
|
});
|
|
1268
1498
|
//# sourceMappingURL=auth.js.map
|