Skip to content

Commit

Permalink
feat(clerk-js): Enable persisted clients by default (#4250)
Browse files Browse the repository at this point in the history
  • Loading branch information
panteliselef authored Oct 1, 2024
1 parent 8568534 commit ea8cd9b
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 24 deletions.
16 changes: 16 additions & 0 deletions .changeset/shy-sloths-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@clerk/clerk-js": minor
---

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.

If you're encountering issues, please open an issue. You can disable this new behavior like so:

```js
// React
<ClerkProvider experimental={{ persistClient: false }} />

// Vanilla JS
await clerk.load({ experimental: { persistClient: false } })
```

6 changes: 3 additions & 3 deletions integration/presets/envs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ const withEmailCodes = base
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-email-codes').pk)
.setEnvVariable('private', 'CLERK_ENCRYPTION_KEY', constants.E2E_CLERK_ENCRYPTION_KEY || 'a-key');

const withEmailCodes_persist_client = withEmailCodes
const withEmailCodes_destroy_client = withEmailCodes
.clone()
.setEnvVariable('public', 'EXPERIMENTAL_PERSIST_CLIENT', 'true');
.setEnvVariable('public', 'EXPERIMENTAL_PERSIST_CLIENT', 'false');

const withEmailLinks = base
.clone()
Expand Down Expand Up @@ -91,7 +91,7 @@ const withDynamicKeys = withEmailCodes
export const envs = {
base,
withEmailCodes,
withEmailCodes_persist_client,
withEmailCodes_destroy_client,
withEmailLinks,
withCustomRoles,
withEmailCodesQuickstart,
Expand Down
4 changes: 2 additions & 2 deletions integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ export const createLongRunningApps = () => {
const configs = [
{ id: 'express.vite.withEmailCodes', config: express.vite, env: envs.withEmailCodes },
{ id: 'react.vite.withEmailCodes', config: react.vite, env: envs.withEmailCodes },
{ id: 'react.vite.withEmailCodes_persist_client', config: react.vite, env: envs.withEmailCodes_persist_client },
{ id: 'react.vite.withEmailCodes_persist_client', config: react.vite, env: envs.withEmailCodes_destroy_client },
{ id: 'react.vite.withEmailLinks', config: react.vite, env: envs.withEmailLinks },
{ id: 'remix.node.withEmailCodes', config: remix.remixNode, env: envs.withEmailCodes },
{ id: 'next.appRouter.withEmailCodes', config: next.appRouter, env: envs.withEmailCodes },
{
id: 'next.appRouter.withEmailCodes_persist_client',
config: next.appRouter,
env: envs.withEmailCodes_persist_client,
env: envs.withEmailCodes_destroy_client,
},
{ id: 'next.appRouter.withCustomRoles', config: next.appRouter, env: envs.withCustomRoles },
{ id: 'quickstart.next.appRouter', config: next.appRouterQuickstart, env: envs.withEmailCodesQuickstart },
Expand Down
4 changes: 3 additions & 1 deletion integration/templates/next-app-router/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
return (
<ClerkProvider
experimental={{
persistClient: process.env.NEXT_PUBLIC_EXPERIMENTAL_PERSIST_CLIENT === 'true',
persistClient: process.env.NEXT_PUBLIC_EXPERIMENTAL_PERSIST_CLIENT
? process.env.NEXT_PUBLIC_EXPERIMENTAL_PERSIST_CLIENT === 'true'
: undefined,
}}
>
<html lang='en'>
Expand Down
1 change: 0 additions & 1 deletion integration/templates/react-vite/src/client-id.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useClerk, useSession } from '@clerk/clerk-react';
import React from 'react';

export function ClientId() {
const clerk = useClerk();
Expand Down
4 changes: 3 additions & 1 deletion integration/templates/react-vite/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const Root = () => {
routerPush={(to: string) => navigate(to)}
routerReplace={(to: string) => navigate(to, { replace: true })}
experimental={{
persistClient: import.meta.env.VITE_EXPERIMENTAL_PERSIST_CLIENT === 'true',
persistClient: import.meta.env.VITE_EXPERIMENTAL_PERSIST_CLIENT
? import.meta.env.VITE_EXPERIMENTAL_PERSIST_CLIENT === 'true'
: undefined,
}}
>
<Outlet />
Expand Down
26 changes: 13 additions & 13 deletions integration/tests/sign-out-smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign out
await app.teardown();
});

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

test('sign out destroying client', async ({ page, context }) => {
test('sign out persisting client', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.setIdentifier(fakeUser.email);
Expand All @@ -55,21 +55,23 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign out
await u.po.signIn.continue();
await u.po.expect.toBeSignedIn();
await u.page.goToAppHome();

await u.page.waitForSelector('p[data-clerk-id]', { state: 'attached' });
const client_id_element = await u.page.waitForSelector('p[data-clerk-id]', { state: 'attached' });
const client_id = await client_id_element.innerHTML();

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

await u.po.expect.toBeSignedOut();
await u.page.waitForSelector('p[data-clerk-id]', { state: 'detached' });
await u.page.waitForSelector('p[data-clerk-session]', { state: 'detached' });

const client_id_after_sign_out = await u.page.locator('p[data-clerk-id]').innerHTML();
expect(client_id).toEqual(client_id_after_sign_out);
});
});

testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes_persist_client] })(
'sign out with persistClient smoke test @generic',
testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes_destroy_client] })(
'sign out with destroy client smoke test @generic',
({ app }) => {
test.describe.configure({ mode: 'serial' });

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

test('sign out persisting client', async ({ page, context }) => {
test('sign out destroying client', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.setIdentifier(fakeUser.email);
Expand All @@ -95,18 +97,16 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes_persist_client
await u.po.signIn.continue();
await u.po.expect.toBeSignedIn();
await u.page.goToAppHome();
const client_id_element = await u.page.waitForSelector('p[data-clerk-id]', { state: 'attached' });
const client_id = await client_id_element.innerHTML();

await u.page.waitForSelector('p[data-clerk-id]', { state: 'attached' });

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

await u.po.expect.toBeSignedOut();
await u.page.waitForSelector('p[data-clerk-id]', { state: 'detached' });
await u.page.waitForSelector('p[data-clerk-session]', { state: 'detached' });

const client_id_after_sign_out = await u.page.locator('p[data-clerk-id]').innerHTML();
expect(client_id).toEqual(client_id_after_sign_out);
});
},
);
10 changes: 8 additions & 2 deletions packages/clerk-js/src/core/__tests__/clerk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,11 +448,13 @@ describe('Clerk singleton', () => {

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

beforeEach(() => {
mockClientDestroy.mockReset();
mockClientRemoveSessions.mockReset();
mockSession1.remove.mockReset();
mockSession2.remove.mockReset();
});
Expand Down Expand Up @@ -480,6 +482,7 @@ describe('Clerk singleton', () => {
activeSessions: [mockSession1, mockSession2],
sessions: [mockSession1, mockSession2],
destroy: mockClientDestroy,
removeSessions: mockClientRemoveSessions,
}),
);

Expand All @@ -488,7 +491,8 @@ describe('Clerk singleton', () => {
await sut.load();
await sut.signOut();
await waitFor(() => {
expect(mockClientDestroy).toHaveBeenCalled();
expect(mockClientDestroy).not.toHaveBeenCalled();
expect(mockClientRemoveSessions).toHaveBeenCalled();
expect(sut.setActive).toHaveBeenCalledWith({
session: null,
beforeEmit: expect.any(Function),
Expand All @@ -502,6 +506,7 @@ describe('Clerk singleton', () => {
activeSessions: [mockSession1],
sessions: [mockSession1],
destroy: mockClientDestroy,
removeSessions: mockClientRemoveSessions,
}),
);

Expand All @@ -510,7 +515,8 @@ describe('Clerk singleton', () => {
await sut.load();
await sut.signOut();
await waitFor(() => {
expect(mockClientDestroy).toHaveBeenCalled();
expect(mockClientDestroy).not.toHaveBeenCalled();
expect(mockClientRemoveSessions).toHaveBeenCalled();
expect(mockSession1.remove).not.toHaveBeenCalled();
expect(sut.setActive).toHaveBeenCalledWith({ session: null, beforeEmit: expect.any(Function) });
});
Expand Down
2 changes: 1 addition & 1 deletion packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ export class Clerk implements ClerkInterface {
const cb = typeof callbackOrOptions === 'function' ? callbackOrOptions : defaultCb;

if (!opts.sessionId || this.client.activeSessions.length === 1) {
if (this.#options.experimental?.persistClient) {
if (this.#options.experimental?.persistClient ?? true) {
await this.client.removeSessions();
} else {
await this.client.destroy();
Expand Down

0 comments on commit ea8cd9b

Please sign in to comment.