Skip to content

Commit

Permalink
feat(nextjs): Detect infinite interstitial redirect loop in middleware
Browse files Browse the repository at this point in the history
When in development, we want to prevent infinite interstitial redirection loops.
We incrementally set a `__clerk_redirection_loop` cookie, and after looping 6 times, we throw an error.
We also utilize the `referer` header to skip the prefetch requests.
  • Loading branch information
anagstef committed Jun 8, 2023
1 parent 06c06c3 commit 5fede9e
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/soft-snakes-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/nextjs': patch
---

Detect infinite interstitial redirect loop in middleware and throw error to inform the user.
29 changes: 28 additions & 1 deletion packages/nextjs/src/server/authMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type RouteMatcherWithNextTypedRoutes =
| NextTypedRoute
| (string & {});

const INFINITE_REDIRECTION_LOOP_COOKIE = '__clerk_redirection_loop';

/**
* The default ideal matcher that excludes the _next directory (internals) and all static files,
* but it will match the root route (/) and any routes that start with /api or /trpc.
Expand Down Expand Up @@ -160,7 +162,8 @@ const authMiddleware: AuthMiddleware = (...args: unknown[]) => {
return handleUnknownState(requestState);
} else if (requestState.isInterstitial) {
logger.debug('authenticateRequest state is interstitial', requestState);
return handleInterstitialState(requestState, options);
const res = handleInterstitialState(requestState, options);
return assertInfiniteRedirectionLoop(req, res);
}

const auth = Object.assign(requestState.toAuth(), {
Expand Down Expand Up @@ -287,3 +290,27 @@ const isRequestMethodIndicatingApiRoute = (req: NextRequest): boolean => {
const requestMethod = req.method.toLowerCase();
return !['get', 'head', 'options'].includes(requestMethod);
};

// When in development, we want to prevent infinite interstitial redirection loops.
// We incrementally set a `__clerk_redirection_loop` cookie, and when it loops 6 times, we throw an error.
// We also utilize the `referer` header to skip the prefetch requests.
const assertInfiniteRedirectionLoop = (req: NextRequest, res: NextResponse): NextResponse => {
if (!isDevelopmentFromApiKey(SECRET_KEY)) {
return res;
}

const infiniteRedirectsCounter = Number(req.cookies.get(INFINITE_REDIRECTION_LOOP_COOKIE)?.value) || 0;
if (infiniteRedirectsCounter === 6) {
throw new Error('Oops! Looks like you ended up in an Infinite Redirection Loop.');
}

// Skip the prefetch requests (when hovering a Next Link element)
if (req.headers.get('referer') === req.url) {
res.cookies.set({
name: INFINITE_REDIRECTION_LOOP_COOKIE,
value: `${infiniteRedirectsCounter + 1}`,
maxAge: 3,
});
}
return res;
};

0 comments on commit 5fede9e

Please sign in to comment.