@digilogiclabs/platform-core 1.5.1 → 1.7.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 +110 -1047
- package/dist/auth.d.ts +110 -1047
- package/dist/auth.js +406 -6
- package/dist/auth.js.map +1 -1
- package/dist/auth.mjs +379 -5
- package/dist/auth.mjs.map +1 -1
- package/dist/env-DerQ7Da-.d.mts +1367 -0
- package/dist/env-DerQ7Da-.d.ts +1367 -0
- package/dist/index.d.mts +2 -257
- package/dist/index.d.ts +2 -257
- package/dist/index.js +94 -95
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -95
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/auth.js
CHANGED
|
@@ -20,6 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/auth/index.ts
|
|
21
21
|
var auth_exports = {};
|
|
22
22
|
__export(auth_exports, {
|
|
23
|
+
ApiError: () => ApiError,
|
|
24
|
+
ApiErrorCode: () => ApiErrorCode,
|
|
25
|
+
CommonApiErrors: () => CommonApiErrors,
|
|
23
26
|
CommonRateLimits: () => CommonRateLimits,
|
|
24
27
|
DateRangeSchema: () => DateRangeSchema,
|
|
25
28
|
EmailSchema: () => EmailSchema,
|
|
@@ -39,34 +42,57 @@ __export(auth_exports, {
|
|
|
39
42
|
buildAuthCookies: () => buildAuthCookies,
|
|
40
43
|
buildErrorBody: () => buildErrorBody,
|
|
41
44
|
buildKeycloakCallbacks: () => buildKeycloakCallbacks,
|
|
45
|
+
buildPagination: () => buildPagination,
|
|
42
46
|
buildRateLimitHeaders: () => buildRateLimitHeaders,
|
|
43
47
|
buildRateLimitResponseHeaders: () => buildRateLimitResponseHeaders,
|
|
44
48
|
buildRedirectCallback: () => buildRedirectCallback,
|
|
45
49
|
buildTokenRefreshParams: () => buildTokenRefreshParams,
|
|
50
|
+
checkEnvVars: () => checkEnvVars,
|
|
46
51
|
checkRateLimit: () => checkRateLimit,
|
|
52
|
+
classifyError: () => classifyError,
|
|
53
|
+
constantTimeEqual: () => constantTimeEqual,
|
|
54
|
+
containsHtml: () => containsHtml,
|
|
55
|
+
containsUrls: () => containsUrls,
|
|
47
56
|
createAuditActor: () => createAuditActor,
|
|
48
57
|
createAuditLogger: () => createAuditLogger,
|
|
49
58
|
createFeatureFlags: () => createFeatureFlags,
|
|
50
59
|
createMemoryRateLimitStore: () => createMemoryRateLimitStore,
|
|
60
|
+
createRedisRateLimitStore: () => createRedisRateLimitStore,
|
|
51
61
|
createSafeTextSchema: () => createSafeTextSchema,
|
|
52
62
|
detectStage: () => detectStage,
|
|
63
|
+
enforceRateLimit: () => enforceRateLimit,
|
|
64
|
+
errorResponse: () => errorResponse,
|
|
65
|
+
escapeHtml: () => escapeHtml,
|
|
53
66
|
extractAuditIp: () => extractAuditIp,
|
|
54
67
|
extractAuditRequestId: () => extractAuditRequestId,
|
|
55
68
|
extractAuditUserAgent: () => extractAuditUserAgent,
|
|
69
|
+
extractBearerToken: () => extractBearerToken,
|
|
56
70
|
extractClientIp: () => extractClientIp,
|
|
71
|
+
getBoolEnv: () => getBoolEnv,
|
|
72
|
+
getCorrelationId: () => getCorrelationId,
|
|
57
73
|
getEndSessionEndpoint: () => getEndSessionEndpoint,
|
|
74
|
+
getEnvSummary: () => getEnvSummary,
|
|
75
|
+
getIntEnv: () => getIntEnv,
|
|
76
|
+
getOptionalEnv: () => getOptionalEnv,
|
|
58
77
|
getRateLimitStatus: () => getRateLimitStatus,
|
|
78
|
+
getRequiredEnv: () => getRequiredEnv,
|
|
59
79
|
getTokenEndpoint: () => getTokenEndpoint,
|
|
60
80
|
hasAllRoles: () => hasAllRoles,
|
|
61
81
|
hasAnyRole: () => hasAnyRole,
|
|
62
82
|
hasRole: () => hasRole,
|
|
63
83
|
isAllowlisted: () => isAllowlisted,
|
|
84
|
+
isApiError: () => isApiError,
|
|
64
85
|
isTokenExpired: () => isTokenExpired,
|
|
86
|
+
isValidBearerToken: () => isValidBearerToken,
|
|
65
87
|
parseKeycloakRoles: () => parseKeycloakRoles,
|
|
66
88
|
refreshKeycloakToken: () => refreshKeycloakToken,
|
|
67
89
|
resetRateLimitForKey: () => resetRateLimitForKey,
|
|
68
90
|
resolveIdentifier: () => resolveIdentifier,
|
|
69
|
-
resolveRateLimitIdentifier: () => resolveRateLimitIdentifier
|
|
91
|
+
resolveRateLimitIdentifier: () => resolveRateLimitIdentifier,
|
|
92
|
+
sanitizeApiError: () => sanitizeApiError,
|
|
93
|
+
stripHtml: () => stripHtml,
|
|
94
|
+
validateEnvVars: () => validateEnvVars,
|
|
95
|
+
zodErrorResponse: () => zodErrorResponse
|
|
70
96
|
});
|
|
71
97
|
module.exports = __toCommonJS(auth_exports);
|
|
72
98
|
|
|
@@ -239,6 +265,7 @@ function buildKeycloakCallbacks(config) {
|
|
|
239
265
|
*
|
|
240
266
|
* Compatible with Auth.js v5 JWT callback signature.
|
|
241
267
|
*/
|
|
268
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
242
269
|
async jwt({
|
|
243
270
|
token,
|
|
244
271
|
user,
|
|
@@ -289,10 +316,8 @@ function buildKeycloakCallbacks(config) {
|
|
|
289
316
|
*
|
|
290
317
|
* Compatible with Auth.js v5 session callback signature.
|
|
291
318
|
*/
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
token
|
|
295
|
-
}) {
|
|
319
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
320
|
+
async session({ session, token }) {
|
|
296
321
|
const user = session.user;
|
|
297
322
|
if (user) {
|
|
298
323
|
user.id = token.id || token.sub;
|
|
@@ -416,9 +441,53 @@ var WrapperPresets = {
|
|
|
416
441
|
var import_zod = require("zod");
|
|
417
442
|
|
|
418
443
|
// src/security.ts
|
|
444
|
+
var import_crypto = require("crypto");
|
|
419
445
|
var URL_PROTOCOL_PATTERN = /(https?:\/\/|ftp:\/\/|www\.)\S+/i;
|
|
420
446
|
var URL_DOMAIN_PATTERN = /\b[\w.-]+\.(com|net|org|io|co|dev|app|xyz|info|biz|me|us|uk|edu|gov)\b/i;
|
|
421
447
|
var HTML_TAG_PATTERN = /<[^>]*>/;
|
|
448
|
+
function escapeHtml(str) {
|
|
449
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
450
|
+
}
|
|
451
|
+
function containsUrls(str) {
|
|
452
|
+
return URL_PROTOCOL_PATTERN.test(str) || URL_DOMAIN_PATTERN.test(str);
|
|
453
|
+
}
|
|
454
|
+
function containsHtml(str) {
|
|
455
|
+
return HTML_TAG_PATTERN.test(str);
|
|
456
|
+
}
|
|
457
|
+
function stripHtml(str) {
|
|
458
|
+
return str.replace(/<[^>]*>/g, "");
|
|
459
|
+
}
|
|
460
|
+
function constantTimeEqual(a, b) {
|
|
461
|
+
try {
|
|
462
|
+
const aBuf = Buffer.from(a, "utf-8");
|
|
463
|
+
const bBuf = Buffer.from(b, "utf-8");
|
|
464
|
+
if (aBuf.length !== bBuf.length) return false;
|
|
465
|
+
return (0, import_crypto.timingSafeEqual)(aBuf, bBuf);
|
|
466
|
+
} catch {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function sanitizeApiError(error, statusCode, isDevelopment = false) {
|
|
471
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
472
|
+
const message = error instanceof Error ? error.message : String(error || "Bad request");
|
|
473
|
+
return { message };
|
|
474
|
+
}
|
|
475
|
+
const result = {
|
|
476
|
+
message: "An internal error occurred. Please try again later.",
|
|
477
|
+
code: "INTERNAL_ERROR"
|
|
478
|
+
};
|
|
479
|
+
if (isDevelopment && error instanceof Error) {
|
|
480
|
+
result.stack = error.stack;
|
|
481
|
+
}
|
|
482
|
+
return result;
|
|
483
|
+
}
|
|
484
|
+
function getCorrelationId(headers) {
|
|
485
|
+
const get = typeof headers === "function" ? headers : (name) => {
|
|
486
|
+
const val = headers[name] ?? headers[name.toLowerCase()];
|
|
487
|
+
return Array.isArray(val) ? val[0] : val;
|
|
488
|
+
};
|
|
489
|
+
return get("x-request-id") || get("X-Request-ID") || get("x-correlation-id") || get("X-Correlation-ID") || crypto.randomUUID();
|
|
490
|
+
}
|
|
422
491
|
|
|
423
492
|
// src/auth/schemas.ts
|
|
424
493
|
var EmailSchema = import_zod.z.string().trim().toLowerCase().email("Invalid email address");
|
|
@@ -762,6 +831,44 @@ function resolveIdentifier(session, clientIp) {
|
|
|
762
831
|
return { identifier: `ip:${clientIp ?? "unknown"}`, isAuthenticated: false };
|
|
763
832
|
}
|
|
764
833
|
|
|
834
|
+
// src/auth/rate-limit-store-redis.ts
|
|
835
|
+
function createRedisRateLimitStore(redis, options = {}) {
|
|
836
|
+
const prefix = options.keyPrefix ?? "";
|
|
837
|
+
return {
|
|
838
|
+
async increment(key, windowMs, now) {
|
|
839
|
+
const fullKey = `${prefix}${key}`;
|
|
840
|
+
const windowStart = now - windowMs;
|
|
841
|
+
const windowSeconds = Math.ceil(windowMs / 1e3) + 60;
|
|
842
|
+
await redis.zremrangebyscore(fullKey, 0, windowStart);
|
|
843
|
+
const current = await redis.zcard(fullKey);
|
|
844
|
+
const member = `${now}:${Math.random().toString(36).slice(2, 10)}`;
|
|
845
|
+
await redis.zadd(fullKey, now, member);
|
|
846
|
+
await redis.expire(fullKey, windowSeconds);
|
|
847
|
+
return { count: current + 1 };
|
|
848
|
+
},
|
|
849
|
+
async isBlocked(key) {
|
|
850
|
+
const fullKey = `${prefix}${key}`;
|
|
851
|
+
const value = await redis.get(fullKey);
|
|
852
|
+
if (!value) {
|
|
853
|
+
return { blocked: false, ttlMs: 0 };
|
|
854
|
+
}
|
|
855
|
+
const ttlSeconds = await redis.ttl(fullKey);
|
|
856
|
+
if (ttlSeconds <= 0) {
|
|
857
|
+
return { blocked: false, ttlMs: 0 };
|
|
858
|
+
}
|
|
859
|
+
return { blocked: true, ttlMs: ttlSeconds * 1e3 };
|
|
860
|
+
},
|
|
861
|
+
async setBlock(key, durationSeconds) {
|
|
862
|
+
const fullKey = `${prefix}${key}`;
|
|
863
|
+
await redis.setex(fullKey, durationSeconds, "1");
|
|
864
|
+
},
|
|
865
|
+
async reset(key) {
|
|
866
|
+
const fullKey = `${prefix}${key}`;
|
|
867
|
+
await redis.del(fullKey);
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
|
|
765
872
|
// src/auth/audit.ts
|
|
766
873
|
var StandardAuditActions = {
|
|
767
874
|
// Authentication
|
|
@@ -920,8 +1027,278 @@ function createAuditLogger(options = {}) {
|
|
|
920
1027
|
}
|
|
921
1028
|
return { log, createTimedAudit };
|
|
922
1029
|
}
|
|
1030
|
+
|
|
1031
|
+
// src/api.ts
|
|
1032
|
+
var ApiErrorCode = {
|
|
1033
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
1034
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
1035
|
+
FORBIDDEN: "FORBIDDEN",
|
|
1036
|
+
NOT_FOUND: "NOT_FOUND",
|
|
1037
|
+
CONFLICT: "CONFLICT",
|
|
1038
|
+
RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
|
|
1039
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
1040
|
+
DATABASE_ERROR: "DATABASE_ERROR",
|
|
1041
|
+
EXTERNAL_SERVICE_ERROR: "EXTERNAL_SERVICE_ERROR",
|
|
1042
|
+
CONFIGURATION_ERROR: "CONFIGURATION_ERROR"
|
|
1043
|
+
};
|
|
1044
|
+
var ApiError = class extends Error {
|
|
1045
|
+
statusCode;
|
|
1046
|
+
code;
|
|
1047
|
+
details;
|
|
1048
|
+
constructor(statusCode, message, code = ApiErrorCode.INTERNAL_ERROR, details) {
|
|
1049
|
+
super(message);
|
|
1050
|
+
this.name = "ApiError";
|
|
1051
|
+
this.statusCode = statusCode;
|
|
1052
|
+
this.code = code;
|
|
1053
|
+
this.details = details;
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
function isApiError(error) {
|
|
1057
|
+
return error instanceof ApiError;
|
|
1058
|
+
}
|
|
1059
|
+
var CommonApiErrors = {
|
|
1060
|
+
unauthorized: (msg = "Unauthorized") => new ApiError(401, msg, ApiErrorCode.UNAUTHORIZED),
|
|
1061
|
+
forbidden: (msg = "Forbidden") => new ApiError(403, msg, ApiErrorCode.FORBIDDEN),
|
|
1062
|
+
notFound: (resource = "Resource") => new ApiError(404, `${resource} not found`, ApiErrorCode.NOT_FOUND),
|
|
1063
|
+
conflict: (msg = "Resource already exists") => new ApiError(409, msg, ApiErrorCode.CONFLICT),
|
|
1064
|
+
rateLimitExceeded: (msg = "Rate limit exceeded") => new ApiError(429, msg, ApiErrorCode.RATE_LIMIT_EXCEEDED),
|
|
1065
|
+
validationError: (details) => new ApiError(
|
|
1066
|
+
400,
|
|
1067
|
+
"Validation failed",
|
|
1068
|
+
ApiErrorCode.VALIDATION_ERROR,
|
|
1069
|
+
details
|
|
1070
|
+
),
|
|
1071
|
+
internalError: (msg = "Internal server error") => new ApiError(500, msg, ApiErrorCode.INTERNAL_ERROR)
|
|
1072
|
+
};
|
|
1073
|
+
var PG_ERROR_MAP = {
|
|
1074
|
+
"23505": {
|
|
1075
|
+
status: 409,
|
|
1076
|
+
code: ApiErrorCode.CONFLICT,
|
|
1077
|
+
message: "Resource already exists"
|
|
1078
|
+
},
|
|
1079
|
+
"23503": {
|
|
1080
|
+
status: 404,
|
|
1081
|
+
code: ApiErrorCode.NOT_FOUND,
|
|
1082
|
+
message: "Referenced resource not found"
|
|
1083
|
+
},
|
|
1084
|
+
PGRST116: {
|
|
1085
|
+
status: 404,
|
|
1086
|
+
code: ApiErrorCode.NOT_FOUND,
|
|
1087
|
+
message: "Resource not found"
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
function classifyError(error, isDev = false) {
|
|
1091
|
+
if (isApiError(error)) {
|
|
1092
|
+
return {
|
|
1093
|
+
status: error.statusCode,
|
|
1094
|
+
body: { error: error.message, code: error.code, details: error.details }
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
if (error && typeof error === "object" && "issues" in error && Array.isArray(error.issues)) {
|
|
1098
|
+
return {
|
|
1099
|
+
status: 400,
|
|
1100
|
+
body: {
|
|
1101
|
+
error: "Validation failed",
|
|
1102
|
+
code: ApiErrorCode.VALIDATION_ERROR,
|
|
1103
|
+
details: error.issues.map((i) => ({
|
|
1104
|
+
field: i.path?.join("."),
|
|
1105
|
+
message: i.message
|
|
1106
|
+
}))
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
if (error && typeof error === "object" && "code" in error && typeof error.code === "string") {
|
|
1111
|
+
const pgCode = error.code;
|
|
1112
|
+
const mapped = PG_ERROR_MAP[pgCode];
|
|
1113
|
+
if (mapped) {
|
|
1114
|
+
return {
|
|
1115
|
+
status: mapped.status,
|
|
1116
|
+
body: { error: mapped.message, code: mapped.code }
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
return {
|
|
1120
|
+
status: 500,
|
|
1121
|
+
body: {
|
|
1122
|
+
error: "Database error",
|
|
1123
|
+
code: ApiErrorCode.DATABASE_ERROR,
|
|
1124
|
+
details: isDev ? error.message : void 0
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
if (error instanceof Error) {
|
|
1129
|
+
return {
|
|
1130
|
+
status: 500,
|
|
1131
|
+
body: {
|
|
1132
|
+
error: isDev ? error.message : "Internal server error",
|
|
1133
|
+
code: ApiErrorCode.INTERNAL_ERROR,
|
|
1134
|
+
details: isDev ? error.stack : void 0
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
return {
|
|
1139
|
+
status: 500,
|
|
1140
|
+
body: {
|
|
1141
|
+
error: "An unexpected error occurred",
|
|
1142
|
+
code: ApiErrorCode.INTERNAL_ERROR
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
function buildPagination(page, limit, total) {
|
|
1147
|
+
return {
|
|
1148
|
+
page,
|
|
1149
|
+
limit,
|
|
1150
|
+
total,
|
|
1151
|
+
totalPages: Math.ceil(total / limit),
|
|
1152
|
+
hasMore: page * limit < total
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// src/auth/nextjs-api.ts
|
|
1157
|
+
async function enforceRateLimit(request, operation, rule, options) {
|
|
1158
|
+
const identifier = options?.identifier ?? (options?.userId ? `user:${options.userId}` : void 0) ?? `ip:${extractClientIp((name) => request.headers.get(name))}`;
|
|
1159
|
+
const isAuthenticated = !!options?.userId;
|
|
1160
|
+
const result = await checkRateLimit(operation, identifier, rule, {
|
|
1161
|
+
...options?.rateLimitOptions,
|
|
1162
|
+
isAuthenticated
|
|
1163
|
+
});
|
|
1164
|
+
if (!result.allowed) {
|
|
1165
|
+
const headers = buildRateLimitResponseHeaders(result);
|
|
1166
|
+
return new Response(
|
|
1167
|
+
JSON.stringify({
|
|
1168
|
+
error: "Rate limit exceeded. Please try again later.",
|
|
1169
|
+
retryAfter: result.retryAfterSeconds
|
|
1170
|
+
}),
|
|
1171
|
+
{
|
|
1172
|
+
status: 429,
|
|
1173
|
+
headers: { "Content-Type": "application/json", ...headers }
|
|
1174
|
+
}
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
function errorResponse(error, options) {
|
|
1180
|
+
const isDev = options?.isDevelopment ?? process.env.NODE_ENV === "development";
|
|
1181
|
+
const { status, body } = classifyError(error, isDev);
|
|
1182
|
+
return new Response(JSON.stringify(body), {
|
|
1183
|
+
status,
|
|
1184
|
+
headers: { "Content-Type": "application/json" }
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
function zodErrorResponse(error) {
|
|
1188
|
+
const firstIssue = error.issues[0];
|
|
1189
|
+
const message = firstIssue ? `${firstIssue.path.join(".") || "input"}: ${firstIssue.message}` : "Validation error";
|
|
1190
|
+
return new Response(JSON.stringify({ error: message }), {
|
|
1191
|
+
status: 400,
|
|
1192
|
+
headers: { "Content-Type": "application/json" }
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
function extractBearerToken(request) {
|
|
1196
|
+
const auth = request.headers.get("authorization");
|
|
1197
|
+
if (!auth?.startsWith("Bearer ")) return null;
|
|
1198
|
+
return auth.slice(7).trim() || null;
|
|
1199
|
+
}
|
|
1200
|
+
function isValidBearerToken(request, secret) {
|
|
1201
|
+
if (!secret) return false;
|
|
1202
|
+
const token = extractBearerToken(request);
|
|
1203
|
+
if (!token) return false;
|
|
1204
|
+
return constantTimeEqual(token, secret);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// src/env.ts
|
|
1208
|
+
function getRequiredEnv(key) {
|
|
1209
|
+
const value = process.env[key];
|
|
1210
|
+
if (!value) {
|
|
1211
|
+
throw new Error(`Missing required environment variable: ${key}`);
|
|
1212
|
+
}
|
|
1213
|
+
return value;
|
|
1214
|
+
}
|
|
1215
|
+
function getOptionalEnv(key, defaultValue) {
|
|
1216
|
+
return process.env[key] || defaultValue;
|
|
1217
|
+
}
|
|
1218
|
+
function getBoolEnv(key, defaultValue = false) {
|
|
1219
|
+
const value = process.env[key];
|
|
1220
|
+
if (value === void 0 || value === "") return defaultValue;
|
|
1221
|
+
return value === "true" || value === "1";
|
|
1222
|
+
}
|
|
1223
|
+
function getIntEnv(key, defaultValue) {
|
|
1224
|
+
const value = process.env[key];
|
|
1225
|
+
if (value === void 0 || value === "") return defaultValue;
|
|
1226
|
+
const parsed = parseInt(value, 10);
|
|
1227
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
1228
|
+
}
|
|
1229
|
+
function validateEnvVars(config) {
|
|
1230
|
+
const result = checkEnvVars(config);
|
|
1231
|
+
if (!result.valid) {
|
|
1232
|
+
const lines = [];
|
|
1233
|
+
if (result.missing.length > 0) {
|
|
1234
|
+
lines.push(
|
|
1235
|
+
"Missing required environment variables:",
|
|
1236
|
+
...result.missing.map((v) => ` - ${v}`)
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
if (result.missingOneOf.length > 0) {
|
|
1240
|
+
for (const group of result.missingOneOf) {
|
|
1241
|
+
lines.push(`Missing one of: ${group.join(" | ")}`);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
if (result.invalid.length > 0) {
|
|
1245
|
+
lines.push(
|
|
1246
|
+
"Invalid environment variables:",
|
|
1247
|
+
...result.invalid.map((v) => ` - ${v.key}: ${v.reason}`)
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
throw new Error(lines.join("\n"));
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
function checkEnvVars(config) {
|
|
1254
|
+
const missing = [];
|
|
1255
|
+
const invalid = [];
|
|
1256
|
+
const missingOneOf = [];
|
|
1257
|
+
if (config.required) {
|
|
1258
|
+
for (const key of config.required) {
|
|
1259
|
+
if (!process.env[key]) {
|
|
1260
|
+
missing.push(key);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
if (config.requireOneOf) {
|
|
1265
|
+
for (const group of config.requireOneOf) {
|
|
1266
|
+
const hasAny = group.some((key) => !!process.env[key]);
|
|
1267
|
+
if (!hasAny) {
|
|
1268
|
+
missingOneOf.push(group);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
if (config.validators) {
|
|
1273
|
+
for (const [key, validator] of Object.entries(config.validators)) {
|
|
1274
|
+
const value = process.env[key];
|
|
1275
|
+
if (value) {
|
|
1276
|
+
const result = validator(value);
|
|
1277
|
+
if (result !== true) {
|
|
1278
|
+
invalid.push({ key, reason: result });
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
return {
|
|
1284
|
+
valid: missing.length === 0 && invalid.length === 0 && missingOneOf.length === 0,
|
|
1285
|
+
missing,
|
|
1286
|
+
invalid,
|
|
1287
|
+
missingOneOf
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
function getEnvSummary(keys) {
|
|
1291
|
+
const summary = {};
|
|
1292
|
+
for (const key of keys) {
|
|
1293
|
+
summary[key] = !!process.env[key];
|
|
1294
|
+
}
|
|
1295
|
+
return summary;
|
|
1296
|
+
}
|
|
923
1297
|
// Annotate the CommonJS export names for ESM import in node:
|
|
924
1298
|
0 && (module.exports = {
|
|
1299
|
+
ApiError,
|
|
1300
|
+
ApiErrorCode,
|
|
1301
|
+
CommonApiErrors,
|
|
925
1302
|
CommonRateLimits,
|
|
926
1303
|
DateRangeSchema,
|
|
927
1304
|
EmailSchema,
|
|
@@ -941,33 +1318,56 @@ function createAuditLogger(options = {}) {
|
|
|
941
1318
|
buildAuthCookies,
|
|
942
1319
|
buildErrorBody,
|
|
943
1320
|
buildKeycloakCallbacks,
|
|
1321
|
+
buildPagination,
|
|
944
1322
|
buildRateLimitHeaders,
|
|
945
1323
|
buildRateLimitResponseHeaders,
|
|
946
1324
|
buildRedirectCallback,
|
|
947
1325
|
buildTokenRefreshParams,
|
|
1326
|
+
checkEnvVars,
|
|
948
1327
|
checkRateLimit,
|
|
1328
|
+
classifyError,
|
|
1329
|
+
constantTimeEqual,
|
|
1330
|
+
containsHtml,
|
|
1331
|
+
containsUrls,
|
|
949
1332
|
createAuditActor,
|
|
950
1333
|
createAuditLogger,
|
|
951
1334
|
createFeatureFlags,
|
|
952
1335
|
createMemoryRateLimitStore,
|
|
1336
|
+
createRedisRateLimitStore,
|
|
953
1337
|
createSafeTextSchema,
|
|
954
1338
|
detectStage,
|
|
1339
|
+
enforceRateLimit,
|
|
1340
|
+
errorResponse,
|
|
1341
|
+
escapeHtml,
|
|
955
1342
|
extractAuditIp,
|
|
956
1343
|
extractAuditRequestId,
|
|
957
1344
|
extractAuditUserAgent,
|
|
1345
|
+
extractBearerToken,
|
|
958
1346
|
extractClientIp,
|
|
1347
|
+
getBoolEnv,
|
|
1348
|
+
getCorrelationId,
|
|
959
1349
|
getEndSessionEndpoint,
|
|
1350
|
+
getEnvSummary,
|
|
1351
|
+
getIntEnv,
|
|
1352
|
+
getOptionalEnv,
|
|
960
1353
|
getRateLimitStatus,
|
|
1354
|
+
getRequiredEnv,
|
|
961
1355
|
getTokenEndpoint,
|
|
962
1356
|
hasAllRoles,
|
|
963
1357
|
hasAnyRole,
|
|
964
1358
|
hasRole,
|
|
965
1359
|
isAllowlisted,
|
|
1360
|
+
isApiError,
|
|
966
1361
|
isTokenExpired,
|
|
1362
|
+
isValidBearerToken,
|
|
967
1363
|
parseKeycloakRoles,
|
|
968
1364
|
refreshKeycloakToken,
|
|
969
1365
|
resetRateLimitForKey,
|
|
970
1366
|
resolveIdentifier,
|
|
971
|
-
resolveRateLimitIdentifier
|
|
1367
|
+
resolveRateLimitIdentifier,
|
|
1368
|
+
sanitizeApiError,
|
|
1369
|
+
stripHtml,
|
|
1370
|
+
validateEnvVars,
|
|
1371
|
+
zodErrorResponse
|
|
972
1372
|
});
|
|
973
1373
|
//# sourceMappingURL=auth.js.map
|