1
- import type { AuthObject , RequestState } from '@clerk/backend' ;
2
- import { buildRequestUrl , constants , TokenVerificationErrorReason } from '@clerk/backend' ;
1
+ import type { AuthenticateRequestOptions , AuthObject } from '@clerk/backend' ;
2
+ import { AuthStatus , buildRequestUrl , constants } from '@clerk/backend' ;
3
3
import { DEV_BROWSER_JWT_MARKER , setDevBrowserJWTInURL } from '@clerk/shared/devBrowser' ;
4
4
import { isDevelopmentFromSecretKey } from '@clerk/shared/keys' ;
5
5
import { eventMethodCalled } from '@clerk/shared/telemetry' ;
@@ -10,15 +10,10 @@ import { NextResponse } from 'next/server';
10
10
11
11
import { isRedirect , mergeResponses , paths , setHeader , stringifyHeaders } from '../utils' ;
12
12
import { withLogger } from '../utils/debugLogger' ;
13
- import { authenticateRequest , handleInterstitialState , handleUnknownState } from './authenticateRequest' ;
13
+ import { authenticateRequest } from './authenticateRequest' ;
14
14
import { clerkClient } from './clerkClient' ;
15
15
import { SECRET_KEY } from './constants' ;
16
- import {
17
- clockSkewDetected ,
18
- infiniteRedirectLoopDetected ,
19
- informAboutProtectedRouteInfo ,
20
- receivedRequestForIgnoredRoute ,
21
- } from './errors' ;
16
+ import { informAboutProtectedRouteInfo , receivedRequestForIgnoredRoute } from './errors' ;
22
17
import { redirectToSignIn } from './redirect' ;
23
18
import type { NextMiddlewareResult , WithAuthOptions } from './types' ;
24
19
import { isDevAccountPortalOrigin } from './url' ;
@@ -39,8 +34,6 @@ type RouteMatcherWithNextTypedRoutes = Autocomplete<
39
34
WithPathPatternWildcard < ExcludeRootPath < NextTypedRoute > > | NextTypedRoute
40
35
> ;
41
36
42
- const INFINITE_REDIRECTION_LOOP_COOKIE = '__clerk_redirection_loop' ;
43
-
44
37
/**
45
38
* The default ideal matcher that excludes the _next directory (internals) and all static files,
46
39
* but it will match the root route (/) and any routes that start with /api or /trpc.
@@ -191,22 +184,50 @@ const authMiddleware: AuthMiddleware = (...args: unknown[]) => {
191
184
return setHeader ( beforeAuthRes , constants . Headers . AuthReason , 'redirect' ) ;
192
185
}
193
186
194
- const requestState = await authenticateRequest ( req , options ) ;
195
- if ( requestState . isUnknown ) {
196
- logger . debug ( 'authenticateRequest state is unknown' , requestState ) ;
197
- return handleUnknownState ( requestState ) ;
198
- } else if ( requestState . isInterstitial && isApiRoute ( req ) ) {
199
- logger . debug ( 'authenticateRequest state is interstitial in an API route' , requestState ) ;
200
- return handleUnknownState ( requestState ) ;
201
- } else if ( requestState . isInterstitial ) {
202
- logger . debug ( 'authenticateRequest state is interstitial' , requestState ) ;
187
+ const devBrowserToken =
188
+ req . nextUrl . searchParams . get ( '__clerk_db_jwt' ) || req . cookies . get ( '__clerk_db_jwt' ) ?. value || '' ;
189
+ const handshakeToken =
190
+ req . nextUrl . searchParams . get ( '__clerk_handshake' ) || req . cookies . get ( '__clerk_handshake' ) ?. value || '' ;
191
+
192
+ // TODO: fix type discrepancy between WithAuthOptions and AuthenticateRequestOptions
193
+ const requestState = await authenticateRequest ( req , {
194
+ ...options ,
195
+ devBrowserToken,
196
+ handshakeToken,
197
+ } as AuthenticateRequestOptions ) ;
198
+ const requestStateHeaders = requestState . headers ;
199
+
200
+ const locationHeader = requestStateHeaders ?. get ( 'location' ) ;
203
201
204
- assertClockSkew ( requestState , options ) ;
202
+ // triggering a handshake redirect
203
+ if ( locationHeader ) {
204
+ return new Response ( null , { status : 307 , headers : requestStateHeaders } ) ;
205
+ }
205
206
206
- const res = handleInterstitialState ( requestState , options ) ;
207
- return assertInfiniteRedirectionLoop ( req , res , options , requestState ) ;
207
+ if (
208
+ requestState . status === AuthStatus . Handshake ||
209
+ requestState . status === AuthStatus . Unknown ||
210
+ requestState . status === AuthStatus . Interstitial
211
+ ) {
212
+ console . log ( requestState ) ;
213
+ throw new Error ( 'Unexpected handshake or unknown state without redirect' ) ;
208
214
}
209
215
216
+ // if (requestState.isUnknown) {
217
+ // logger.debug('authenticateRequest state is unknown', requestState);
218
+ // return handleUnknownState(requestState);
219
+ // } else if (requestState.isInterstitial && isApiRoute(req)) {
220
+ // logger.debug('authenticateRequest state is interstitial in an API route', requestState);
221
+ // return handleUnknownState(requestState);
222
+ // } else if (requestState.isInterstitial) {
223
+ // logger.debug('authenticateRequest state is interstitial', requestState);
224
+
225
+ // assertClockSkew(requestState, options);
226
+
227
+ // const res = handleInterstitialState(requestState, options);
228
+ // return assertInfiniteRedirectionLoop(req, res, options, requestState);
229
+ // }
230
+
210
231
const auth = Object . assign ( requestState . toAuth ( ) , {
211
232
isPublicRoute : isPublicRoute ( req ) ,
212
233
isApiRoute : isApiRoute ( req ) ,
@@ -227,7 +248,15 @@ const authMiddleware: AuthMiddleware = (...args: unknown[]) => {
227
248
logger . debug ( `Added ${ constants . Headers . EnableDebug } on request` ) ;
228
249
}
229
250
230
- return decorateRequest ( req , finalRes , requestState ) ;
251
+ const result = decorateRequest ( req , finalRes , requestState ) || NextResponse . next ( ) ;
252
+
253
+ if ( requestStateHeaders ) {
254
+ requestStateHeaders . forEach ( ( value , key ) => {
255
+ result . headers . append ( key , value ) ;
256
+ } ) ;
257
+ }
258
+
259
+ return result ;
231
260
} ) ;
232
261
} ;
233
262
@@ -352,55 +381,6 @@ const isRequestMethodIndicatingApiRoute = (req: NextRequest): boolean => {
352
381
return ! [ 'get' , 'head' , 'options' ] . includes ( requestMethod ) ;
353
382
} ;
354
383
355
- /**
356
- * In development, attempt to detect clock skew based on the requestState. This check should run when requestState.isInterstitial is true. If detected, we throw an error.
357
- */
358
- const assertClockSkew = ( requestState : RequestState , opts : AuthMiddlewareParams ) : void => {
359
- if ( ! isDevelopmentFromSecretKey ( opts . secretKey || SECRET_KEY ) ) {
360
- return ;
361
- }
362
-
363
- if ( requestState . reason === TokenVerificationErrorReason . TokenNotActiveYet ) {
364
- throw new Error ( clockSkewDetected ( requestState . message ) ) ;
365
- }
366
- } ;
367
-
368
- // When in development, we want to prevent infinite interstitial redirection loops.
369
- // We incrementally set a `__clerk_redirection_loop` cookie, and when it loops 6 times, we throw an error.
370
- // We also utilize the `referer` header to skip the prefetch requests.
371
- const assertInfiniteRedirectionLoop = (
372
- req : NextRequest ,
373
- res : NextResponse ,
374
- opts : AuthMiddlewareParams ,
375
- requestState : RequestState ,
376
- ) : NextResponse => {
377
- if ( ! isDevelopmentFromSecretKey ( opts . secretKey || SECRET_KEY ) ) {
378
- return res ;
379
- }
380
-
381
- const infiniteRedirectsCounter = Number ( req . cookies . get ( INFINITE_REDIRECTION_LOOP_COOKIE ) ?. value ) || 0 ;
382
- if ( infiniteRedirectsCounter === 6 ) {
383
- // Infinite redirect detected, is it clock skew?
384
- // We check for token-expired here because it can be a valid, recoverable scenario, but in a redirect loop a token-expired error likely indicates clock skew.
385
- if ( requestState . reason === TokenVerificationErrorReason . TokenExpired ) {
386
- throw new Error ( clockSkewDetected ( requestState . message ) ) ;
387
- }
388
-
389
- // Not clock skew, return general error
390
- throw new Error ( infiniteRedirectLoopDetected ( ) ) ;
391
- }
392
-
393
- // Skip the prefetch requests (when hovering a Next Link element)
394
- if ( req . headers . get ( 'referer' ) === req . url ) {
395
- res . cookies . set ( {
396
- name : INFINITE_REDIRECTION_LOOP_COOKIE ,
397
- value : `${ infiniteRedirectsCounter + 1 } ` ,
398
- maxAge : 3 ,
399
- } ) ;
400
- }
401
- return res ;
402
- } ;
403
-
404
384
const withNormalizedClerkUrl = ( req : NextRequest ) : WithClerkUrl < NextRequest > => {
405
385
const clerkUrl = req . nextUrl . clone ( ) ;
406
386
0 commit comments