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;