Skip to content

Commit 694ff23

Browse files
authored
fix(clerk-js): Check active session ID before setting cookie (#5989)
1 parent 71e6a1f commit 694ff23

File tree

2 files changed

+34
-20
lines changed

2 files changed

+34
-20
lines changed

.changeset/cool-comics-crash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Fixes a bug where the session cookie was getting incorrectly set from tabs that do not reflect the active session.

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

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class AuthCookieService {
4040
private poller: SessionCookiePoller | null = null;
4141
private clientUat: ClientUatCookieHandler;
4242
private sessionCookie: SessionCookieHandler;
43-
private activeOrgCookie: ReturnType<typeof createCookieHandler>;
43+
private activeCookie: ReturnType<typeof createCookieHandler>;
4444
private devBrowser: DevBrowser;
4545

4646
public static async create(
@@ -73,7 +73,7 @@ export class AuthCookieService {
7373

7474
this.clientUat = createClientUatCookie(cookieSuffix);
7575
this.sessionCookie = createSessionCookie(cookieSuffix);
76-
this.activeOrgCookie = createCookieHandler('clerk_active_org');
76+
this.activeCookie = createCookieHandler('clerk_active_context');
7777
this.devBrowser = createDevBrowser({
7878
frontendApi: clerk.frontendApi,
7979
fapiClient,
@@ -82,6 +82,10 @@ export class AuthCookieService {
8282
}
8383

8484
public async setup() {
85+
// Cleanup old cookie
86+
// TODO: This should be removed after 2025-08-01
87+
createCookieHandler('clerk_active_org').remove();
88+
8589
if (this.instanceType === 'production') {
8690
return this.setupProduction();
8791
} else {
@@ -166,13 +170,12 @@ export class AuthCookieService {
166170
}
167171

168172
private updateSessionCookie(token: string | null) {
169-
// only update session cookie from the active tab,
170-
// or if the tab's selected organization matches the session's active organization
171-
if (!document.hasFocus() && !this.isCurrentOrganizationActive()) {
173+
// Only allow background tabs to update if both session and organization match
174+
if (!document.hasFocus() && !this.isCurrentContextActive()) {
172175
return;
173176
}
174177

175-
this.setActiveOrganizationInStorage();
178+
this.setActiveContextInStorage();
176179

177180
return token ? this.sessionCookie.set(token) : this.sessionCookie.remove();
178181
}
@@ -210,29 +213,35 @@ export class AuthCookieService {
210213
}
211214

212215
/**
213-
* The below methods are used to determine whether or not an unfocused tab can be responsible
214-
* for setting the session cookie. A session cookie should only be set by a tab who's selected
215-
* organization matches the session's active organization. By storing the active organization
216-
* ID in a cookie, we can check the value across tabs. If a tab's organization ID does not
217-
* match the value in the active org cookie, it is not responsible for updating the session cookie.
216+
* The below methods handle active context tracking (session and organization) to ensure
217+
* only tabs with matching context can update the session cookie.
218+
* The format of the cookie value is "<session id>:<org id>" where either part can be empty.
218219
*/
219220

220-
public setActiveOrganizationInStorage() {
221-
if (this.clerk.organization?.id) {
222-
this.activeOrgCookie.set(this.clerk.organization.id);
221+
public setActiveContextInStorage() {
222+
const sessionId = this.clerk.session?.id || '';
223+
const orgId = this.clerk.organization?.id || '';
224+
const contextValue = `${sessionId}:${orgId}`;
225+
226+
if (contextValue !== ':') {
227+
this.activeCookie.set(contextValue);
223228
} else {
224-
this.activeOrgCookie.remove();
229+
this.activeCookie.remove();
225230
}
226231
}
227232

228-
private isCurrentOrganizationActive() {
229-
const activeOrganizationId = this.activeOrgCookie.get();
230-
231-
if (!activeOrganizationId && !this.clerk.organization?.id) {
233+
private isCurrentContextActive() {
234+
const activeContext = this.activeCookie.get();
235+
if (!activeContext) {
236+
// we should always have an active context, so return true if there isn't one and treat the current context as active
232237
return true;
233238
}
234239

235-
return this.clerk.organization?.id === activeOrganizationId;
240+
const [activeSessionId, activeOrgId] = activeContext.split(':');
241+
const currentSessionId = this.clerk.session?.id || '';
242+
const currentOrgId = this.clerk.organization?.id || '';
243+
244+
return activeSessionId === currentSessionId && activeOrgId === currentOrgId;
236245
}
237246

238247
public getSessionCookie() {

0 commit comments

Comments
 (0)