From a102c21d4762895a80a1ad846700763cc801b3f3 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Fri, 1 Sep 2023 08:45:35 -0500 Subject: [PATCH] feat(clerk-expo): Support swapping publishableKey at runtime (#1655) --- .changeset/hot-pans-grow.md | 7 +++++++ packages/clerk-js/src/core/clerk.ts | 1 + packages/expo/src/ClerkProvider.tsx | 2 ++ packages/expo/src/cache.ts | 4 ++++ packages/expo/src/singleton.ts | 9 ++++++++- packages/react/src/isomorphicClerk.ts | 3 ++- 6 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 .changeset/hot-pans-grow.md diff --git a/.changeset/hot-pans-grow.md b/.changeset/hot-pans-grow.md new file mode 100644 index 0000000000..b29be33369 --- /dev/null +++ b/.changeset/hot-pans-grow.md @@ -0,0 +1,7 @@ +--- +'@clerk/clerk-js': patch +'@clerk/clerk-react': patch +'@clerk/clerk-expo': minor +--- + +Support swapping the Clerk publishableKey at runtime to allow users to toggle the instance being used. diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 8c7c00431a..1dc3e89512 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -256,6 +256,7 @@ export default class Clerk implements ClerkInterface { const { frontendApi, instanceType } = publishableKey as PublishableKey; + this.publishableKey = key; this.frontendApi = frontendApi; this.#instanceType = instanceType; } diff --git a/packages/expo/src/ClerkProvider.tsx b/packages/expo/src/ClerkProvider.tsx index 8fe2013dbf..de478b50ce 100644 --- a/packages/expo/src/ClerkProvider.tsx +++ b/packages/expo/src/ClerkProvider.tsx @@ -25,6 +25,8 @@ export function ClerkProvider(props: ClerkProviderProps): JSX.Element { return ( //@ts-expect-error Promise; saveToken: (key: string, token: string) => Promise; + clearToken?: (key: string) => void; } const createMemoryTokenCache = (): TokenCache => { @@ -13,6 +14,9 @@ const createMemoryTokenCache = (): TokenCache => { getToken: key => { return Promise.resolve(cache[key]); }, + clearToken: key => { + delete cache[key]; + }, }; }; diff --git a/packages/expo/src/singleton.ts b/packages/expo/src/singleton.ts index 587e4af565..686f7a435c 100644 --- a/packages/expo/src/singleton.ts +++ b/packages/expo/src/singleton.ts @@ -14,7 +14,14 @@ type BuildClerkOptions = { }; export function buildClerk({ key, tokenCache }: BuildClerkOptions): HeadlessBrowserClerk { - if (!clerk) { + // Support "hot-swapping" the Clerk instance at runtime. See JS-598 for additional details. + const hasKeyChanged = clerk && key !== clerk.publishableKey; + + if (!clerk || hasKeyChanged) { + if (hasKeyChanged) { + tokenCache.clearToken?.(KEY); + } + const getToken = tokenCache.getToken; const saveToken = tokenCache.saveToken; // TODO: DO NOT ACCEPT THIS diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 8bb6c3569b..8927f0d8d6 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -87,8 +87,9 @@ export default class IsomorphicClerk { static getOrCreateInstance(options: IsomorphicClerkOptions) { // During SSR: a new instance should be created for every request // During CSR: use the cached instance for the whole lifetime of the app + // Also will recreate the instance if the provided Clerk instance changes // This method should be idempotent in both scenarios - if (!inClientSide() || !this.#instance) { + if (!inClientSide() || !this.#instance || (options.Clerk && this.#instance.Clerk !== options.Clerk)) { this.#instance = new IsomorphicClerk(options); } return this.#instance;