@payez/next-mvp 3.2.2 → 3.2.3
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.
|
@@ -96,5 +96,7 @@ export interface MvpMiddlewareOptions {
|
|
|
96
96
|
onRefreshFailure?: (status: number, isNetworkError: boolean) => void;
|
|
97
97
|
/** Additional paths to bypass middleware (beyond /api/auth/ and /api/session/) */
|
|
98
98
|
bypassPaths?: string[];
|
|
99
|
+
/** Paths exempt from RBAC checks (auth still enforced, just no page-permission check) */
|
|
100
|
+
rbacExemptPaths?: string[];
|
|
99
101
|
}
|
|
100
102
|
export declare function createMvpMiddleware(options?: MvpMiddlewareOptions): (request: NextRequest) => Promise<NextResponse>;
|
|
@@ -232,6 +232,7 @@ function createMvpMiddleware(options = {}) {
|
|
|
232
232
|
const viabilityEndpoint = options.viabilityEndpoint || '/api/session/viability';
|
|
233
233
|
const refreshEndpoint = options.refreshEndpoint || '/api/auth/refresh';
|
|
234
234
|
const bypassPaths = options.bypassPaths || [];
|
|
235
|
+
const rbacExemptPaths = options.rbacExemptPaths || [];
|
|
235
236
|
return async function middleware(request) {
|
|
236
237
|
const { pathname, searchParams } = request.nextUrl;
|
|
237
238
|
// =========================================================================
|
|
@@ -287,6 +288,7 @@ function createMvpMiddleware(options = {}) {
|
|
|
287
288
|
circuitBreaker: cb,
|
|
288
289
|
logger: log,
|
|
289
290
|
refreshEndpoint,
|
|
291
|
+
rbacExemptPaths,
|
|
290
292
|
onRefreshSuccess: options.onRefreshSuccess,
|
|
291
293
|
onRefreshFailure: options.onRefreshFailure,
|
|
292
294
|
});
|
|
@@ -351,7 +353,7 @@ async function executeDecision(request, decision, pathname, sessionPointer, sess
|
|
|
351
353
|
const safeCallback = getSafeCallbackUrl(pathname);
|
|
352
354
|
switch (decision.type) {
|
|
353
355
|
case 'allow':
|
|
354
|
-
return handleAllow(request, pathname, sessionPointer, sessionStatus);
|
|
356
|
+
return handleAllow(request, pathname, sessionPointer, sessionStatus, opts.rbacExemptPaths);
|
|
355
357
|
case 'redirect':
|
|
356
358
|
return redirectTo(request, decision.location, decision.clearCookies);
|
|
357
359
|
case 'service_error':
|
|
@@ -363,11 +365,12 @@ async function executeDecision(request, decision, pathname, sessionPointer, sess
|
|
|
363
365
|
/** Paths that must never be RBAC-checked (they are RBAC redirect targets) */
|
|
364
366
|
const RBAC_EXEMPT_PATHS = ['/error', '/unauthorized', '/service-unavailable'];
|
|
365
367
|
/** Handle 'allow' decision - run RBAC if enabled */
|
|
366
|
-
async function handleAllow(request, pathname, sessionPointer, sessionStatus) {
|
|
368
|
+
async function handleAllow(request, pathname, sessionPointer, sessionStatus, rbacExemptPaths = []) {
|
|
367
369
|
const isPublic = (0, route_config_1.isUnauthenticatedRoute)(pathname);
|
|
368
370
|
if ((0, rbac_check_1.isRBACEnabled)() && !isPublic && sessionPointer.exists) {
|
|
369
|
-
// Skip RBAC for error/fallback pages
|
|
370
|
-
if (RBAC_EXEMPT_PATHS.some(p => pathname.startsWith(p))
|
|
371
|
+
// Skip RBAC for error/fallback pages (prevent redirect loops) and app-configured exempt paths
|
|
372
|
+
if (RBAC_EXEMPT_PATHS.some(p => pathname.startsWith(p)) ||
|
|
373
|
+
rbacExemptPaths.some(p => pathname.startsWith(p))) {
|
|
371
374
|
return server_1.NextResponse.next();
|
|
372
375
|
}
|
|
373
376
|
if (!sessionPointer.clientId) {
|
package/package.json
CHANGED
|
@@ -108,6 +108,8 @@ export interface MvpMiddlewareOptions {
|
|
|
108
108
|
onRefreshFailure?: (status: number, isNetworkError: boolean) => void;
|
|
109
109
|
/** Additional paths to bypass middleware (beyond /api/auth/ and /api/session/) */
|
|
110
110
|
bypassPaths?: string[];
|
|
111
|
+
/** Paths exempt from RBAC checks (auth still enforced, just no page-permission check) */
|
|
112
|
+
rbacExemptPaths?: string[];
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
// =============================================================================
|
|
@@ -336,6 +338,7 @@ export function createMvpMiddleware(options: MvpMiddlewareOptions = {}) {
|
|
|
336
338
|
const viabilityEndpoint = options.viabilityEndpoint || '/api/session/viability';
|
|
337
339
|
const refreshEndpoint = options.refreshEndpoint || '/api/auth/refresh';
|
|
338
340
|
const bypassPaths = options.bypassPaths || [];
|
|
341
|
+
const rbacExemptPaths = options.rbacExemptPaths || [];
|
|
339
342
|
|
|
340
343
|
return async function middleware(request: NextRequest): Promise<NextResponse> {
|
|
341
344
|
const { pathname, searchParams } = request.nextUrl;
|
|
@@ -400,6 +403,7 @@ export function createMvpMiddleware(options: MvpMiddlewareOptions = {}) {
|
|
|
400
403
|
circuitBreaker: cb,
|
|
401
404
|
logger: log,
|
|
402
405
|
refreshEndpoint,
|
|
406
|
+
rbacExemptPaths,
|
|
403
407
|
onRefreshSuccess: options.onRefreshSuccess,
|
|
404
408
|
onRefreshFailure: options.onRefreshFailure,
|
|
405
409
|
});
|
|
@@ -474,6 +478,7 @@ interface ExecuteOptions {
|
|
|
474
478
|
circuitBreaker: CircuitBreakerProvider;
|
|
475
479
|
logger: MiddlewareLogger;
|
|
476
480
|
refreshEndpoint: string;
|
|
481
|
+
rbacExemptPaths: string[];
|
|
477
482
|
onRefreshSuccess?: () => void;
|
|
478
483
|
onRefreshFailure?: (status: number, isNetworkError: boolean) => void;
|
|
479
484
|
}
|
|
@@ -491,7 +496,7 @@ async function executeDecision(
|
|
|
491
496
|
|
|
492
497
|
switch (decision.type) {
|
|
493
498
|
case 'allow':
|
|
494
|
-
return handleAllow(request, pathname, sessionPointer, sessionStatus);
|
|
499
|
+
return handleAllow(request, pathname, sessionPointer, sessionStatus, opts.rbacExemptPaths);
|
|
495
500
|
|
|
496
501
|
case 'redirect':
|
|
497
502
|
return redirectTo(request, decision.location, decision.clearCookies);
|
|
@@ -512,13 +517,15 @@ async function handleAllow(
|
|
|
512
517
|
request: NextRequest,
|
|
513
518
|
pathname: string,
|
|
514
519
|
sessionPointer: SessionPointer,
|
|
515
|
-
sessionStatus: SessionStatus
|
|
520
|
+
sessionStatus: SessionStatus,
|
|
521
|
+
rbacExemptPaths: string[] = []
|
|
516
522
|
): Promise<NextResponse> {
|
|
517
523
|
const isPublic = isUnauthenticatedRoute(pathname);
|
|
518
524
|
|
|
519
525
|
if (isRBACEnabled() && !isPublic && sessionPointer.exists) {
|
|
520
|
-
// Skip RBAC for error/fallback pages
|
|
521
|
-
if (RBAC_EXEMPT_PATHS.some(p => pathname.startsWith(p))
|
|
526
|
+
// Skip RBAC for error/fallback pages (prevent redirect loops) and app-configured exempt paths
|
|
527
|
+
if (RBAC_EXEMPT_PATHS.some(p => pathname.startsWith(p)) ||
|
|
528
|
+
rbacExemptPaths.some(p => pathname.startsWith(p))) {
|
|
522
529
|
return NextResponse.next();
|
|
523
530
|
}
|
|
524
531
|
|