@digilogiclabs/platform-core 1.9.0 → 1.11.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 +217 -3
- package/dist/auth.d.ts +217 -3
- package/dist/auth.js +251 -0
- package/dist/auth.js.map +1 -1
- package/dist/auth.mjs +244 -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/dist/migrate.js +0 -0
- package/package.json +11 -11
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,219 @@ 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
|
+
};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Federated Logout Handler for Keycloak + Auth.js
|
|
290
|
+
*
|
|
291
|
+
* Builds a Next.js route handler that:
|
|
292
|
+
* 1. Revokes the refresh token with Keycloak
|
|
293
|
+
* 2. Returns an HTML page that clears all auth cookies
|
|
294
|
+
* 3. Auto-redirects to Keycloak's OIDC logout endpoint
|
|
295
|
+
*
|
|
296
|
+
* Uses HTML 200 (not 307 redirect) because browsers may not reliably
|
|
297
|
+
* process Set-Cookie headers on redirect responses through CDN proxies.
|
|
298
|
+
*
|
|
299
|
+
* Edge-runtime compatible.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```typescript
|
|
303
|
+
* // app/api/auth/federated-logout/route.ts
|
|
304
|
+
* import { buildFederatedLogoutHandler } from '@digilogiclabs/platform-core/auth'
|
|
305
|
+
* import { auth } from '@/auth'
|
|
306
|
+
*
|
|
307
|
+
* export const dynamic = 'force-dynamic'
|
|
308
|
+
*
|
|
309
|
+
* export const GET = buildFederatedLogoutHandler({
|
|
310
|
+
* auth,
|
|
311
|
+
* domain: '.digilogiclabs.com',
|
|
312
|
+
* baseUrlFallback: 'https://digilogiclabs.com',
|
|
313
|
+
* })
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
interface FederatedLogoutConfig {
|
|
317
|
+
/**
|
|
318
|
+
* Auth.js `auth()` function — used to get the session's idToken.
|
|
319
|
+
* Must return an object with an optional `idToken` field.
|
|
320
|
+
*/
|
|
321
|
+
auth: () => Promise<{
|
|
322
|
+
idToken?: string;
|
|
323
|
+
} | null>;
|
|
324
|
+
/**
|
|
325
|
+
* Production cookie domain (e.g. '.digilogiclabs.com').
|
|
326
|
+
* Used to clear cookies scoped to the root domain.
|
|
327
|
+
*/
|
|
328
|
+
domain: string;
|
|
329
|
+
/**
|
|
330
|
+
* Fallback base URL when NEXTAUTH_URL/AUTH_URL are not set.
|
|
331
|
+
* e.g. 'https://digilogiclabs.com'
|
|
332
|
+
*/
|
|
333
|
+
baseUrlFallback: string;
|
|
334
|
+
/**
|
|
335
|
+
* Additional cookie names to clear beyond the standard Auth.js set.
|
|
336
|
+
* e.g. ['admin-session']
|
|
337
|
+
*/
|
|
338
|
+
extraCookies?: string[];
|
|
339
|
+
/**
|
|
340
|
+
* Optional error reporter. Called on missing issuer and token revocation failures.
|
|
341
|
+
* If not provided, errors are silently ignored.
|
|
342
|
+
*/
|
|
343
|
+
onError?: (message: string, error?: unknown) => void;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Build a Next.js route handler for federated logout with Keycloak.
|
|
347
|
+
*
|
|
348
|
+
* The returned handler:
|
|
349
|
+
* - Validates the callbackUrl against an allowlist (open redirect protection)
|
|
350
|
+
* - Revokes the Keycloak refresh token
|
|
351
|
+
* - Clears all Auth.js cookies (both standard and __Secure- prefixed)
|
|
352
|
+
* - Returns an HTML page that auto-redirects to Keycloak's OIDC logout
|
|
353
|
+
*/
|
|
354
|
+
declare function buildFederatedLogoutHandler(config: FederatedLogoutConfig): (request: Request) => Promise<Response>;
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Lazy Rate Limit Store
|
|
358
|
+
*
|
|
359
|
+
* Creates a singleton rate limit store backed by Redis with automatic
|
|
360
|
+
* fallback to in-memory when Redis is unavailable.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* // lib/rate-limit-store.ts
|
|
365
|
+
* import { createLazyRateLimitStore } from '@digilogiclabs/platform-core/auth'
|
|
366
|
+
* import { getRedisClient } from './redis'
|
|
367
|
+
*
|
|
368
|
+
* export const getRateLimitStore = createLazyRateLimitStore(getRedisClient)
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
interface LazyRateLimitStoreOptions {
|
|
373
|
+
/** Redis key prefix for rate limit keys (default: 'rl:') */
|
|
374
|
+
keyPrefix?: string;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Create a lazy singleton rate limit store factory.
|
|
378
|
+
*
|
|
379
|
+
* Returns a getter function that:
|
|
380
|
+
* - On first call, attempts to get a Redis client and create a Redis-backed store
|
|
381
|
+
* - If Redis is unavailable, returns undefined (enforceRateLimit falls back to in-memory)
|
|
382
|
+
* - Caches the result for subsequent calls
|
|
383
|
+
*
|
|
384
|
+
* @param getRedis - Function that returns a Redis client (or null/undefined if unavailable)
|
|
385
|
+
* @param options - Optional configuration
|
|
386
|
+
* @returns A getter function that returns the RateLimitStore or undefined
|
|
387
|
+
*/
|
|
388
|
+
declare function createLazyRateLimitStore(getRedis: () => RedisRateLimitClient | null | undefined, options?: LazyRateLimitStoreOptions): () => RateLimitStore | undefined;
|
|
175
389
|
|
|
176
|
-
export { RateLimitOptions, RateLimitRule, RateLimitStore, type RedisRateLimitClient, type RedisRateLimitStoreOptions, createRedisRateLimitStore, enforceRateLimit, errorResponse, extractBearerToken, isValidBearerToken, zodErrorResponse };
|
|
390
|
+
export { type FederatedLogoutConfig, type LazyRateLimitStoreOptions, RateLimitOptions, type RateLimitResult, RateLimitRule, RateLimitStore, type RedisRateLimitClient, type RedisRateLimitStoreOptions, addRateLimitHeaders, buildFederatedLogoutHandler, createLazyRateLimitStore, 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,219 @@ 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
|
+
};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Federated Logout Handler for Keycloak + Auth.js
|
|
290
|
+
*
|
|
291
|
+
* Builds a Next.js route handler that:
|
|
292
|
+
* 1. Revokes the refresh token with Keycloak
|
|
293
|
+
* 2. Returns an HTML page that clears all auth cookies
|
|
294
|
+
* 3. Auto-redirects to Keycloak's OIDC logout endpoint
|
|
295
|
+
*
|
|
296
|
+
* Uses HTML 200 (not 307 redirect) because browsers may not reliably
|
|
297
|
+
* process Set-Cookie headers on redirect responses through CDN proxies.
|
|
298
|
+
*
|
|
299
|
+
* Edge-runtime compatible.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```typescript
|
|
303
|
+
* // app/api/auth/federated-logout/route.ts
|
|
304
|
+
* import { buildFederatedLogoutHandler } from '@digilogiclabs/platform-core/auth'
|
|
305
|
+
* import { auth } from '@/auth'
|
|
306
|
+
*
|
|
307
|
+
* export const dynamic = 'force-dynamic'
|
|
308
|
+
*
|
|
309
|
+
* export const GET = buildFederatedLogoutHandler({
|
|
310
|
+
* auth,
|
|
311
|
+
* domain: '.digilogiclabs.com',
|
|
312
|
+
* baseUrlFallback: 'https://digilogiclabs.com',
|
|
313
|
+
* })
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
interface FederatedLogoutConfig {
|
|
317
|
+
/**
|
|
318
|
+
* Auth.js `auth()` function — used to get the session's idToken.
|
|
319
|
+
* Must return an object with an optional `idToken` field.
|
|
320
|
+
*/
|
|
321
|
+
auth: () => Promise<{
|
|
322
|
+
idToken?: string;
|
|
323
|
+
} | null>;
|
|
324
|
+
/**
|
|
325
|
+
* Production cookie domain (e.g. '.digilogiclabs.com').
|
|
326
|
+
* Used to clear cookies scoped to the root domain.
|
|
327
|
+
*/
|
|
328
|
+
domain: string;
|
|
329
|
+
/**
|
|
330
|
+
* Fallback base URL when NEXTAUTH_URL/AUTH_URL are not set.
|
|
331
|
+
* e.g. 'https://digilogiclabs.com'
|
|
332
|
+
*/
|
|
333
|
+
baseUrlFallback: string;
|
|
334
|
+
/**
|
|
335
|
+
* Additional cookie names to clear beyond the standard Auth.js set.
|
|
336
|
+
* e.g. ['admin-session']
|
|
337
|
+
*/
|
|
338
|
+
extraCookies?: string[];
|
|
339
|
+
/**
|
|
340
|
+
* Optional error reporter. Called on missing issuer and token revocation failures.
|
|
341
|
+
* If not provided, errors are silently ignored.
|
|
342
|
+
*/
|
|
343
|
+
onError?: (message: string, error?: unknown) => void;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Build a Next.js route handler for federated logout with Keycloak.
|
|
347
|
+
*
|
|
348
|
+
* The returned handler:
|
|
349
|
+
* - Validates the callbackUrl against an allowlist (open redirect protection)
|
|
350
|
+
* - Revokes the Keycloak refresh token
|
|
351
|
+
* - Clears all Auth.js cookies (both standard and __Secure- prefixed)
|
|
352
|
+
* - Returns an HTML page that auto-redirects to Keycloak's OIDC logout
|
|
353
|
+
*/
|
|
354
|
+
declare function buildFederatedLogoutHandler(config: FederatedLogoutConfig): (request: Request) => Promise<Response>;
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Lazy Rate Limit Store
|
|
358
|
+
*
|
|
359
|
+
* Creates a singleton rate limit store backed by Redis with automatic
|
|
360
|
+
* fallback to in-memory when Redis is unavailable.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* // lib/rate-limit-store.ts
|
|
365
|
+
* import { createLazyRateLimitStore } from '@digilogiclabs/platform-core/auth'
|
|
366
|
+
* import { getRedisClient } from './redis'
|
|
367
|
+
*
|
|
368
|
+
* export const getRateLimitStore = createLazyRateLimitStore(getRedisClient)
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
interface LazyRateLimitStoreOptions {
|
|
373
|
+
/** Redis key prefix for rate limit keys (default: 'rl:') */
|
|
374
|
+
keyPrefix?: string;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Create a lazy singleton rate limit store factory.
|
|
378
|
+
*
|
|
379
|
+
* Returns a getter function that:
|
|
380
|
+
* - On first call, attempts to get a Redis client and create a Redis-backed store
|
|
381
|
+
* - If Redis is unavailable, returns undefined (enforceRateLimit falls back to in-memory)
|
|
382
|
+
* - Caches the result for subsequent calls
|
|
383
|
+
*
|
|
384
|
+
* @param getRedis - Function that returns a Redis client (or null/undefined if unavailable)
|
|
385
|
+
* @param options - Optional configuration
|
|
386
|
+
* @returns A getter function that returns the RateLimitStore or undefined
|
|
387
|
+
*/
|
|
388
|
+
declare function createLazyRateLimitStore(getRedis: () => RedisRateLimitClient | null | undefined, options?: LazyRateLimitStoreOptions): () => RateLimitStore | undefined;
|
|
175
389
|
|
|
176
|
-
export { RateLimitOptions, RateLimitRule, RateLimitStore, type RedisRateLimitClient, type RedisRateLimitStoreOptions, createRedisRateLimitStore, enforceRateLimit, errorResponse, extractBearerToken, isValidBearerToken, zodErrorResponse };
|
|
390
|
+
export { type FederatedLogoutConfig, type LazyRateLimitStoreOptions, RateLimitOptions, type RateLimitResult, RateLimitRule, RateLimitStore, type RedisRateLimitClient, type RedisRateLimitStoreOptions, addRateLimitHeaders, buildFederatedLogoutHandler, createLazyRateLimitStore, createRedisRateLimitStore, enforceRateLimit, errorResponse, extractBearerToken, getClientIp, isValidBearerToken, rateLimitResponse, safeValidate, verifyCronAuth, zodErrorResponse };
|