Skip to content

Commit 7f2adfa

Browse files
authored
Merge branch 'main' into jacek/re-enable-renovate
2 parents d46c802 + ec71511 commit 7f2adfa

30 files changed

Lines changed: 2423 additions & 1199 deletions

.changeset/fix-release-npm-upgrade.md

Lines changed: 0 additions & 2 deletions
This file was deleted.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"@clerk/testing": "workspace:*",
8080
"@commitlint/cli": "^19.8.0",
8181
"@commitlint/config-conventional": "^19.8.0",
82-
"@eslint/eslintrc": "^3.3.1",
82+
"@eslint/eslintrc": "^3.3.5",
8383
"@eslint/js": "9.31.0",
8484
"@faker-js/faker": "^9.9.0",
8585
"@octokit/rest": "^20.1.2",
@@ -107,12 +107,12 @@
107107
"eslint-plugin-import": "2.32.0",
108108
"eslint-plugin-jsdoc": "50.8.0",
109109
"eslint-plugin-jsx-a11y": "6.10.2",
110-
"eslint-plugin-playwright": "2.2.0",
110+
"eslint-plugin-playwright": "2.10.1",
111111
"eslint-plugin-react": "7.37.5",
112112
"eslint-plugin-react-hooks": "5.2.0",
113113
"eslint-plugin-simple-import-sort": "12.1.1",
114114
"eslint-plugin-turbo": "2.5.5",
115-
"eslint-plugin-unused-imports": "4.1.4",
115+
"eslint-plugin-unused-imports": "4.4.1",
116116
"eslint-plugin-yml": "1.18.0",
117117
"execa": "^5.1.1",
118118
"expect-type": "^0.20.0",
@@ -145,7 +145,7 @@
145145
"typedoc-plugin-markdown": "4.6.4",
146146
"typedoc-plugin-replace-text": "4.2.0",
147147
"typescript": "catalog:repo",
148-
"typescript-eslint": "8.38.0",
148+
"typescript-eslint": "8.58.0",
149149
"uuid": "8.3.2",
150150
"vitest": "3.2.4",
151151
"zx": "catalog:repo"

packages/agent-toolkit/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @clerk/agent-toolkit
22

