Skip to content

Commit 7f0cac8

Browse files
dmoernerclaude
andauthored
fix(clerk-js): complete the Safari ITP touch hop in redirect (#9308)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 355839d commit 7f0cac8

3 files changed

Lines changed: 12 additions & 8 deletions

File tree

.changeset/smart-jars-repeat.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Complete the Safari ITP cookie refresh when `setActive({ redirectUrl })` navigates.
6+
7+
Safari's ITP caps the client cookie at 7 days when it is re-issued from a fetch, so `setActive()` routes its redirect through `/v1/client/touch` to restore the full lifetime. That navigation was immediately followed by a second one to the undecorated redirect URL, which superseded it and aborted the touch request before it completed, leaving the cookie capped.
8+
9+
This applies to flows that pass `redirectUrl` without a `navigate` callback — email link sign-in, the password reset success screen, the OAuth popup flow, and direct `setActive({ session, redirectUrl })` calls — in apps where Clerk performs a full page navigation rather than handing off to a router. Users still landed on the correct page, so the only symptom was Safari sessions ending after 7 days and returning devices being challenged as if they were new.

packages/clerk-js/src/core/__tests__/clerk.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ describe('Clerk singleton', () => {
349349
const redirectUrl = new URL((sut.navigate as ReturnType<typeof vi.fn>).mock.calls[0][0]);
350350
expect(redirectUrl.pathname).toEqual('/v1/client/touch');
351351
expect(redirectUrl.searchParams.get('redirect_url')).toEqual(`${mockWindowLocation.href}/redirect-url-path`);
352+
// A second navigate would supersede the touch hop and abort it before it completes.
353+
expect(sut.navigate).toHaveBeenCalledTimes(1);
352354
});
353355

354356
it('does not redirect the user to the /v1/client/touch endpoint if the cookie_expires_at is more than 8 days away', async () => {

packages/clerk-js/src/core/clerk.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,14 +1864,7 @@ export class Clerk implements ClerkInterface {
18641864
);
18651865
}
18661866
} else if (redirectUrl) {
1867-
if (this.client.isEligibleForTouch()) {
1868-
const absoluteRedirectUrl = new URL(redirectUrl, window.location.href);
1869-
const redirectUrlWithAuth = this.buildUrlWithAuth(
1870-
this.client.buildTouchUrl({ redirectUrl: absoluteRedirectUrl }),
1871-
);
1872-
await this.navigate(redirectUrlWithAuth);
1873-
}
1874-
await this.navigate(redirectUrl);
1867+
await this.navigate(this.#decorateUrlWithTouch(redirectUrl));
18751868
}
18761869
});
18771870
}

0 commit comments

Comments
 (0)