From 99c6620813fb9077d26a30c67029f5555cef2e9b Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 14 Dec 2023 11:11:13 -0600 Subject: [PATCH 1/5] feat(backend): Support dev handshake where the redirect ends up tainted --- packages/backend/src/tokens/request.ts | 33 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/tokens/request.ts b/packages/backend/src/tokens/request.ts index fa65c36cf2..20f20e07a9 100644 --- a/packages/backend/src/tokens/request.ts +++ b/packages/backend/src/tokens/request.ts @@ -175,7 +175,9 @@ ${err.getFullMessage()}`, async function authenticateRequestWithTokenInCookie() { const { derivedRequestUrl, + devBrowser, isSatellite, + origin, secFetchDest, signInUrl, clientUat: clientUatRaw, @@ -185,12 +187,24 @@ ${err.getFullMessage()}`, const clientUat = parseInt(clientUatRaw || '', 10) || 0; const hasActiveClient = clientUat > 0; const hasSessionToken = !!sessionToken; + const isTaintedDevRedirect = !!devBrowser && origin === 'null'; const isRequestEligibleForMultiDomainSync = isSatellite && secFetchDest === 'document' && !derivedRequestUrl.searchParams.has(constants.QueryParameters.ClerkSynced); + let headers = new Headers({}); + + /** + * In dev, the redirect chain for fetch requests eventually results in a "tainted redirect" in which the origin gets set to "null" and the request is treated as cross-origin, even if it's technically not. + * In order to allow the handshake to succeed in this edge case, we need to add the proper CORS headers to the response. + */ + if (isTaintedDevRedirect) { + headers.set('Access-Control-Allow-Origin', 'null'); + headers.set('Access-Control-Allow-Credentials', 'true'); + } + /** * If we have a handshakeToken, resolve the handshake and attempt to return a definitive signed in or signed out state. */ @@ -202,7 +216,7 @@ ${err.getFullMessage()}`, * Otherwise, check for "known unknown" auth states that we can resolve with a handshake. */ if (instanceType === 'development' && derivedRequestUrl.searchParams.has(constants.Cookies.DevBrowser)) { - const headers = buildRedirectToHandshake(); + headers = buildRedirectToHandshake(); return handshake(authenticateContext, AuthErrorReason.DevBrowserSync, '', headers); } @@ -210,7 +224,7 @@ ${err.getFullMessage()}`, * Begin multi-domain sync flows */ if (instanceType === 'production' && isRequestEligibleForMultiDomainSync) { - const headers = buildRedirectToHandshake(); + headers = buildRedirectToHandshake(); return handshake(authenticateContext, AuthErrorReason.SatelliteCookieNeedsSyncing, '', headers); } @@ -222,7 +236,7 @@ ${err.getFullMessage()}`, const redirectURL = new URL(signInUrl!); redirectURL.searchParams.append(constants.QueryParameters.ClerkRedirectUrl, derivedRequestUrl.toString()); - const headers = new Headers({ location: redirectURL.toString() }); + headers.set('location', redirectURL.toString()); return handshake(authenticateContext, AuthErrorReason.SatelliteCookieNeedsSyncing, '', headers); } @@ -237,7 +251,7 @@ ${err.getFullMessage()}`, } redirectBackToSatelliteUrl.searchParams.append(constants.QueryParameters.ClerkSynced, 'true'); - const headers = new Headers({ location: redirectBackToSatelliteUrl.toString() }); + headers.set('location', redirectBackToSatelliteUrl.toString()); return handshake(authenticateContext, AuthErrorReason.PrimaryRespondsToSyncing, '', headers); } /** @@ -245,31 +259,31 @@ ${err.getFullMessage()}`, */ if (!hasActiveClient && !hasSessionToken) { - return signedOut(authenticateContext, AuthErrorReason.SessionTokenAndUATMissing); + return signedOut(authenticateContext, AuthErrorReason.SessionTokenAndUATMissing, '', headers); } // This can eagerly run handshake since client_uat is SameSite=Strict in dev if (!hasActiveClient && hasSessionToken) { - const headers = buildRedirectToHandshake(); + headers = buildRedirectToHandshake(); return handshake(authenticateContext, AuthErrorReason.SessionTokenWithoutClientUAT, '', headers); } if (hasActiveClient && !hasSessionToken) { - const headers = buildRedirectToHandshake(); + headers = buildRedirectToHandshake(); return handshake(authenticateContext, AuthErrorReason.ClientUATWithoutSessionToken, '', headers); } const decodeResult = decodeJwt(sessionToken!); if (decodeResult.payload.iat < clientUat) { - const headers = buildRedirectToHandshake(); + headers = buildRedirectToHandshake(); return handshake(authenticateContext, AuthErrorReason.SessionTokenOutdated, '', headers); } try { const verifyResult = await verifyToken(sessionToken!, authenticateContext); if (verifyResult) { - return signedIn(authenticateContext, verifyResult); + return signedIn(authenticateContext, verifyResult, headers); } } catch (err) { return handleError(err, 'cookie'); @@ -341,5 +355,6 @@ export const loadOptionsFromCookies = (cookies: ReturnType[ return { sessionTokenInCookie: cookies?.(constants.Cookies.Session), clientUat: cookies?.(constants.Cookies.ClientUat), + devBrowser: cookies?.(constants.Cookies.DevBrowser), }; }; From b103ec762c5d4571248a74b8e9edd578df3faa95 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 14 Dec 2023 16:10:16 -0600 Subject: [PATCH 2/5] feat(backend): Fix tests, only check sec-fetch-dest for handshake elgibility --- integration/tests/handshake.test.ts | 35 +++++++++++ packages/backend/src/constants.ts | 1 + packages/backend/src/tokens/request.ts | 83 ++++++++++++++++---------- 3 files changed, 86 insertions(+), 33 deletions(-) diff --git a/integration/tests/handshake.test.ts b/integration/tests/handshake.test.ts index e4629ca010..23d1926dd8 100644 --- a/integration/tests/handshake.test.ts +++ b/integration/tests/handshake.test.ts @@ -83,6 +83,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie} __client_uat=${clientUat}; __session=${token}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -101,6 +102,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, Authorization: `Bearer ${token}`, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -118,6 +120,7 @@ test.describe('Client handshake @generic', () => { Cookie: `__client_uat=${clientUat}; __session=${token}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -153,6 +156,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie} __client_uat=${clientUat}; __session=${token}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -175,6 +179,7 @@ test.describe('Client handshake @generic', () => { Cookie: `__client_uat=${clientUat}; __session=${token}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -196,6 +201,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, Authorization: `Bearer ${token}`, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -216,6 +222,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie} __client_uat=${clientUat}; __session=${token}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -239,6 +246,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, Authorization: `Bearer ${token}`, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -262,6 +270,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, 'X-Proxy-Url': 'https://example.com/clerk', + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -285,6 +294,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, 'X-Proxy-Url': 'https://example.com/clerk', + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -306,6 +316,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, 'X-Domain': 'localhost:3000', + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -329,6 +340,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, 'X-Domain': 'example.com', + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -347,6 +359,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie} __client_uat=1`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -367,6 +380,7 @@ test.describe('Client handshake @generic', () => { Cookie: `__client_uat=1`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -385,6 +399,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie} __client_uat=0`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -400,6 +415,7 @@ test.describe('Client handshake @generic', () => { Cookie: `__client_uat=0`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -415,6 +431,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -429,6 +446,7 @@ test.describe('Client handshake @generic', () => { headers: new Headers({ 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -496,6 +514,7 @@ test.describe('Client handshake @generic', () => { headers: new Headers({ 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -513,6 +532,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie} __client_uat=${clientUat}; __session=${token}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -535,6 +555,7 @@ test.describe('Client handshake @generic', () => { Cookie: `__client_uat=${clientUat}; __session=${token}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -559,6 +580,7 @@ test.describe('Client handshake @generic', () => { 'X-Secret-Key': config.sk, 'X-Forwarded-Host': 'example.com', 'X-Forwarded-Proto': 'https', + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -581,6 +603,7 @@ test.describe('Client handshake @generic', () => { 'X-Secret-Key': config.sk, 'X-Forwarded-Host': 'example.com', 'X-Forwarded-Proto': 'https', + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -603,6 +626,7 @@ test.describe('Client handshake @generic', () => { 'X-Secret-Key': config.sk, 'X-Forwarded-Host': 'example.com:3213', 'X-Forwarded-Proto': 'https', + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -625,6 +649,7 @@ test.describe('Client handshake @generic', () => { 'X-Secret-Key': config.sk, 'X-Forwarded-Host': 'example.com:3213', 'X-Forwarded-Proto': 'https', + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -646,6 +671,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -669,6 +695,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -688,6 +715,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -707,6 +735,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -726,6 +755,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -746,6 +776,7 @@ test.describe('Client handshake @generic', () => { Cookie: `${devBrowserCookie}`, 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -769,6 +800,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, Cookie: `__clerk_handshake=${handshake}`, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -791,6 +823,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, Cookie: `__clerk_handshake=${handshake}`, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -809,6 +842,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, Cookie: `__clerk_handshake=${handshake}`, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); @@ -828,6 +862,7 @@ test.describe('Client handshake @generic', () => { 'X-Publishable-Key': config.pk, 'X-Secret-Key': config.sk, Cookie: `__clerk_handshake=${handshake}`, + 'Sec-Fetch-Dest': 'document', }), redirect: 'manual', }); diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index 55bb889723..90e1d3476d 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -35,6 +35,7 @@ const Headers = { ForwardedPort: 'x-forwarded-port', ForwardedProto: 'x-forwarded-proto', ForwardedHost: 'x-forwarded-host', + Accept: 'accept', Referrer: 'referer', UserAgent: 'user-agent', Origin: 'origin', diff --git a/packages/backend/src/tokens/request.ts b/packages/backend/src/tokens/request.ts index 20f20e07a9..599572d7f8 100644 --- a/packages/backend/src/tokens/request.ts +++ b/packages/backend/src/tokens/request.ts @@ -47,6 +47,25 @@ function assertSignInUrlFormatAndOrigin(_signInUrl: string, origin: string) { } } +/** + * Currently, a request is only eligible for a handshake if we can say it's *probably* a request for a document, not a fetch or some other exotic request. + * This heuristic should give us a reliable enough signal for browsers that support `Sec-Fetch-Dest` and for those that don't. + */ +function isRequestEligibleForHandshake(authenticateContext: { secFetchDest?: string; accept?: string }) { + const { accept, secFetchDest } = authenticateContext; + + // NOTE: we could also check sec-fetch-mode === navigate here, but according to the spec, sec-fetch-dest: document should indicate that the request is the result of a user navigation. + if (secFetchDest === 'document') { + return true; + } + + if (!secFetchDest && accept?.startsWith('text/html')) { + return true; + } + + return false; +} + export async function authenticateRequest( request: Request, options: AuthenticateRequestOptions, @@ -153,6 +172,20 @@ ${err.getFullMessage()}`, return signedIn(authenticateContext, verifyResult!, headers); } + function handleMaybeHandshakeStatus( + context: typeof authenticateContext, + reason: AuthErrorReason, + message: string, + headers?: Headers, + ) { + if (isRequestEligibleForHandshake(context)) { + // Right now the only usage of passing in different headers is for multi-domain sync, which redirects somewhere else. + // In the future if we want to decorate the handshake redirect with additional headers per call we need to tweak this logic. + return handshake(context, reason, message, headers ?? buildRedirectToHandshake()); + } + return signedOut(context, reason, message, new Headers()); + } + const pk = parsePublishableKey(options.publishableKey, { fatal: true, proxyUrl: options.proxyUrl, @@ -175,9 +208,7 @@ ${err.getFullMessage()}`, async function authenticateRequestWithTokenInCookie() { const { derivedRequestUrl, - devBrowser, isSatellite, - origin, secFetchDest, signInUrl, clientUat: clientUatRaw, @@ -187,24 +218,12 @@ ${err.getFullMessage()}`, const clientUat = parseInt(clientUatRaw || '', 10) || 0; const hasActiveClient = clientUat > 0; const hasSessionToken = !!sessionToken; - const isTaintedDevRedirect = !!devBrowser && origin === 'null'; const isRequestEligibleForMultiDomainSync = isSatellite && secFetchDest === 'document' && !derivedRequestUrl.searchParams.has(constants.QueryParameters.ClerkSynced); - let headers = new Headers({}); - - /** - * In dev, the redirect chain for fetch requests eventually results in a "tainted redirect" in which the origin gets set to "null" and the request is treated as cross-origin, even if it's technically not. - * In order to allow the handshake to succeed in this edge case, we need to add the proper CORS headers to the response. - */ - if (isTaintedDevRedirect) { - headers.set('Access-Control-Allow-Origin', 'null'); - headers.set('Access-Control-Allow-Credentials', 'true'); - } - /** * If we have a handshakeToken, resolve the handshake and attempt to return a definitive signed in or signed out state. */ @@ -216,16 +235,14 @@ ${err.getFullMessage()}`, * Otherwise, check for "known unknown" auth states that we can resolve with a handshake. */ if (instanceType === 'development' && derivedRequestUrl.searchParams.has(constants.Cookies.DevBrowser)) { - headers = buildRedirectToHandshake(); - return handshake(authenticateContext, AuthErrorReason.DevBrowserSync, '', headers); + return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.DevBrowserSync, ''); } /** * Begin multi-domain sync flows */ if (instanceType === 'production' && isRequestEligibleForMultiDomainSync) { - headers = buildRedirectToHandshake(); - return handshake(authenticateContext, AuthErrorReason.SatelliteCookieNeedsSyncing, '', headers); + return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.SatelliteCookieNeedsSyncing, ''); } // Multi-domain development sync flow @@ -236,8 +253,8 @@ ${err.getFullMessage()}`, const redirectURL = new URL(signInUrl!); redirectURL.searchParams.append(constants.QueryParameters.ClerkRedirectUrl, derivedRequestUrl.toString()); - headers.set('location', redirectURL.toString()); - return handshake(authenticateContext, AuthErrorReason.SatelliteCookieNeedsSyncing, '', headers); + const headers = new Headers({ location: redirectURL.toString() }); + return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.SatelliteCookieNeedsSyncing, '', headers); } // Multi-domain development sync flow @@ -251,39 +268,36 @@ ${err.getFullMessage()}`, } redirectBackToSatelliteUrl.searchParams.append(constants.QueryParameters.ClerkSynced, 'true'); - headers.set('location', redirectBackToSatelliteUrl.toString()); - return handshake(authenticateContext, AuthErrorReason.PrimaryRespondsToSyncing, '', headers); + const headers = new Headers({ location: redirectBackToSatelliteUrl.toString() }); + return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.PrimaryRespondsToSyncing, '', headers); } /** * End multi-domain sync flows */ if (!hasActiveClient && !hasSessionToken) { - return signedOut(authenticateContext, AuthErrorReason.SessionTokenAndUATMissing, '', headers); + return signedOut(authenticateContext, AuthErrorReason.SessionTokenAndUATMissing, ''); } // This can eagerly run handshake since client_uat is SameSite=Strict in dev if (!hasActiveClient && hasSessionToken) { - headers = buildRedirectToHandshake(); - return handshake(authenticateContext, AuthErrorReason.SessionTokenWithoutClientUAT, '', headers); + return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.SessionTokenWithoutClientUAT, ''); } if (hasActiveClient && !hasSessionToken) { - headers = buildRedirectToHandshake(); - return handshake(authenticateContext, AuthErrorReason.ClientUATWithoutSessionToken, '', headers); + return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.ClientUATWithoutSessionToken, ''); } const decodeResult = decodeJwt(sessionToken!); if (decodeResult.payload.iat < clientUat) { - headers = buildRedirectToHandshake(); - return handshake(authenticateContext, AuthErrorReason.SessionTokenOutdated, '', headers); + return handleMaybeHandshakeStatus(authenticateContext, AuthErrorReason.SessionTokenOutdated, ''); } try { const verifyResult = await verifyToken(sessionToken!, authenticateContext); if (verifyResult) { - return signedIn(authenticateContext, verifyResult, headers); + return signedIn(authenticateContext, verifyResult); } } catch (err) { return handleError(err, 'cookie'); @@ -302,8 +316,11 @@ ${err.getFullMessage()}`, ].includes(err.reason); if (reasonToHandshake) { - const headers = buildRedirectToHandshake(); - return handshake(authenticateContext, AuthErrorReason.SessionTokenOutdated, err.getFullMessage(), headers); + return handleMaybeHandshakeStatus( + authenticateContext, + AuthErrorReason.SessionTokenOutdated, + err.getFullMessage(), + ); } return signedOut(authenticateContext, err.reason, err.getFullMessage()); } @@ -341,6 +358,7 @@ export const loadOptionsFromHeaders = (headers: ReturnType[ referrer: headers(constants.Headers.Referrer), userAgent: headers(constants.Headers.UserAgent), secFetchDest: headers(constants.Headers.SecFetchDest), + accept: headers(constants.Headers.Accept), }; }; @@ -355,6 +373,5 @@ export const loadOptionsFromCookies = (cookies: ReturnType[ return { sessionTokenInCookie: cookies?.(constants.Cookies.Session), clientUat: cookies?.(constants.Cookies.ClientUat), - devBrowser: cookies?.(constants.Cookies.DevBrowser), }; }; From 412c5f5264b840fbad602682f8b160c5088f28f3 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 14 Dec 2023 16:12:49 -0600 Subject: [PATCH 3/5] chore(repo): Add changeset --- .changeset/itchy-papayas-dress.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/itchy-papayas-dress.md diff --git a/.changeset/itchy-papayas-dress.md b/.changeset/itchy-papayas-dress.md new file mode 100644 index 0000000000..8f2c037f51 --- /dev/null +++ b/.changeset/itchy-papayas-dress.md @@ -0,0 +1,5 @@ +--- +'@clerk/backend': patch +--- + +Update the handshake flow to only trigger for document requests. From 841b0eb04fd17102a1db66ca939960d590d4ccf5 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 14 Dec 2023 16:52:11 -0600 Subject: [PATCH 4/5] fix(backend): Update request unit tests --- packages/backend/src/tokens/request.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/tokens/request.test.ts b/packages/backend/src/tokens/request.test.ts index 66dc24b3e3..0c6230da1f 100644 --- a/packages/backend/src/tokens/request.test.ts +++ b/packages/backend/src/tokens/request.test.ts @@ -123,6 +123,7 @@ export default (QUnit: QUnit) => { const defaultHeaders: Record = { host: 'example.com', 'user-agent': 'Mozilla/TestAgent', + 'sec-fetch-dest': 'document', }; const mockRequest = (headers = {}, requestUrl = 'http://clerk.com/path') => { @@ -272,9 +273,7 @@ export default (QUnit: QUnit) => { test('cookieToken: returns handshake when clientUat is missing or equals to 0 and is satellite and not is synced [11y]', async assert => { const requestState = await authenticateRequest( mockRequestWithCookies( - { - 'Sec-Fetch-Dest': 'document', - }, + {}, { __client_uat: '0', }, @@ -303,6 +302,7 @@ export default (QUnit: QUnit) => { mockRequestWithCookies( { ...defaultHeaders, + 'sec-fetch-dest': 'empty', 'user-agent': '[some-agent]', }, { __client_uat: '0' }, @@ -392,12 +392,13 @@ export default (QUnit: QUnit) => { assert.strictEqual(requestState.toAuth(), null); }); - test('cookieToken: returns undefined when crossOriginReferrer in development and is satellite [6n]', async assert => { + test('cookieToken: returns signedIn when satellite but valid token and clientUat', async assert => { // Scenario: after auth action on Clerk-hosted UIs const requestState = await authenticateRequest( mockRequestWithCookies( { ...defaultHeaders, + 'sec-fetch-dest': 'empty', // this is not a typo, it's intentional to be `referer` to match HTTP header key referer: 'https://clerk.com', }, From 075957839e579abc0e02d4714a7c9769f2b40261 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 14 Dec 2023 17:02:41 -0600 Subject: [PATCH 5/5] fix(fastify): Update snapshot tests --- packages/fastify/src/__snapshots__/constants.test.ts.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/fastify/src/__snapshots__/constants.test.ts.snap b/packages/fastify/src/__snapshots__/constants.test.ts.snap index 7e3776bd19..99998a7f10 100644 --- a/packages/fastify/src/__snapshots__/constants.test.ts.snap +++ b/packages/fastify/src/__snapshots__/constants.test.ts.snap @@ -11,6 +11,7 @@ exports[`constants from environment variables 1`] = ` "Session": "__session", }, "Headers": { + "Accept": "accept", "AuthMessage": "x-clerk-auth-message", "AuthReason": "x-clerk-auth-reason", "AuthStatus": "x-clerk-auth-status",