3+
## 0.3.11
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`bedad42`](https://github.com/clerk/javascript/commit/bedad42b3a3bce899e23b38ef0b0f8d5b8d1149d)]:
8+
- @clerk/backend@3.2.7
9+
310
## 0.3.10
411

512
### Patch Changes

packages/agent-toolkit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@clerk/agent-toolkit",
3-
"version": "0.3.10",
3+
"version": "0.3.11",
44
"description": "Clerk Toolkit for AI Agents",
55
"homepage": "https://clerk.com/",
66
"bugs": {

packages/astro/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @clerk/astro
22

3+
## 3.0.11
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`bedad42`](https://github.com/clerk/javascript/commit/bedad42b3a3bce899e23b38ef0b0f8d5b8d1149d)]:
8+
- @clerk/backend@3.2.7
9+
310
## 3.0.10
411

512
### Patch Changes

packages/astro/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@clerk/astro",
3-
"version": "3.0.10",
3+
"version": "3.0.11",
44
"description": "Clerk SDK for Astro",
55
"keywords": [
66
"auth",

packages/backend/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Change Log
22

3+
## 3.2.7
4+
5+
### Patch Changes
6+
7+
- Fix POST requests with `sec-fetch-dest: document` incorrectly triggering handshake redirects, resulting in 405 errors from FAPI. Non-GET requests (e.g. native form submissions) are now excluded from handshake and multi-domain sync eligibility. ([#8045](https://github.com/clerk/javascript/pull/8045)) by [@jacekradko](https://github.com/jacekradko)
8+
39
## 3.2.6
410

511
### Patch Changes

packages/backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@clerk/backend",
3-
"version": "3.2.6",
3+
"version": "3.2.7",
44
"description": "Clerk Backend SDK - REST Client for Backend API & JWT verification utilities",
55
"homepage": "https://clerk.com/",
66
"bugs": {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ describe('HandshakeService', () => {
9494
clerkUrl: new URL('https://example.com'),
9595
frontendApi: 'api.clerk.com',
9696
instanceType: 'production',
97+
method: 'GET',
9798
usesSuffixedCookies: () => true,
9899
secFetchDest: 'document',
99100
accept: 'text/html',
@@ -139,6 +140,25 @@ describe('HandshakeService', () => {
139140
mockAuthenticateContext.accept = 'image/png';
140141
expect(handshakeService.isRequestEligibleForHandshake()).toBe(false);
141142
});
143+
144+
it('should return false for POST requests with document secFetchDest', () => {
145+
mockAuthenticateContext.method = 'POST';
146+
mockAuthenticateContext.secFetchDest = 'document';
147+
expect(handshakeService.isRequestEligibleForHandshake()).toBe(false);
148+
});
149+
150+
it('should return false for PUT requests with document secFetchDest', () => {
151+
mockAuthenticateContext.method = 'PUT';
152+
mockAuthenticateContext.secFetchDest = 'document';
153+
expect(handshakeService.isRequestEligibleForHandshake()).toBe(false);
154+
});
155+
156+
it('should return false for POST requests with text/html accept without secFetchDest', () => {
157+
mockAuthenticateContext.method = 'POST';
158+
mockAuthenticateContext.secFetchDest = undefined;
159+
mockAuthenticateContext.accept = 'text/html';
160+
expect(handshakeService.isRequestEligibleForHandshake()).toBe(false);
161+
});
142162
});
143163

144164
describe('buildRedirectToHandshake', () => {

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

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,6 +1925,39 @@ describe('tokens.authenticateRequest(options)', () => {
19251925
});
19261926
});
19271927

1928+
test('does not trigger handshake for cross-origin POST document request on primary domain', async () => {
1929+
const cookieStr = Object.entries({
1930+
__session: mockJwt,
1931+
__client_uat: '12345',
1932+
})
1933+
.map(([k, v]) => `${k}=${v}`)
1934+
.join(';');
1935+
1936+
const request = new Request('https://primary.com/dashboard', {
1937+
method: 'POST',
1938+
headers: {
1939+
...defaultHeaders,
1940+
referer: 'https://satellite.com/form',
1941+
'sec-fetch-dest': 'document',
1942+
cookie: cookieStr,
1943+
},
1944+
});
1945+
1946+
const requestState = await authenticateRequest(request, {
1947+
...mockOptions(),
1948+
publishableKey: PK_LIVE,
1949+
domain: 'primary.com',
1950+
isSatellite: false,
1951+
signInUrl: 'https://primary.com/sign-in',
1952+
});
1953+
1954+
expect(requestState).toBeSignedIn({
1955+
domain: 'primary.com',
1956+
isSatellite: false,
1957+
signInUrl: 'https://primary.com/sign-in',
1958+
});
1959+
});
1960+
19281961
test('does not trigger handshake for non-document requests', async () => {
19291962
const request = mockRequestWithCookies(
19301963
{
@@ -2205,4 +2238,62 @@ describe('tokens.authenticateRequest(options)', () => {
22052238
});
22062239
});
22072240
});
2241+
2242+
describe('POST requests with sec-fetch-dest: document', () => {
2243+
const mockPostRequest = (headers = {}, cookies = {}, requestUrl = 'http://clerk.com/path') => {
2244+
const cookieStr = Object.entries(cookies)
2245+
.map(([k, v]) => `${k}=${v}`)
2246+
.join(';');
2247+
2248+
return new Request(requestUrl, {
2249+
method: 'POST',
2250+
headers: { ...defaultHeaders, 'sec-fetch-dest': 'document', cookie: cookieStr, ...headers },
2251+
});
2252+
};
2253+
2254+
test('returns signed out instead of handshake when clientUat > 0 and no cookieToken', async () => {
2255+
const requestState = await authenticateRequest(
2256+
mockPostRequest({}, { __client_uat: '12345' }),
2257+
mockOptions({ secretKey: 'deadbeef', publishableKey: PK_LIVE }),
2258+
);
2259+
2260+
expect(requestState).toBeSignedOut({ reason: AuthErrorReason.ClientUATWithoutSessionToken });
2261+
});
2262+
2263+
test('returns signed out instead of handshake for satellite app needing sync', async () => {
2264+
const requestState = await authenticateRequest(
2265+
mockPostRequest({}, { __client_uat: '0' }),
2266+
mockOptions({
2267+
publishableKey: PK_LIVE,
2268+
secretKey: 'deadbeef',
2269+
isSatellite: true,
2270+
signInUrl: 'https://primary.dev/sign-in',
2271+
domain: 'satellite.dev',
2272+
}),
2273+
);
2274+
2275+
expect(requestState).toBeSignedOut({
2276+
reason: AuthErrorReason.SessionTokenAndUATMissing,
2277+
isSatellite: true,
2278+
signInUrl: 'https://primary.dev/sign-in',
2279+
domain: 'satellite.dev',
2280+
});
2281+
});
2282+
2283+
test('returns signed out instead of handshake when clientUat > cookieToken.iat', async () => {
2284+
const requestState = await authenticateRequest(
2285+
mockPostRequest(
2286+
{},
2287+
{
2288+
__clerk_db_jwt: 'deadbeef',
2289+
__client_uat: `${mockJwtPayload.iat + 10}`,
2290+
__session: mockJwt,
2291+
},
2292+
),
2293+
mockOptions(),
2294+
);
2295+
2296+
expect(requestState).toBeSignedOut({ reason: AuthErrorReason.SessionTokenIATBeforeClientUAT });
2297+
});
2298+
});
22082299
});

0 commit comments

Comments
 (0)