Skip to content

Commit 625c8fe

Browse files
committed
Super admin redirects
1 parent e4fe85e commit 625c8fe

1 file changed

Lines changed: 9 additions & 8 deletions

File tree

apps/webapp/app/services/routeBuilders/dashboardBuilder.server.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,16 @@ export async function authenticateAndAuthorize<TParams, TSearchParams, TContext
9696
}
9797

9898
if (options.authorization && !isAuthorized(auth.ability, options.authorization)) {
99-
// Logged in but lacking the permission. By default throw a
100-
// permission-denied 403 — both loader and action wrappers throw this
101-
// response, so it bubbles to the nearest route ErrorBoundary, where
102-
// RouteErrorDisplay renders the permission panel. Routes that prefer a
103-
// redirect (e.g. credential endpoints with no UI) opt out by setting
104-
// unauthorizedRedirect.
105-
if (options.unauthorizedRedirect) {
106-
return { ok: false, response: redirect(options.unauthorizedRedirect) };
99+
// Super-admin gates must not reveal that the route exists, so they redirect
100+
// away rather than render the panel. A redirect is also used by routes that
101+
// opt in via unauthorizedRedirect (e.g. credential endpoints with no UI).
102+
const isSuperGate = "requireSuper" in options.authorization;
103+
if (options.unauthorizedRedirect || isSuperGate) {
104+
return { ok: false, response: redirect(options.unauthorizedRedirect ?? "/") };
107105
}
106+
// Role-based denial: throw a permission-denied 403. Both loader and action
107+
// wrappers throw this, so it bubbles to the nearest route ErrorBoundary,
108+
// where RouteErrorDisplay renders the permission panel.
108109
return { ok: false, response: permissionDeniedResponse(options.authorization.message) };
109110
}
110111

0 commit comments

Comments
 (0)