@digilogiclabs/platform-core 1.9.0 → 1.10.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 +115 -3
- package/dist/auth.d.ts +115 -3
- package/dist/auth.js +81 -0
- package/dist/auth.js.map +1 -1
- package/dist/auth.mjs +76 -0
- package/dist/auth.mjs.map +1 -1
- package/dist/email-templates.js +13 -6
- package/dist/email-templates.js.map +1 -1
- package/dist/email-templates.mjs +13 -6
- package/dist/email-templates.mjs.map +1 -1
- package/dist/{env-DHPZR3Lv.d.mts → env-CYKVNpLl.d.mts} +6 -0
- package/dist/{env-DHPZR3Lv.d.ts → env-CYKVNpLl.d.ts} +6 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/auth.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as RateLimitStore, a as RateLimitRule, b as RateLimitOptions } from './env-
|
|
2
|
-
export { al as AllowlistConfig, A as ApiError, d as ApiErrorCode, f as ApiErrorCodeType, h as ApiPaginatedResponse, Q as ApiSecurityConfig, V as ApiSecurityContext, g as ApiSuccessResponse, aD as AuditRequest, H as AuthCookiesConfig, N as AuthMethod, aL as BetaClientConfig, C as CommonApiErrors, ar as CommonRateLimits, ac as DateRangeInput, a6 as DateRangeSchema, ag as DeploymentStage, aa as EmailInput, $ as EmailSchema, E as EnvValidationConfig, p as EnvValidationResult, ai as FlagDefinition, aj as FlagDefinitions, ah as FlagValue, r as KEYCLOAK_DEFAULT_ROLES, F as KeycloakCallbacksConfig, K as KeycloakConfig, G as KeycloakJwtFields, q as KeycloakTokenSet, ae as LoginInput, a8 as LoginSchema, ay as OpsAuditActor, aA as OpsAuditEvent, aC as OpsAuditLoggerOptions, aB as OpsAuditRecord, az as OpsAuditResource, ab as PaginationInput, a5 as PaginationSchema, a0 as PasswordSchema, a3 as PersonNameSchema, a2 as PhoneSchema, aq as RateLimitCheckResult, P as RateLimitPreset, J as RedirectCallbackConfig, ak as ResolvedFlags, O as RouteAuditConfig, ad as SearchQueryInput, a7 as SearchQuerySchema, U as SecuritySession, af as SignupInput, a9 as SignupSchema, a1 as SlugSchema, aF as StandardAuditActionType, aE as StandardAuditActions, S as StandardRateLimitPresets, T as TokenRefreshResult, _ as WrapperPresets, ao as buildAllowlist, I as buildAuthCookies, Z as buildErrorBody, M as buildKeycloakCallbacks, e as buildPagination, Y as buildRateLimitHeaders, aw as buildRateLimitResponseHeaders, L as buildRedirectCallback, y as buildTokenRefreshParams, n as checkEnvVars, at as checkRateLimit, c as classifyError, aR as clearStoredBetaCode, aJ as createAuditActor, aK as createAuditLogger, aM as createBetaClient, an as createFeatureFlags, as as createMemoryRateLimitStore, a4 as createSafeTextSchema, am as detectStage, aG as extractAuditIp, aI as extractAuditRequestId, aH as extractAuditUserAgent, X as extractClientIp, aN as fetchBetaSettings, l as getBoolEnv, B as getEndSessionEndpoint, o as getEnvSummary, m as getIntEnv, k as getOptionalEnv, au as getRateLimitStatus, j as getRequiredEnv, aQ as getStoredBetaCode, z as getTokenEndpoint, w as hasAllRoles, u as hasAnyRole, t as hasRole, ap as isAllowlisted, i as isApiError, x as isTokenExpired, s as parseKeycloakRoles, D as refreshKeycloakToken, av as resetRateLimitForKey, ax as resolveIdentifier, W as resolveRateLimitIdentifier, aP as storeBetaCode, aO as validateBetaCode, v as validateEnvVars } from './env-
|
|
1
|
+
import { R as RateLimitStore, a as RateLimitRule, b as RateLimitOptions } from './env-CYKVNpLl.mjs';
|
|
2
|
+
export { al as AllowlistConfig, A as ApiError, d as ApiErrorCode, f as ApiErrorCodeType, h as ApiPaginatedResponse, Q as ApiSecurityConfig, V as ApiSecurityContext, g as ApiSuccessResponse, aD as AuditRequest, H as AuthCookiesConfig, N as AuthMethod, aL as BetaClientConfig, C as CommonApiErrors, ar as CommonRateLimits, ac as DateRangeInput, a6 as DateRangeSchema, ag as DeploymentStage, aa as EmailInput, $ as EmailSchema, E as EnvValidationConfig, p as EnvValidationResult, ai as FlagDefinition, aj as FlagDefinitions, ah as FlagValue, r as KEYCLOAK_DEFAULT_ROLES, F as KeycloakCallbacksConfig, K as KeycloakConfig, G as KeycloakJwtFields, q as KeycloakTokenSet, ae as LoginInput, a8 as LoginSchema, ay as OpsAuditActor, aA as OpsAuditEvent, aC as OpsAuditLoggerOptions, aB as OpsAuditRecord, az as OpsAuditResource, ab as PaginationInput, a5 as PaginationSchema, a0 as PasswordSchema, a3 as PersonNameSchema, a2 as PhoneSchema, aq as RateLimitCheckResult, P as RateLimitPreset, J as RedirectCallbackConfig, ak as ResolvedFlags, O as RouteAuditConfig, ad as SearchQueryInput, a7 as SearchQuerySchema, U as SecuritySession, af as SignupInput, a9 as SignupSchema, a1 as SlugSchema, aF as StandardAuditActionType, aE as StandardAuditActions, S as StandardRateLimitPresets, T as TokenRefreshResult, _ as WrapperPresets, ao as buildAllowlist, I as buildAuthCookies, Z as buildErrorBody, M as buildKeycloakCallbacks, e as buildPagination, Y as buildRateLimitHeaders, aw as buildRateLimitResponseHeaders, L as buildRedirectCallback, y as buildTokenRefreshParams, n as checkEnvVars, at as checkRateLimit, c as classifyError, aR as clearStoredBetaCode, aJ as createAuditActor, aK as createAuditLogger, aM as createBetaClient, an as createFeatureFlags, as as createMemoryRateLimitStore, a4 as createSafeTextSchema, am as detectStage, aG as extractAuditIp, aI as extractAuditRequestId, aH as extractAuditUserAgent, X as extractClientIp, aN as fetchBetaSettings, l as getBoolEnv, B as getEndSessionEndpoint, o as getEnvSummary, m as getIntEnv, k as getOptionalEnv, au as getRateLimitStatus, j as getRequiredEnv, aQ as getStoredBetaCode, z as getTokenEndpoint, w as hasAllRoles, u as hasAnyRole, t as hasRole, ap as isAllowlisted, i as isApiError, x as isTokenExpired, s as parseKeycloakRoles, D as refreshKeycloakToken, av as resetRateLimitForKey, ax as resolveIdentifier, W as resolveRateLimitIdentifier, aP as storeBetaCode, aO as validateBetaCode, v as validateEnvVars } from './env-CYKVNpLl.mjs';
|
|
3
3
|
export { c as constantTimeEqual, b as containsHtml, a as containsUrls, e as escapeHtml, g as getCorrelationId, d as sanitizeApiError, s as stripHtml } from './security-BvLXaQkv.mjs';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
|
@@ -172,5 +172,117 @@ declare function isValidBearerToken(request: {
|
|
|
172
172
|
get(name: string): string | null;
|
|
173
173
|
};
|
|
174
174
|
}, secret: string | undefined): boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Verify cron/sync endpoint authentication using a bearer token.
|
|
177
|
+
*
|
|
178
|
+
* Extracts the Bearer token from the Authorization header and compares
|
|
179
|
+
* it against `CRON_SECRET` (or a custom secret) using timing-safe comparison.
|
|
180
|
+
* Returns a 401/500 Response on failure, or `null` on success.
|
|
181
|
+
*
|
|
182
|
+
* @param request - Incoming request (must have headers.get)
|
|
183
|
+
* @param secret - Custom secret to compare against. Defaults to `process.env.CRON_SECRET`.
|
|
184
|
+
* @returns `null` if authorized, or a JSON Response (401/500) if not.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* export async function POST(request: NextRequest) {
|
|
189
|
+
* const authError = verifyCronAuth(request)
|
|
190
|
+
* if (authError) return authError
|
|
191
|
+
* // ... handle cron job
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
declare function verifyCronAuth(request: {
|
|
196
|
+
headers: {
|
|
197
|
+
get(name: string): string | null;
|
|
198
|
+
};
|
|
199
|
+
}, secret?: string): Response | null;
|
|
200
|
+
/**
|
|
201
|
+
* Extract the client IP address from a request, checking proxy headers.
|
|
202
|
+
*
|
|
203
|
+
* Checks in order:
|
|
204
|
+
* 1. `cf-connecting-ip` (Cloudflare)
|
|
205
|
+
* 2. `x-real-ip` (Nginx)
|
|
206
|
+
* 3. `x-forwarded-for` (first IP from comma-separated list)
|
|
207
|
+
* 4. Falls back to `'unknown'`
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* const ip = getClientIp(request)
|
|
212
|
+
* await auditLog({ action: 'login', ip })
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
declare function getClientIp(request: {
|
|
216
|
+
headers: {
|
|
217
|
+
get(name: string): string | null;
|
|
218
|
+
};
|
|
219
|
+
}): string;
|
|
220
|
+
/** Shape expected by rate limit response helpers. */
|
|
221
|
+
interface RateLimitResult {
|
|
222
|
+
limit: number;
|
|
223
|
+
remaining: number;
|
|
224
|
+
resetMs: number;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Create a 429 "Too Many Requests" response with RFC-compliant rate limit headers.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* if (!result.allowed) {
|
|
232
|
+
* return rateLimitResponse(result)
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
declare function rateLimitResponse(result: RateLimitResult): Response;
|
|
237
|
+
/**
|
|
238
|
+
* Add rate limit headers to an existing response.
|
|
239
|
+
*
|
|
240
|
+
* Use this to attach rate limit info to successful responses so clients
|
|
241
|
+
* can see their remaining quota.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* const response = new Response(JSON.stringify(data), { status: 200 })
|
|
246
|
+
* return addRateLimitHeaders(response, result)
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
declare function addRateLimitHeaders(response: Response, result: RateLimitResult): Response;
|
|
250
|
+
/**
|
|
251
|
+
* Safe Zod validation — returns { success, data?, errors? } without throwing.
|
|
252
|
+
* Useful for API routes where you want to return validation errors as JSON.
|
|
253
|
+
*
|
|
254
|
+
* Uses structural typing for the schema parameter so platform-core
|
|
255
|
+
* doesn't depend on Zod directly.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* const result = safeValidate(MySchema, await request.json())
|
|
260
|
+
* if (!result.success) {
|
|
261
|
+
* return new Response(JSON.stringify({ errors: result.errors }), { status: 400 })
|
|
262
|
+
* }
|
|
263
|
+
* const data = result.data // fully typed
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare function safeValidate<T>(schema: {
|
|
267
|
+
safeParse: (data: unknown) => {
|
|
268
|
+
success: boolean;
|
|
269
|
+
data?: T;
|
|
270
|
+
error?: {
|
|
271
|
+
issues: Array<{
|
|
272
|
+
path: (string | number)[];
|
|
273
|
+
message: string;
|
|
274
|
+
}>;
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
}, data: unknown): {
|
|
278
|
+
success: true;
|
|
279
|
+
data: T;
|
|
280
|
+
} | {
|
|
281
|
+
success: false;
|
|
282
|
+
errors: Array<{
|
|
283
|
+
field: string;
|
|
284
|
+
message: string;
|
|
285
|
+
}>;
|
|
286
|
+
};
|
|
175
287
|
|
|
176
|
-
export { RateLimitOptions, RateLimitRule, RateLimitStore, type RedisRateLimitClient, type RedisRateLimitStoreOptions, createRedisRateLimitStore, enforceRateLimit, errorResponse, extractBearerToken, isValidBearerToken, zodErrorResponse };
|
|
288
|
+
export { RateLimitOptions, type RateLimitResult, RateLimitRule, RateLimitStore, type RedisRateLimitClient, type RedisRateLimitStoreOptions, addRateLimitHeaders, createRedisRateLimitStore, enforceRateLimit, errorResponse, extractBearerToken, getClientIp, isValidBearerToken, rateLimitResponse, safeValidate, verifyCronAuth, zodErrorResponse };
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as RateLimitStore, a as RateLimitRule, b as RateLimitOptions } from './env-
|
|
2
|
-
export { al as AllowlistConfig, A as ApiError, d as ApiErrorCode, f as ApiErrorCodeType, h as ApiPaginatedResponse, Q as ApiSecurityConfig, V as ApiSecurityContext, g as ApiSuccessResponse, aD as AuditRequest, H as AuthCookiesConfig, N as AuthMethod, aL as BetaClientConfig, C as CommonApiErrors, ar as CommonRateLimits, ac as DateRangeInput, a6 as DateRangeSchema, ag as DeploymentStage, aa as EmailInput, $ as EmailSchema, E as EnvValidationConfig, p as EnvValidationResult, ai as FlagDefinition, aj as FlagDefinitions, ah as FlagValue, r as KEYCLOAK_DEFAULT_ROLES, F as KeycloakCallbacksConfig, K as KeycloakConfig, G as KeycloakJwtFields, q as KeycloakTokenSet, ae as LoginInput, a8 as LoginSchema, ay as OpsAuditActor, aA as OpsAuditEvent, aC as OpsAuditLoggerOptions, aB as OpsAuditRecord, az as OpsAuditResource, ab as PaginationInput, a5 as PaginationSchema, a0 as PasswordSchema, a3 as PersonNameSchema, a2 as PhoneSchema, aq as RateLimitCheckResult, P as RateLimitPreset, J as RedirectCallbackConfig, ak as ResolvedFlags, O as RouteAuditConfig, ad as SearchQueryInput, a7 as SearchQuerySchema, U as SecuritySession, af as SignupInput, a9 as SignupSchema, a1 as SlugSchema, aF as StandardAuditActionType, aE as StandardAuditActions, S as StandardRateLimitPresets, T as TokenRefreshResult, _ as WrapperPresets, ao as buildAllowlist, I as buildAuthCookies, Z as buildErrorBody, M as buildKeycloakCallbacks, e as buildPagination, Y as buildRateLimitHeaders, aw as buildRateLimitResponseHeaders, L as buildRedirectCallback, y as buildTokenRefreshParams, n as checkEnvVars, at as checkRateLimit, c as classifyError, aR as clearStoredBetaCode, aJ as createAuditActor, aK as createAuditLogger, aM as createBetaClient, an as createFeatureFlags, as as createMemoryRateLimitStore, a4 as createSafeTextSchema, am as detectStage, aG as extractAuditIp, aI as extractAuditRequestId, aH as extractAuditUserAgent, X as extractClientIp, aN as fetchBetaSettings, l as getBoolEnv, B as getEndSessionEndpoint, o as getEnvSummary, m as getIntEnv, k as getOptionalEnv, au as getRateLimitStatus, j as getRequiredEnv, aQ as getStoredBetaCode, z as getTokenEndpoint, w as hasAllRoles, u as hasAnyRole, t as hasRole, ap as isAllowlisted, i as isApiError, x as isTokenExpired, s as parseKeycloakRoles, D as refreshKeycloakToken, av as resetRateLimitForKey, ax as resolveIdentifier, W as resolveRateLimitIdentifier, aP as storeBetaCode, aO as validateBetaCode, v as validateEnvVars } from './env-
|
|
1
|
+
import { R as RateLimitStore, a as RateLimitRule, b as RateLimitOptions } from './env-CYKVNpLl.js';
|
|
2
|
+
export { al as AllowlistConfig, A as ApiError, d as ApiErrorCode, f as ApiErrorCodeType, h as ApiPaginatedResponse, Q as ApiSecurityConfig, V as ApiSecurityContext, g as ApiSuccessResponse, aD as AuditRequest, H as AuthCookiesConfig, N as AuthMethod, aL as BetaClientConfig, C as CommonApiErrors, ar as CommonRateLimits, ac as DateRangeInput, a6 as DateRangeSchema, ag as DeploymentStage, aa as EmailInput, $ as EmailSchema, E as EnvValidationConfig, p as EnvValidationResult, ai as FlagDefinition, aj as FlagDefinitions, ah as FlagValue, r as KEYCLOAK_DEFAULT_ROLES, F as KeycloakCallbacksConfig, K as KeycloakConfig, G as KeycloakJwtFields, q as KeycloakTokenSet, ae as LoginInput, a8 as LoginSchema, ay as OpsAuditActor, aA as OpsAuditEvent, aC as OpsAuditLoggerOptions, aB as OpsAuditRecord, az as OpsAuditResource, ab as PaginationInput, a5 as PaginationSchema, a0 as PasswordSchema, a3 as PersonNameSchema, a2 as PhoneSchema, aq as RateLimitCheckResult, P as RateLimitPreset, J as RedirectCallbackConfig, ak as ResolvedFlags, O as RouteAuditConfig, ad as SearchQueryInput, a7 as SearchQuerySchema, U as SecuritySession, af as SignupInput, a9 as SignupSchema, a1 as SlugSchema, aF as StandardAuditActionType, aE as StandardAuditActions, S as StandardRateLimitPresets, T as TokenRefreshResult, _ as WrapperPresets, ao as buildAllowlist, I as buildAuthCookies, Z as buildErrorBody, M as buildKeycloakCallbacks, e as buildPagination, Y as buildRateLimitHeaders, aw as buildRateLimitResponseHeaders, L as buildRedirectCallback, y as buildTokenRefreshParams, n as checkEnvVars, at as checkRateLimit, c as classifyError, aR as clearStoredBetaCode, aJ as createAuditActor, aK as createAuditLogger, aM as createBetaClient, an as createFeatureFlags, as as createMemoryRateLimitStore, a4 as createSafeTextSchema, am as detectStage, aG as extractAuditIp, aI as extractAuditRequestId, aH as extractAuditUserAgent, X as extractClientIp, aN as fetchBetaSettings, l as getBoolEnv, B as getEndSessionEndpoint, o as getEnvSummary, m as getIntEnv, k as getOptionalEnv, au as getRateLimitStatus, j as getRequiredEnv, aQ as getStoredBetaCode, z as getTokenEndpoint, w as hasAllRoles, u as hasAnyRole, t as hasRole, ap as isAllowlisted, i as isApiError, x as isTokenExpired, s as parseKeycloakRoles, D as refreshKeycloakToken, av as resetRateLimitForKey, ax as resolveIdentifier, W as resolveRateLimitIdentifier, aP as storeBetaCode, aO as validateBetaCode, v as validateEnvVars } from './env-CYKVNpLl.js';
|
|
3
3
|
export { c as constantTimeEqual, b as containsHtml, a as containsUrls, e as escapeHtml, g as getCorrelationId, d as sanitizeApiError, s as stripHtml } from './security-BvLXaQkv.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
|
@@ -172,5 +172,117 @@ declare function isValidBearerToken(request: {
|
|
|
172
172
|
get(name: string): string | null;
|
|
173
173
|
};
|
|
174
174
|
}, secret: string | undefined): boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Verify cron/sync endpoint authentication using a bearer token.
|
|
177
|
+
*
|
|
178
|
+
* Extracts the Bearer token from the Authorization header and compares
|
|
179
|
+
* it against `CRON_SECRET` (or a custom secret) using timing-safe comparison.
|
|
180
|
+
* Returns a 401/500 Response on failure, or `null` on success.
|
|
181
|
+
*
|
|
182
|
+
* @param request - Incoming request (must have headers.get)
|
|
183
|
+
* @param secret - Custom secret to compare against. Defaults to `process.env.CRON_SECRET`.
|
|
184
|
+
* @returns `null` if authorized, or a JSON Response (401/500) if not.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* export async function POST(request: NextRequest) {
|
|
189
|
+
* const authError = verifyCronAuth(request)
|
|
190
|
+
* if (authError) return authError
|
|
191
|
+
* // ... handle cron job
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
declare function verifyCronAuth(request: {
|
|
196
|
+
headers: {
|
|
197
|
+
get(name: string): string | null;
|
|
198
|
+
};
|
|
199
|
+
}, secret?: string): Response | null;
|
|
200
|
+
/**
|
|
201
|
+
* Extract the client IP address from a request, checking proxy headers.
|
|
202
|
+
*
|
|
203
|
+
* Checks in order:
|
|
204
|
+
* 1. `cf-connecting-ip` (Cloudflare)
|
|
205
|
+
* 2. `x-real-ip` (Nginx)
|
|
206
|
+
* 3. `x-forwarded-for` (first IP from comma-separated list)
|
|
207
|
+
* 4. Falls back to `'unknown'`
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* const ip = getClientIp(request)
|
|
212
|
+
* await auditLog({ action: 'login', ip })
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
declare function getClientIp(request: {
|
|
216
|
+
headers: {
|
|
217
|
+
get(name: string): string | null;
|
|
218
|
+
};
|
|
219
|
+
}): string;
|
|
220
|
+
/** Shape expected by rate limit response helpers. */
|
|
221
|
+
interface RateLimitResult {
|
|
222
|
+
limit: number;
|
|
223
|
+
remaining: number;
|
|
224
|
+
resetMs: number;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Create a 429 "Too Many Requests" response with RFC-compliant rate limit headers.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* if (!result.allowed) {
|
|
232
|
+
* return rateLimitResponse(result)
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
declare function rateLimitResponse(result: RateLimitResult): Response;
|
|
237
|
+
/**
|
|
238
|
+
* Add rate limit headers to an existing response.
|
|
239
|
+
*
|
|
240
|
+
* Use this to attach rate limit info to successful responses so clients
|
|
241
|
+
* can see their remaining quota.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* const response = new Response(JSON.stringify(data), { status: 200 })
|
|
246
|
+
* return addRateLimitHeaders(response, result)
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
declare function addRateLimitHeaders(response: Response, result: RateLimitResult): Response;
|
|
250
|
+
/**
|
|
251
|
+
* Safe Zod validation — returns { success, data?, errors? } without throwing.
|
|
252
|
+
* Useful for API routes where you want to return validation errors as JSON.
|
|
253
|
+
*
|
|
254
|
+
* Uses structural typing for the schema parameter so platform-core
|
|
255
|
+
* doesn't depend on Zod directly.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* const result = safeValidate(MySchema, await request.json())
|
|
260
|
+
* if (!result.success) {
|
|
261
|
+
* return new Response(JSON.stringify({ errors: result.errors }), { status: 400 })
|
|
262
|
+
* }
|
|
263
|
+
* const data = result.data // fully typed
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare function safeValidate<T>(schema: {
|
|
267
|
+
safeParse: (data: unknown) => {
|
|
268
|
+
success: boolean;
|
|
269
|
+
data?: T;
|
|
270
|
+
error?: {
|
|
271
|
+
issues: Array<{
|
|
272
|
+
path: (string | number)[];
|
|
273
|
+
message: string;
|
|
274
|
+
}>;
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
}, data: unknown): {
|
|
278
|
+
success: true;
|
|
279
|
+
data: T;
|
|
280
|
+
} | {
|
|
281
|
+
success: false;
|
|
282
|
+
errors: Array<{
|
|
283
|
+
field: string;
|
|
284
|
+
message: string;
|
|
285
|
+
}>;
|
|
286
|
+
};
|
|
175
287
|
|
|
176
|
-
export { RateLimitOptions, RateLimitRule, RateLimitStore, type RedisRateLimitClient, type RedisRateLimitStoreOptions, createRedisRateLimitStore, enforceRateLimit, errorResponse, extractBearerToken, isValidBearerToken, zodErrorResponse };
|
|
288
|
+
export { RateLimitOptions, type RateLimitResult, RateLimitRule, RateLimitStore, type RedisRateLimitClient, type RedisRateLimitStoreOptions, addRateLimitHeaders, createRedisRateLimitStore, enforceRateLimit, errorResponse, extractBearerToken, getClientIp, isValidBearerToken, rateLimitResponse, safeValidate, verifyCronAuth, zodErrorResponse };
|
package/dist/auth.js
CHANGED
|
@@ -38,6 +38,7 @@ __export(auth_exports, {
|
|
|
38
38
|
StandardAuditActions: () => StandardAuditActions,
|
|
39
39
|
StandardRateLimitPresets: () => StandardRateLimitPresets,
|
|
40
40
|
WrapperPresets: () => WrapperPresets,
|
|
41
|
+
addRateLimitHeaders: () => addRateLimitHeaders,
|
|
41
42
|
buildAllowlist: () => buildAllowlist,
|
|
42
43
|
buildAuthCookies: () => buildAuthCookies,
|
|
43
44
|
buildErrorBody: () => buildErrorBody,
|
|
@@ -72,6 +73,7 @@ __export(auth_exports, {
|
|
|
72
73
|
extractClientIp: () => extractClientIp,
|
|
73
74
|
fetchBetaSettings: () => fetchBetaSettings,
|
|
74
75
|
getBoolEnv: () => getBoolEnv,
|
|
76
|
+
getClientIp: () => getClientIp,
|
|
75
77
|
getCorrelationId: () => getCorrelationId,
|
|
76
78
|
getEndSessionEndpoint: () => getEndSessionEndpoint,
|
|
77
79
|
getEnvSummary: () => getEnvSummary,
|
|
@@ -89,15 +91,18 @@ __export(auth_exports, {
|
|
|
89
91
|
isTokenExpired: () => isTokenExpired,
|
|
90
92
|
isValidBearerToken: () => isValidBearerToken,
|
|
91
93
|
parseKeycloakRoles: () => parseKeycloakRoles,
|
|
94
|
+
rateLimitResponse: () => rateLimitResponse,
|
|
92
95
|
refreshKeycloakToken: () => refreshKeycloakToken,
|
|
93
96
|
resetRateLimitForKey: () => resetRateLimitForKey,
|
|
94
97
|
resolveIdentifier: () => resolveIdentifier,
|
|
95
98
|
resolveRateLimitIdentifier: () => resolveRateLimitIdentifier,
|
|
99
|
+
safeValidate: () => safeValidate,
|
|
96
100
|
sanitizeApiError: () => sanitizeApiError,
|
|
97
101
|
storeBetaCode: () => storeBetaCode,
|
|
98
102
|
stripHtml: () => stripHtml,
|
|
99
103
|
validateBetaCode: () => validateBetaCode,
|
|
100
104
|
validateEnvVars: () => validateEnvVars,
|
|
105
|
+
verifyCronAuth: () => verifyCronAuth,
|
|
101
106
|
zodErrorResponse: () => zodErrorResponse
|
|
102
107
|
});
|
|
103
108
|
module.exports = __toCommonJS(auth_exports);
|
|
@@ -666,6 +671,12 @@ var CommonRateLimits = {
|
|
|
666
671
|
limit: 10,
|
|
667
672
|
windowSeconds: 3600,
|
|
668
673
|
blockDurationSeconds: 3600
|
|
674
|
+
},
|
|
675
|
+
/** Beta code validation: 5/min with 5min block (prevents brute force guessing) */
|
|
676
|
+
betaValidation: {
|
|
677
|
+
limit: 5,
|
|
678
|
+
windowSeconds: 60,
|
|
679
|
+
blockDurationSeconds: 300
|
|
669
680
|
}
|
|
670
681
|
};
|
|
671
682
|
function createMemoryRateLimitStore() {
|
|
@@ -1209,6 +1220,71 @@ function isValidBearerToken(request, secret) {
|
|
|
1209
1220
|
if (!token) return false;
|
|
1210
1221
|
return constantTimeEqual(token, secret);
|
|
1211
1222
|
}
|
|
1223
|
+
function verifyCronAuth(request, secret) {
|
|
1224
|
+
const authHeader = request.headers.get("authorization");
|
|
1225
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
1226
|
+
return new Response(
|
|
1227
|
+
JSON.stringify({ error: "Missing authorization" }),
|
|
1228
|
+
{ status: 401, headers: { "Content-Type": "application/json" } }
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
const token = authHeader.slice(7);
|
|
1232
|
+
const expectedSecret = secret ?? process.env.CRON_SECRET;
|
|
1233
|
+
if (!expectedSecret) {
|
|
1234
|
+
console.error("[verifyCronAuth] CRON_SECRET not configured");
|
|
1235
|
+
return new Response(
|
|
1236
|
+
JSON.stringify({ error: "Server configuration error" }),
|
|
1237
|
+
{ status: 500, headers: { "Content-Type": "application/json" } }
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
if (!constantTimeEqual(token, expectedSecret)) {
|
|
1241
|
+
return new Response(
|
|
1242
|
+
JSON.stringify({ error: "Invalid authorization" }),
|
|
1243
|
+
{ status: 401, headers: { "Content-Type": "application/json" } }
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
return null;
|
|
1247
|
+
}
|
|
1248
|
+
function getClientIp(request) {
|
|
1249
|
+
return request.headers.get("cf-connecting-ip") || request.headers.get("x-real-ip") || request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || "unknown";
|
|
1250
|
+
}
|
|
1251
|
+
function rateLimitResponse(result) {
|
|
1252
|
+
const retryAfter = Math.ceil(result.resetMs / 1e3);
|
|
1253
|
+
const resetTimestamp = Math.ceil(Date.now() / 1e3 + result.resetMs / 1e3);
|
|
1254
|
+
return new Response(
|
|
1255
|
+
JSON.stringify({ error: "Too many requests", retryAfter }),
|
|
1256
|
+
{
|
|
1257
|
+
status: 429,
|
|
1258
|
+
headers: {
|
|
1259
|
+
"Content-Type": "application/json",
|
|
1260
|
+
"X-RateLimit-Limit": String(result.limit),
|
|
1261
|
+
"X-RateLimit-Remaining": "0",
|
|
1262
|
+
"X-RateLimit-Reset": String(resetTimestamp),
|
|
1263
|
+
"Retry-After": String(retryAfter)
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
function addRateLimitHeaders(response, result) {
|
|
1269
|
+
const resetTimestamp = Math.ceil(Date.now() / 1e3 + result.resetMs / 1e3);
|
|
1270
|
+
response.headers.set("X-RateLimit-Limit", String(result.limit));
|
|
1271
|
+
response.headers.set("X-RateLimit-Remaining", String(result.remaining));
|
|
1272
|
+
response.headers.set("X-RateLimit-Reset", String(resetTimestamp));
|
|
1273
|
+
return response;
|
|
1274
|
+
}
|
|
1275
|
+
function safeValidate(schema, data) {
|
|
1276
|
+
const result = schema.safeParse(data);
|
|
1277
|
+
if (result.success) {
|
|
1278
|
+
return { success: true, data: result.data };
|
|
1279
|
+
}
|
|
1280
|
+
return {
|
|
1281
|
+
success: false,
|
|
1282
|
+
errors: (result.error?.issues || []).map((issue) => ({
|
|
1283
|
+
field: issue.path.join("."),
|
|
1284
|
+
message: issue.message
|
|
1285
|
+
}))
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1212
1288
|
|
|
1213
1289
|
// src/auth/beta-client.ts
|
|
1214
1290
|
var DEFAULT_CONFIG = {
|
|
@@ -1427,6 +1503,7 @@ function getEnvSummary(keys) {
|
|
|
1427
1503
|
StandardAuditActions,
|
|
1428
1504
|
StandardRateLimitPresets,
|
|
1429
1505
|
WrapperPresets,
|
|
1506
|
+
addRateLimitHeaders,
|
|
1430
1507
|
buildAllowlist,
|
|
1431
1508
|
buildAuthCookies,
|
|
1432
1509
|
buildErrorBody,
|
|
@@ -1461,6 +1538,7 @@ function getEnvSummary(keys) {
|
|
|
1461
1538
|
extractClientIp,
|
|
1462
1539
|
fetchBetaSettings,
|
|
1463
1540
|
getBoolEnv,
|
|
1541
|
+
getClientIp,
|
|
1464
1542
|
getCorrelationId,
|
|
1465
1543
|
getEndSessionEndpoint,
|
|
1466
1544
|
getEnvSummary,
|
|
@@ -1478,15 +1556,18 @@ function getEnvSummary(keys) {
|
|
|
1478
1556
|
isTokenExpired,
|
|
1479
1557
|
isValidBearerToken,
|
|
1480
1558
|
parseKeycloakRoles,
|
|
1559
|
+
rateLimitResponse,
|
|
1481
1560
|
refreshKeycloakToken,
|
|
1482
1561
|
resetRateLimitForKey,
|
|
1483
1562
|
resolveIdentifier,
|
|
1484
1563
|
resolveRateLimitIdentifier,
|
|
1564
|
+
safeValidate,
|
|
1485
1565
|
sanitizeApiError,
|
|
1486
1566
|
storeBetaCode,
|
|
1487
1567
|
stripHtml,
|
|
1488
1568
|
validateBetaCode,
|
|
1489
1569
|
validateEnvVars,
|
|
1570
|
+
verifyCronAuth,
|
|
1490
1571
|
zodErrorResponse
|
|
1491
1572
|
});
|
|
1492
1573
|
//# sourceMappingURL=auth.js.map
|