Skip to content

Commit 91dc54f

Browse files
committed
fix(backend): auth _next/data requests when __client_uat cookie is absent
1 parent ab9efa2 commit 91dc54f

3 files changed

Lines changed: 46 additions & 2 deletions

File tree

.changeset/solid-yaks-enter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/backend': patch
3+
---
4+
5+
Fixes a bug where `getAuth()` returns unauthenticated during Next.js Pages Router client-side navigations (such as `_next/data` requests), when the `__client_uat` cookie is missing.

packages/backend/src/tokens/__tests__/request.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,43 @@ describe('tokens.authenticateRequest(options)', () => {
988988
expect(requestState.toAuth()).toBeNull();
989989
});
990990

991+
test('cookieToken: returns signed in for non-document request when no clientUat but valid session token (production)', async () => {
992+
server.use(
993+
http.get('https://api.clerk.test/v1/jwks', () => {
994+
return HttpResponse.json(mockJwks);
995+
}),
996+
);
997+
998+
const requestState = await authenticateRequest(
999+
mockRequestWithCookies({ ...defaultHeaders, 'sec-fetch-dest': 'empty' }, { __session: mockJwt }),
1000+
mockOptions({ publishableKey: PK_LIVE }),
1001+
);
1002+
1003+
expect(requestState).toBeSignedIn();
1004+
expect(requestState.toAuth()).toBeSignedInToAuth();
1005+
});
1006+
1007+
test('cookieToken: returns signed in for non-document request when no clientUat but valid session token (development)', async () => {
1008+
server.use(
1009+
http.get('https://api.clerk.test/v1/jwks', () => {
1010+
return HttpResponse.json(mockJwks);
1011+
}),
1012+
);
1013+
1014+
const requestState = await authenticateRequest(
1015+
mockRequestWithCookies(
1016+
{ ...defaultHeaders, 'sec-fetch-dest': 'empty' },
1017+
{ __clerk_db_jwt: 'deadbeef', __session: mockJwt },
1018+
),
1019+
mockOptions({
1020+
secretKey: 'test_deadbeef',
1021+
}),
1022+
);
1023+
1024+
expect(requestState).toBeSignedIn();
1025+
expect(requestState.toAuth()).toBeSignedInToAuth();
1026+
});
1027+
9911028
test('cookieToken: returns handshake when no cookies in development [5y]', async () => {
9921029
const requestState = await authenticateRequest(
9931030
mockRequestWithCookies({}),

packages/backend/src/tokens/request.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,9 @@ export const authenticateRequest: AuthenticateRequest = (async (
607607

608608
// This can eagerly run handshake since client_uat is SameSite=Strict in dev
609609
if (!hasActiveClient && hasSessionToken) {
610-
return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.SessionTokenWithoutClientUAT, '');
610+
if (handshakeService.isRequestEligibleForHandshake()) {
611+
return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.SessionTokenWithoutClientUAT, '');
612+
}
611613
}
612614

613615
if (hasActiveClient && !hasSessionToken) {
@@ -621,7 +623,7 @@ export const authenticateRequest: AuthenticateRequest = (async (
621623
return handleSessionTokenError(decodedErrors[0], 'cookie');
622624
}
623625

624-
if (decodeResult.payload.iat < authenticateContext.clientUat) {
626+
if (authenticateContext.clientUat && decodeResult.payload.iat < authenticateContext.clientUat) {
625627
return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.SessionTokenIATBeforeClientUAT, '');
626628
}
627629

0 commit comments

Comments
 (0)