Skip to content

Commit ea8cd9b

Browse files
authored
feat(clerk-js): Enable persisted clients by default (#4250)
1 parent 8568534 commit ea8cd9b

File tree

9 files changed

+49
-24
lines changed

9 files changed

+49
-24
lines changed

.changeset/shy-sloths-laugh.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"@clerk/clerk-js": minor
3+
---
4+
5+
We recently shipped an experimental feature to persist the Clerk client (under `persistClient` flag) as an opt-in. This allows for matching a user's device with a client. We want to test this behavior with more users, so we're making it opt-out as the next step. After more successful testing we'll remove the experimental flag and enable it by default.
6+
7+
If you're encountering issues, please open an issue. You can disable this new behavior like so:
8+
9+
```js
10+
// React
11+
<ClerkProvider experimental={{ persistClient: false }} />
12+
13+
// Vanilla JS
14+
await clerk.load({ experimental: { persistClient: false } })
15+
```
16+

integration/presets/envs.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ const withEmailCodes = base
3535
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-email-codes').pk)
3636
.setEnvVariable('private', 'CLERK_ENCRYPTION_KEY', constants.E2E_CLERK_ENCRYPTION_KEY || 'a-key');
3737

38-
const withEmailCodes_persist_client = withEmailCodes
38+
const withEmailCodes_destroy_client = withEmailCodes
3939
.clone()
40-
.setEnvVariable('public', 'EXPERIMENTAL_PERSIST_CLIENT', 'true');
40+
.setEnvVariable('public', 'EXPERIMENTAL_PERSIST_CLIENT', 'false');
4141

4242
const withEmailLinks = base
4343
.clone()
@@ -91,7 +91,7 @@ const withDynamicKeys = withEmailCodes
9191
export const envs = {
9292
base,
9393
withEmailCodes,
94-
withEmailCodes_persist_client,
94+
withEmailCodes_destroy_client,
9595
withEmailLinks,
9696
withCustomRoles,
9797
withEmailCodesQuickstart,

integration/presets/longRunningApps.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ export const createLongRunningApps = () => {
1919
const configs = [
2020
{ id: 'express.vite.withEmailCodes', config: express.vite, env: envs.withEmailCodes },
2121
{ id: 'react.vite.withEmailCodes', config: react.vite, env: envs.withEmailCodes },
22-
{ id: 'react.vite.withEmailCodes_persist_client', config: react.vite, env: envs.withEmailCodes_persist_client },
22+
{ id: 'react.vite.withEmailCodes_persist_client', config: react.vite, env: envs.withEmailCodes_destroy_client },
2323
{ id: 'react.vite.withEmailLinks', config: react.vite, env: envs.withEmailLinks },
2424
{ id: 'remix.node.withEmailCodes', config: remix.remixNode, env: envs.withEmailCodes },
2525
{ id: 'next.appRouter.withEmailCodes', config: next.appRouter, env: envs.withEmailCodes },
2626
{
2727
id: 'next.appRouter.withEmailCodes_persist_client',
2828
config: next.appRouter,
29-
env: envs.withEmailCodes_persist_client,
29+
env: envs.withEmailCodes_destroy_client,
3030
},
3131
{ id: 'next.appRouter.withCustomRoles', config: next.appRouter, env: envs.withCustomRoles },
3232
{ id: 'quickstart.next.appRouter', config: next.appRouterQuickstart, env: envs.withEmailCodesQuickstart },

integration/templates/next-app-router/src/app/layout.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
1313
return (
1414
<ClerkProvider
1515
experimental={{
16-
persistClient: process.env.NEXT_PUBLIC_EXPERIMENTAL_PERSIST_CLIENT === 'true',
16+
persistClient: process.env.NEXT_PUBLIC_EXPERIMENTAL_PERSIST_CLIENT
17+
? process.env.NEXT_PUBLIC_EXPERIMENTAL_PERSIST_CLIENT === 'true'
18+
: undefined,
1719
}}
1820
>
1921
<html lang='en'>

integration/templates/react-vite/src/client-id.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useClerk, useSession } from '@clerk/clerk-react';
2-
import React from 'react';
32

43
export function ClientId() {
54
const clerk = useClerk();

integration/templates/react-vite/src/main.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ const Root = () => {
2121
routerPush={(to: string) => navigate(to)}
2222
routerReplace={(to: string) => navigate(to, { replace: true })}
2323
experimental={{
24-
persistClient: import.meta.env.VITE_EXPERIMENTAL_PERSIST_CLIENT === 'true',
24+
persistClient: import.meta.env.VITE_EXPERIMENTAL_PERSIST_CLIENT
25+
? import.meta.env.VITE_EXPERIMENTAL_PERSIST_CLIENT === 'true'
26+
: undefined,
2527
}}
2628
>
2729
<Outlet />

integration/tests/sign-out-smoke.test.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign out
2020
await app.teardown();
2121
});
2222

23-
test('sign out throught all open tabs at once', async ({ page, context }) => {
23+
test('sign out through all open tabs at once', async ({ page, context }) => {
2424
const mainTab = createTestUtils({ app, page, context });
2525
await mainTab.po.signIn.goTo();
2626
await mainTab.po.signIn.setIdentifier(fakeUser.email);
@@ -46,7 +46,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign out
4646
await mainTab.po.expect.toBeSignedOut();
4747
});
4848

49-
test('sign out destroying client', async ({ page, context }) => {
49+
test('sign out persisting client', async ({ page, context }) => {
5050
const u = createTestUtils({ app, page, context });
5151
await u.po.signIn.goTo();
5252
await u.po.signIn.setIdentifier(fakeUser.email);
@@ -55,21 +55,23 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign out
5555
await u.po.signIn.continue();
5656
await u.po.expect.toBeSignedIn();
5757
await u.page.goToAppHome();
58-
59-
await u.page.waitForSelector('p[data-clerk-id]', { state: 'attached' });
58+
const client_id_element = await u.page.waitForSelector('p[data-clerk-id]', { state: 'attached' });
59+
const client_id = await client_id_element.innerHTML();
6060

6161
await u.page.evaluate(async () => {
6262
await window.Clerk.signOut();
6363
});
6464

6565
await u.po.expect.toBeSignedOut();
66-
await u.page.waitForSelector('p[data-clerk-id]', { state: 'detached' });
6766
await u.page.waitForSelector('p[data-clerk-session]', { state: 'detached' });
67+
68+
const client_id_after_sign_out = await u.page.locator('p[data-clerk-id]').innerHTML();
69+
expect(client_id).toEqual(client_id_after_sign_out);
6870
});
6971
});
7072

71-
testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes_persist_client] })(
72-
'sign out with persistClient smoke test @generic',
73+
testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes_destroy_client] })(
74+
'sign out with destroy client smoke test @generic',
7375
({ app }) => {
7476
test.describe.configure({ mode: 'serial' });
7577

@@ -86,7 +88,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes_persist_client
8688
await app.teardown();
8789
});
8890

89-
test('sign out persisting client', async ({ page, context }) => {
91+
test('sign out destroying client', async ({ page, context }) => {
9092
const u = createTestUtils({ app, page, context });
9193
await u.po.signIn.goTo();
9294
await u.po.signIn.setIdentifier(fakeUser.email);
@@ -95,18 +97,16 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes_persist_client
9597
await u.po.signIn.continue();
9698
await u.po.expect.toBeSignedIn();
9799
await u.page.goToAppHome();
98-
const client_id_element = await u.page.waitForSelector('p[data-clerk-id]', { state: 'attached' });
99-
const client_id = await client_id_element.innerHTML();
100+
101+
await u.page.waitForSelector('p[data-clerk-id]', { state: 'attached' });
100102

101103
await u.page.evaluate(async () => {
102104
await window.Clerk.signOut();
103105
});
104106

105107
await u.po.expect.toBeSignedOut();
108+
await u.page.waitForSelector('p[data-clerk-id]', { state: 'detached' });
106109
await u.page.waitForSelector('p[data-clerk-session]', { state: 'detached' });
107-
108-
const client_id_after_sign_out = await u.page.locator('p[data-clerk-id]').innerHTML();
109-
expect(client_id).toEqual(client_id_after_sign_out);
110110
});
111111
},
112112
);

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -448,11 +448,13 @@ describe('Clerk singleton', () => {
448448

449449
describe('.signOut()', () => {
450450
const mockClientDestroy = jest.fn();
451+
const mockClientRemoveSessions = jest.fn();
451452
const mockSession1 = { id: '1', remove: jest.fn(), status: 'active', user: {}, getToken: jest.fn() };
452453
const mockSession2 = { id: '2', remove: jest.fn(), status: 'active', user: {}, getToken: jest.fn() };
453454

454455
beforeEach(() => {
455456
mockClientDestroy.mockReset();
457+
mockClientRemoveSessions.mockReset();
456458
mockSession1.remove.mockReset();
457459
mockSession2.remove.mockReset();
458460
});
@@ -480,6 +482,7 @@ describe('Clerk singleton', () => {
480482
activeSessions: [mockSession1, mockSession2],
481483
sessions: [mockSession1, mockSession2],
482484
destroy: mockClientDestroy,
485+
removeSessions: mockClientRemoveSessions,
483486
}),
484487
);
485488

@@ -488,7 +491,8 @@ describe('Clerk singleton', () => {
488491
await sut.load();
489492
await sut.signOut();
490493
await waitFor(() => {
491-
expect(mockClientDestroy).toHaveBeenCalled();
494+
expect(mockClientDestroy).not.toHaveBeenCalled();
495+
expect(mockClientRemoveSessions).toHaveBeenCalled();
492496
expect(sut.setActive).toHaveBeenCalledWith({
493497
session: null,
494498
beforeEmit: expect.any(Function),
@@ -502,6 +506,7 @@ describe('Clerk singleton', () => {
502506
activeSessions: [mockSession1],
503507
sessions: [mockSession1],
504508
destroy: mockClientDestroy,
509+
removeSessions: mockClientRemoveSessions,
505510
}),
506511
);
507512

@@ -510,7 +515,8 @@ describe('Clerk singleton', () => {
510515
await sut.load();
511516
await sut.signOut();
512517
await waitFor(() => {
513-
expect(mockClientDestroy).toHaveBeenCalled();
518+
expect(mockClientDestroy).not.toHaveBeenCalled();
519+
expect(mockClientRemoveSessions).toHaveBeenCalled();
514520
expect(mockSession1.remove).not.toHaveBeenCalled();
515521
expect(sut.setActive).toHaveBeenCalledWith({ session: null, beforeEmit: expect.any(Function) });
516522
});

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ export class Clerk implements ClerkInterface {
331331
const cb = typeof callbackOrOptions === 'function' ? callbackOrOptions : defaultCb;
332332

333333
if (!opts.sessionId || this.client.activeSessions.length === 1) {
334-
if (this.#options.experimental?.persistClient) {
334+
if (this.#options.experimental?.persistClient ?? true) {
335335
await this.client.removeSessions();
336336
} else {
337337
await this.client.destroy();

0 commit comments

Comments
 (0)