Skip to content
Draft
5 changes: 5 additions & 0 deletions .changeset/upset-words-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Fix session setting immediately after sign in
14 changes: 12 additions & 2 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1419,7 +1419,7 @@ export class Clerk implements ClerkInterface {
return;
}

if (newSession?.status !== 'pending') {
if (newSession?.status !== 'pending' && (this.session?.id !== newSession?.id || shouldSwitchOrganization)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what problem is this addressing? The transitive state sets undefined, not null. If we're not entirely sure, I would recommend that we remove this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unclear to me as well. Could this cause a chain of events that result to null eventually ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check prevents calling #setTransitiveState() unnecessarily. It ensures we only clear the transitive state (which sets the session to undefined) when the session is changing or the org is changing.

Without it you get unnecessary session → undefined → session when nothing changed

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what triggered during the sign-in, causing session to be undefined when the updateClient call ran, causing it to not update the session?

So this PR kind of fixes the sign-in bug from two separate angles?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ephem Exactly! Without this, every navigation with setActive() would clear the session unnecessarily, opening the window for a race condition where updateClient() would run before setActive() can set and emit the session

this.#setTransitiveState();
}

Expand Down Expand Up @@ -2358,7 +2358,9 @@ export class Clerk implements ClerkInterface {
}

updateClient = (newClient: ClientResource): void => {
if (!this.client) {
const isFirstClientSet = !this.client;

if (isFirstClientSet) {
// This is the first time client is being
// set, so we also need to set session
const session = this.#options.selectInitialSession
Expand Down Expand Up @@ -2395,6 +2397,14 @@ export class Clerk implements ClerkInterface {
);
}
eventBus.emit(events.TokenUpdate, { token: this.session?.lastActiveToken });
} else if (!isFirstClientSet && newClient.sessions?.length > 0) {
// Handles the case where updateClient() is called (e.g., from a touch() response) with session data,
// but this.session is falsy. This commonly occurs after sign-in when the touch() response
// includes fresh client data before setActive() completes, preventing a session null flash.
const session = this.#options.selectInitialSession
? this.#options.selectInitialSession(newClient)
: this.#defaultSession(newClient);
this.#setAccessors(session);
}

this.#emit();
Expand Down
Loading