@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.mjs
CHANGED
|
@@ -168,7 +168,11 @@ function buildKeycloakCallbacks(config) {
|
|
|
168
168
|
* Compatible with Auth.js v5 JWT callback signature.
|
|
169
169
|
*/
|
|
170
170
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
|
-
async jwt({
|
|
171
|
+
async jwt({
|
|
172
|
+
token,
|
|
173
|
+
user,
|
|
174
|
+
account
|
|
175
|
+
}) {
|
|
172
176
|
if (user) {
|
|
173
177
|
token.id = token.sub ?? user.id;
|
|
174
178
|
}
|
|
@@ -729,6 +733,44 @@ function resolveIdentifier(session, clientIp) {
|
|
|
729
733
|
return { identifier: `ip:${clientIp ?? "unknown"}`, isAuthenticated: false };
|
|
730
734
|
}
|
|
731
735
|
|
|
736
|
+
// src/auth/rate-limit-store-redis.ts
|
|
737
|
+
function createRedisRateLimitStore(redis, options = {}) {
|
|
738
|
+
const prefix = options.keyPrefix ?? "";
|
|
739
|
+
return {
|
|
740
|
+
async increment(key, windowMs, now) {
|
|
741
|
+
const fullKey = `${prefix}${key}`;
|
|
742
|
+
const windowStart = now - windowMs;
|
|
743
|
+
const windowSeconds = Math.ceil(windowMs / 1e3) + 60;
|
|
744
|
+
await redis.zremrangebyscore(fullKey, 0, windowStart);
|
|
745
|
+
const current = await redis.zcard(fullKey);
|
|
746
|
+
const member = `${now}:${Math.random().toString(36).slice(2, 10)}`;
|
|
747
|
+
await redis.zadd(fullKey, now, member);
|
|
748
|
+
await redis.expire(fullKey, windowSeconds);
|
|
749
|
+
return { count: current + 1 };
|
|
750
|
+
},
|
|
751
|
+
async isBlocked(key) {
|
|
752
|
+
const fullKey = `${prefix}${key}`;
|
|
753
|
+
const value = await redis.get(fullKey);
|
|
754
|
+
if (!value) {
|
|
755
|
+
return { blocked: false, ttlMs: 0 };
|
|
756
|
+
}
|
|
757
|
+
const ttlSeconds = await redis.ttl(fullKey);
|
|
758
|
+
if (ttlSeconds <= 0) {
|
|
759
|
+
return { blocked: false, ttlMs: 0 };
|
|
760
|
+
}
|
|
761
|
+
return { blocked: true, ttlMs: ttlSeconds * 1e3 };
|
|
762
|
+
},
|
|
763
|
+
async setBlock(key, durationSeconds) {
|
|
764
|
+
const fullKey = `${prefix}${key}`;
|
|
765
|
+
await redis.setex(fullKey, durationSeconds, "1");
|
|
766
|
+
},
|
|
767
|
+
async reset(key) {
|
|
768
|
+
const fullKey = `${prefix}${key}`;
|
|
769
|
+
await redis.del(fullKey);
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
|
|
732
774
|
// src/auth/audit.ts
|
|
733
775
|
var StandardAuditActions = {
|
|
734
776
|
// Authentication
|
|
@@ -1013,6 +1055,170 @@ function buildPagination(page, limit, total) {
|
|
|
1013
1055
|
};
|
|
1014
1056
|
}
|
|
1015
1057
|
|
|
1058
|
+
// src/auth/nextjs-api.ts
|
|
1059
|
+
async function enforceRateLimit(request, operation, rule, options) {
|
|
1060
|
+
const identifier = options?.identifier ?? (options?.userId ? `user:${options.userId}` : void 0) ?? `ip:${extractClientIp((name) => request.headers.get(name))}`;
|
|
1061
|
+
const isAuthenticated = !!options?.userId;
|
|
1062
|
+
const result = await checkRateLimit(operation, identifier, rule, {
|
|
1063
|
+
...options?.rateLimitOptions,
|
|
1064
|
+
isAuthenticated
|
|
1065
|
+
});
|
|
1066
|
+
if (!result.allowed) {
|
|
1067
|
+
const headers = buildRateLimitResponseHeaders(result);
|
|
1068
|
+
return new Response(
|
|
1069
|
+
JSON.stringify({
|
|
1070
|
+
error: "Rate limit exceeded. Please try again later.",
|
|
1071
|
+
retryAfter: result.retryAfterSeconds
|
|
1072
|
+
}),
|
|
1073
|
+
{
|
|
1074
|
+
status: 429,
|
|
1075
|
+
headers: { "Content-Type": "application/json", ...headers }
|
|
1076
|
+
}
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
function errorResponse(error, options) {
|
|
1082
|
+
const isDev = options?.isDevelopment ?? process.env.NODE_ENV === "development";
|
|
1083
|
+
const { status, body } = classifyError(error, isDev);
|
|
1084
|
+
return new Response(JSON.stringify(body), {
|
|
1085
|
+
status,
|
|
1086
|
+
headers: { "Content-Type": "application/json" }
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
function zodErrorResponse(error) {
|
|
1090
|
+
const firstIssue = error.issues[0];
|
|
1091
|
+
const message = firstIssue ? `${firstIssue.path.join(".") || "input"}: ${firstIssue.message}` : "Validation error";
|
|
1092
|
+
return new Response(JSON.stringify({ error: message }), {
|
|
1093
|
+
status: 400,
|
|
1094
|
+
headers: { "Content-Type": "application/json" }
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
function extractBearerToken(request) {
|
|
1098
|
+
const auth = request.headers.get("authorization");
|
|
1099
|
+
if (!auth?.startsWith("Bearer ")) return null;
|
|
1100
|
+
return auth.slice(7).trim() || null;
|
|
1101
|
+
}
|
|
1102
|
+
function isValidBearerToken(request, secret) {
|
|
1103
|
+
if (!secret) return false;
|
|
1104
|
+
const token = extractBearerToken(request);
|
|
1105
|
+
if (!token) return false;
|
|
1106
|
+
return constantTimeEqual(token, secret);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// src/auth/beta-client.ts
|
|
1110
|
+
var DEFAULT_CONFIG = {
|
|
1111
|
+
baseUrl: "",
|
|
1112
|
+
settingsEndpoint: "/api/beta-settings",
|
|
1113
|
+
validateEndpoint: "/api/validate-beta-code",
|
|
1114
|
+
storageKey: "beta_code",
|
|
1115
|
+
failSafeDefaults: {
|
|
1116
|
+
betaMode: true,
|
|
1117
|
+
requireInviteCode: true,
|
|
1118
|
+
betaMessage: ""
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
function createBetaClient(config = {}) {
|
|
1122
|
+
const cfg = {
|
|
1123
|
+
...DEFAULT_CONFIG,
|
|
1124
|
+
...config,
|
|
1125
|
+
failSafeDefaults: {
|
|
1126
|
+
...DEFAULT_CONFIG.failSafeDefaults,
|
|
1127
|
+
...config.failSafeDefaults
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
return {
|
|
1131
|
+
fetchSettings: () => fetchBetaSettings(cfg),
|
|
1132
|
+
validateCode: (code) => validateBetaCode(code, cfg),
|
|
1133
|
+
storeCode: (code) => storeBetaCode(code, cfg),
|
|
1134
|
+
getStoredCode: () => getStoredBetaCode(cfg),
|
|
1135
|
+
clearStoredCode: () => clearStoredBetaCode(cfg)
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
async function fetchBetaSettings(config = {}) {
|
|
1139
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
1140
|
+
try {
|
|
1141
|
+
const response = await fetch(
|
|
1142
|
+
`${cfg.baseUrl}${cfg.settingsEndpoint}`,
|
|
1143
|
+
{
|
|
1144
|
+
method: "GET",
|
|
1145
|
+
headers: { "Content-Type": "application/json" },
|
|
1146
|
+
cache: "no-store"
|
|
1147
|
+
}
|
|
1148
|
+
);
|
|
1149
|
+
if (!response.ok) {
|
|
1150
|
+
throw new Error(`Failed to fetch beta settings: ${response.status}`);
|
|
1151
|
+
}
|
|
1152
|
+
const data = await response.json();
|
|
1153
|
+
return {
|
|
1154
|
+
betaMode: data.betaMode ?? cfg.failSafeDefaults.betaMode ?? true,
|
|
1155
|
+
requireInviteCode: data.requireInviteCode ?? cfg.failSafeDefaults.requireInviteCode ?? true,
|
|
1156
|
+
betaMessage: data.betaMessage ?? cfg.failSafeDefaults.betaMessage ?? ""
|
|
1157
|
+
};
|
|
1158
|
+
} catch (error) {
|
|
1159
|
+
console.error("Error fetching beta settings:", error);
|
|
1160
|
+
return {
|
|
1161
|
+
betaMode: cfg.failSafeDefaults.betaMode ?? true,
|
|
1162
|
+
requireInviteCode: cfg.failSafeDefaults.requireInviteCode ?? true,
|
|
1163
|
+
betaMessage: cfg.failSafeDefaults.betaMessage ?? ""
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
async function validateBetaCode(code, config = {}) {
|
|
1168
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
1169
|
+
if (!code || code.trim().length < 3) {
|
|
1170
|
+
return {
|
|
1171
|
+
valid: false,
|
|
1172
|
+
message: "Please enter a valid invite code."
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
try {
|
|
1176
|
+
const response = await fetch(
|
|
1177
|
+
`${cfg.baseUrl}${cfg.validateEndpoint}`,
|
|
1178
|
+
{
|
|
1179
|
+
method: "POST",
|
|
1180
|
+
headers: { "Content-Type": "application/json" },
|
|
1181
|
+
body: JSON.stringify({ code: code.trim().toUpperCase() })
|
|
1182
|
+
}
|
|
1183
|
+
);
|
|
1184
|
+
if (response.status === 429) {
|
|
1185
|
+
return {
|
|
1186
|
+
valid: false,
|
|
1187
|
+
message: "Too many attempts. Please try again later."
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
if (!response.ok) {
|
|
1191
|
+
throw new Error(`Validation request failed: ${response.status}`);
|
|
1192
|
+
}
|
|
1193
|
+
return await response.json();
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
console.error("Error validating invite code:", error);
|
|
1196
|
+
return {
|
|
1197
|
+
valid: false,
|
|
1198
|
+
message: "Unable to validate code. Please try again."
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
function storeBetaCode(code, config = {}) {
|
|
1203
|
+
const key = config.storageKey ?? DEFAULT_CONFIG.storageKey;
|
|
1204
|
+
if (typeof window !== "undefined") {
|
|
1205
|
+
sessionStorage.setItem(key, code.trim().toUpperCase());
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
function getStoredBetaCode(config = {}) {
|
|
1209
|
+
const key = config.storageKey ?? DEFAULT_CONFIG.storageKey;
|
|
1210
|
+
if (typeof window !== "undefined") {
|
|
1211
|
+
return sessionStorage.getItem(key);
|
|
1212
|
+
}
|
|
1213
|
+
return null;
|
|
1214
|
+
}
|
|
1215
|
+
function clearStoredBetaCode(config = {}) {
|
|
1216
|
+
const key = config.storageKey ?? DEFAULT_CONFIG.storageKey;
|
|
1217
|
+
if (typeof window !== "undefined") {
|
|
1218
|
+
sessionStorage.removeItem(key);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1016
1222
|
// src/env.ts
|
|
1017
1223
|
function getRequiredEnv(key) {
|
|
1018
1224
|
const value = process.env[key];
|
|
@@ -1134,20 +1340,27 @@ export {
|
|
|
1134
1340
|
checkEnvVars,
|
|
1135
1341
|
checkRateLimit,
|
|
1136
1342
|
classifyError,
|
|
1343
|
+
clearStoredBetaCode,
|
|
1137
1344
|
constantTimeEqual,
|
|
1138
1345
|
containsHtml,
|
|
1139
1346
|
containsUrls,
|
|
1140
1347
|
createAuditActor,
|
|
1141
1348
|
createAuditLogger,
|
|
1349
|
+
createBetaClient,
|
|
1142
1350
|
createFeatureFlags,
|
|
1143
1351
|
createMemoryRateLimitStore,
|
|
1352
|
+
createRedisRateLimitStore,
|
|
1144
1353
|
createSafeTextSchema,
|
|
1145
1354
|
detectStage,
|
|
1355
|
+
enforceRateLimit,
|
|
1356
|
+
errorResponse,
|
|
1146
1357
|
escapeHtml,
|
|
1147
1358
|
extractAuditIp,
|
|
1148
1359
|
extractAuditRequestId,
|
|
1149
1360
|
extractAuditUserAgent,
|
|
1361
|
+
extractBearerToken,
|
|
1150
1362
|
extractClientIp,
|
|
1363
|
+
fetchBetaSettings,
|
|
1151
1364
|
getBoolEnv,
|
|
1152
1365
|
getCorrelationId,
|
|
1153
1366
|
getEndSessionEndpoint,
|
|
@@ -1156,6 +1369,7 @@ export {
|
|
|
1156
1369
|
getOptionalEnv,
|
|
1157
1370
|
getRateLimitStatus,
|
|
1158
1371
|
getRequiredEnv,
|
|
1372
|
+
getStoredBetaCode,
|
|
1159
1373
|
getTokenEndpoint,
|
|
1160
1374
|
hasAllRoles,
|
|
1161
1375
|
hasAnyRole,
|
|
@@ -1163,13 +1377,17 @@ export {
|
|
|
1163
1377
|
isAllowlisted,
|
|
1164
1378
|
isApiError,
|
|
1165
1379
|
isTokenExpired,
|
|
1380
|
+
isValidBearerToken,
|
|
1166
1381
|
parseKeycloakRoles,
|
|
1167
1382
|
refreshKeycloakToken,
|
|
1168
1383
|
resetRateLimitForKey,
|
|
1169
1384
|
resolveIdentifier,
|
|
1170
1385
|
resolveRateLimitIdentifier,
|
|
1171
1386
|
sanitizeApiError,
|
|
1387
|
+
storeBetaCode,
|
|
1172
1388
|
stripHtml,
|
|
1173
|
-
|
|
1389
|
+
validateBetaCode,
|
|
1390
|
+
validateEnvVars,
|
|
1391
|
+
zodErrorResponse
|
|
1174
1392
|
};
|
|
1175
1393
|
//# sourceMappingURL=auth.mjs.map
|