From da5955c3006c8794c3143ace7cad12ba2fed261a Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Wed, 30 Apr 2025 22:50:57 -0500 Subject: [PATCH 1/4] Pass __clerk_api_version to handshake redirect --- .changeset/cuddly-numbers-hear.md | 5 +++++ packages/backend/src/tokens/request.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/cuddly-numbers-hear.md diff --git a/.changeset/cuddly-numbers-hear.md b/.changeset/cuddly-numbers-hear.md new file mode 100644 index 00000000000..017b546c7ea --- /dev/null +++ b/.changeset/cuddly-numbers-hear.md @@ -0,0 +1,5 @@ +--- +'@clerk/backend': patch +--- + +Fix an issue where the handshake redirect was not respecting the supported Clerk API version specified in `@clerk/backend`. diff --git a/packages/backend/src/tokens/request.ts b/packages/backend/src/tokens/request.ts index 9d714029618..46088354378 100644 --- a/packages/backend/src/tokens/request.ts +++ b/packages/backend/src/tokens/request.ts @@ -2,7 +2,7 @@ import type { Match, MatchFunction } from '@clerk/shared/pathToRegexp'; import { match } from '@clerk/shared/pathToRegexp'; import type { JwtPayload } from '@clerk/types'; -import { constants } from '../constants'; +import { constants, SUPPORTED_BAPI_VERSION } from '../constants'; import type { TokenCarrier } from '../errors'; import { TokenVerificationError, TokenVerificationErrorReason } from '../errors'; import { decodeJwt } from '../jwt/verifyJwt'; @@ -123,6 +123,7 @@ export async function authenticateRequest( const frontendApiNoProtocol = authenticateContext.frontendApi.replace(/http(s)?:\/\//, ''); const url = new URL(`https://${frontendApiNoProtocol}/v1/client/handshake`); + url.searchParams.append('__clerk_api_version', SUPPORTED_BAPI_VERSION); url.searchParams.append('redirect_url', redirectUrl?.href || ''); url.searchParams.append( constants.QueryParameters.SuffixedCookies, From a652f56f486e0bb0673606af553920c094a7dc68 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 1 May 2025 11:17:24 -0500 Subject: [PATCH 2/4] re-order params --- packages/backend/src/tokens/request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/tokens/request.ts b/packages/backend/src/tokens/request.ts index 46088354378..83df960523e 100644 --- a/packages/backend/src/tokens/request.ts +++ b/packages/backend/src/tokens/request.ts @@ -123,8 +123,8 @@ export async function authenticateRequest( const frontendApiNoProtocol = authenticateContext.frontendApi.replace(/http(s)?:\/\//, ''); const url = new URL(`https://${frontendApiNoProtocol}/v1/client/handshake`); - url.searchParams.append('__clerk_api_version', SUPPORTED_BAPI_VERSION); url.searchParams.append('redirect_url', redirectUrl?.href || ''); + url.searchParams.append('__clerk_api_version', SUPPORTED_BAPI_VERSION); url.searchParams.append( constants.QueryParameters.SuffixedCookies, authenticateContext.usesSuffixedCookies().toString(), From 8d4e805395613c7cd22cc800418ce1bf4e39ad28 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 1 May 2025 13:03:47 -0500 Subject: [PATCH 3/4] Update handshake tests to be more resilient to URL changes --- integration/tests/handshake.test.ts | 222 ++++++++++++++++++---------- 1 file changed, 140 insertions(+), 82 deletions(-) diff --git a/integration/tests/handshake.test.ts b/integration/tests/handshake.test.ts index af70df88937..e49d78ade49 100644 --- a/integration/tests/handshake.test.ts +++ b/integration/tests/handshake.test.ts @@ -156,11 +156,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie${devBrowserQuery}`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('expired session token - prod', async () => { @@ -179,11 +182,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('expired session token - authorization header - prod', async () => { @@ -203,11 +209,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('early session token - dev', async () => { @@ -226,11 +235,12 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-nbf${devBrowserQuery}`, - ); + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe('session-token-nbf'); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('early session token - authorization header - dev', async () => { @@ -250,11 +260,12 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-nbf${devBrowserQuery}`, - ); + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe('session-token-nbf'); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('proxyUrl - dev', async () => { @@ -274,11 +285,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://example.com/clerk/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie${devBrowserQuery}`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe('https://example.com/clerk/v1/client/handshake'); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('proxyUrl - prod', async () => { @@ -298,11 +312,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://example.com/clerk/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe('https://example.com/clerk/v1/client/handshake'); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('domain - dev', async () => { @@ -322,11 +339,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie${devBrowserQuery}`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('domain - prod', async () => { @@ -346,11 +366,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://clerk.example.com/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe('https://clerk.example.com/v1/client/handshake'); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('missing session token, positive uat - dev', async () => { @@ -367,11 +390,12 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=client-uat-but-no-session-token${devBrowserQuery}`, - ); + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe('client-uat-but-no-session-token'); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('missing session token, positive uat - prod', async () => { @@ -388,11 +412,12 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=client-uat-but-no-session-token`, - ); + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe('client-uat-but-no-session-token'); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('missing session token, 0 uat (indicating signed out) - dev', async () => { @@ -489,11 +514,12 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://clerk.example.com/v1/client/handshake?redirect_url=${encodeURIComponent( - app.serverUrl + '/', - )}&suffixed_cookies=false&__clerk_hs_reason=satellite-needs-syncing`, - ); + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe('https://clerk.example.com/v1/client/handshake'); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe('satellite-needs-syncing'); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('signed out satellite - dev', async () => { @@ -526,11 +552,12 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=dev-browser-missing`, - ); + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe('dev-browser-missing'); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('redirect url - path and qs - dev', async () => { @@ -549,11 +576,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}hello%3Ffoo%3Dbar&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie${devBrowserQuery}`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/hello?foo=bar`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('redirect url - path and qs - prod', async () => { @@ -572,11 +602,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}hello%3Ffoo%3Dbar&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/hello?foo=bar`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('redirect url - proxy - dev', async () => { @@ -597,9 +630,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=https%3A%2F%2Fexample.com%2Fhello%3Ffoo%3Dbar&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie${devBrowserQuery}`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe('https://example.com/hello?foo=bar'); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('redirect url - proxy - prod', async () => { @@ -620,9 +658,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=https%3A%2F%2Fexample.com%2Fhello%3Ffoo%3Dbar&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe('https://example.com/hello?foo=bar'); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('redirect url - proxy with port - dev', async () => { @@ -643,9 +686,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=https%3A%2F%2Fexample.com%3A3213%2Fhello%3Ffoo%3Dbar&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie${devBrowserQuery}`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe('https://example.com:3213/hello?foo=bar'); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('redirect url - proxy with port - prod', async () => { @@ -666,9 +714,14 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=https%3A%2F%2Fexample.com%3A3213%2Fhello%3Ffoo%3Dbar&suffixed_cookies=false&__clerk_hs_reason=session-token-expired-refresh-non-eligible-no-refresh-cookie`, + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe('https://example.com:3213/hello?foo=bar'); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe( + 'session-token-expired-refresh-non-eligible-no-refresh-cookie', ); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); }); test('Handshake result - dev - nominal', async () => { @@ -793,11 +846,13 @@ test.describe('Client handshake @generic', () => { redirect: 'manual', }); expect(res.status).toBe(307); - expect(res.headers.get('location')).toBe( - `https://${config.pkHost}/v1/client/handshake?redirect_url=${encodeURIComponent( - `${app.serverUrl}/`, - )}&suffixed_cookies=false&__clerk_hs_reason=dev-browser-sync&__clerk_db_jwt=asdf`, - ); + const locationUrl = new URL(res.headers.get('location')); + expect(locationUrl.origin + locationUrl.pathname).toBe(`https://${config.pkHost}/v1/client/handshake`); + expect(locationUrl.searchParams.get('redirect_url')).toBe(`${app.serverUrl}/`); + expect(locationUrl.searchParams.get('__clerk_hs_reason')).toBe('dev-browser-sync'); + expect(locationUrl.searchParams.has('__clerk_api_version')).toBe(true); + expect(locationUrl.searchParams.get('suffixed_cookies')).toBe('false'); + expect(locationUrl.searchParams.get('__clerk_db_jwt')).toBe('asdf'); }); test('Handshake result - prod - nominal', async () => { @@ -904,7 +959,9 @@ test.describe('Client handshake with organization activation @nextjs', () => { test.beforeAll('setup local jwks server', async () => { // Start the jwks server await new Promise(resolve => jwksServer.listen(0, resolve)); - app = await startAppWithOrganizationSyncOptions(`http://localhost:${jwksServer.address().port}`); + const address = jwksServer.address(); + const port = typeof address === 'string' ? 0 : address?.port; + app = await startAppWithOrganizationSyncOptions(`http://localhost:${port}`); }); test.afterAll('setup local Clerk API mock', async () => { @@ -1285,8 +1342,9 @@ test.describe('Client handshake with an organization activation avoids infinite test.beforeAll('setup local jwks server', async () => { // Start the jwks server await new Promise(resolve => jwksServer.listen(0, resolve)); - - thisApp = await startAppWithOrganizationSyncOptions(`http://localhost:${jwksServer.address().port}`); + const address = jwksServer.address(); + const port = typeof address === 'string' ? 0 : address?.port; + thisApp = await startAppWithOrganizationSyncOptions(`http://localhost:${port}`); }); test.afterAll('setup local Clerk API mock', async () => { From fe88ec07ce914718a35ddfd697a71ac3a9cf244f Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 1 May 2025 13:38:45 -0500 Subject: [PATCH 4/4] remove unused import --- packages/backend/src/tokens/request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/tokens/request.ts b/packages/backend/src/tokens/request.ts index 15a29425838..882aae8158a 100644 --- a/packages/backend/src/tokens/request.ts +++ b/packages/backend/src/tokens/request.ts @@ -1,6 +1,6 @@ import type { JwtPayload } from '@clerk/types'; -import { constants, SUPPORTED_BAPI_VERSION } from '../constants'; +import { constants } from '../constants'; import type { TokenCarrier } from '../errors'; import { TokenVerificationError, TokenVerificationErrorReason } from '../errors'; import { decodeJwt } from '../jwt/verifyJwt';