Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bright-falcons-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/clerk-js": patch
---

Fix bug where session.getToken() was reading a stale organization ID.
6 changes: 3 additions & 3 deletions packages/clerk-js/src/core/resources/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export class Session extends BaseResource implements SessionResource {
// and retrieve it using the session id concatenated with the jwt template name.
// e.g. session id is 'sess_abc12345' and jwt template name is 'haris'
// The session token ID will be 'sess_abc12345' and the jwt template token ID will be 'sess_abc12345-haris'
#getCacheId(template?: string, organizationId?: string) {
#getCacheId(template?: string, organizationId?: string | null) {
const resolvedOrganizationId =
typeof organizationId === 'undefined' ? this.lastActiveOrganizationId : organizationId;
return [this.id, template, resolvedOrganizationId, this.updatedAt.getTime()].filter(Boolean).join('-');
Expand Down Expand Up @@ -263,7 +263,7 @@ export class Session extends BaseResource implements SessionResource {
// If no organization ID is provided, default to the selected organization in memory
// Note: this explicitly allows passing `null` or `""`, which should select the personal workspace.
const organizationId =
typeof options?.organizationId === 'undefined' ? Session.clerk.organization?.id : options?.organizationId;
typeof options?.organizationId === 'undefined' ? this.lastActiveOrganizationId : options?.organizationId;

if (!template && Number(leewayInSeconds) >= 60) {
throw new Error('Leeway can not exceed the token lifespan (60 seconds)');
Expand All @@ -273,7 +273,7 @@ export class Session extends BaseResource implements SessionResource {
const cachedEntry = skipCache ? undefined : SessionTokenCache.get({ tokenId }, leewayInSeconds);

// Dispatch tokenUpdate only for __session tokens with the session's active organization ID, and not JWT templates
const shouldDispatchTokenUpdate = !template && organizationId === Session.clerk.organization?.id;
const shouldDispatchTokenUpdate = !template && organizationId === this.lastActiveOrganizationId;

if (cachedEntry) {
const cachedToken = await cachedEntry.tokenResolver;
Expand Down
29 changes: 25 additions & 4 deletions packages/clerk-js/src/core/resources/__tests__/Session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,7 @@ describe('Session', () => {
});

it('does not dispatch token:update when provided organization ID does not match current active organization', async () => {
BaseResource.clerk = clerkMock({
organization: new Organization({ id: 'anotherOrganization' } as OrganizationJSON),
}) as any;
BaseResource.clerk = clerkMock() as any;

const session = new Session({
status: 'active',
Expand All @@ -184,7 +182,7 @@ describe('Session', () => {
updated_at: new Date().getTime(),
} as SessionJSON);

await session.getToken({ organizationId: 'activeOrganization' });
await session.getToken({ organizationId: 'anotherOrganization' });

expect(dispatchSpy).toHaveBeenCalledTimes(0);
});
Expand Down Expand Up @@ -229,6 +227,29 @@ describe('Session', () => {
expect(token).toEqual(null);
});
});

it(`uses the current session's lastActiveOrganizationId by default, not clerk.organization.id`, async () => {
BaseResource.clerk = clerkMock({
organization: new Organization({ id: 'oldActiveOrganization' } as OrganizationJSON),
}) as any;

const session = new Session({
status: 'active',
id: 'session_1',
object: 'session',
user: createUser({}),
last_active_organization_id: 'newActiveOrganization',
actor: null,
created_at: new Date().getTime(),
updated_at: new Date().getTime(),
} as SessionJSON);

await session.getToken();

expect((BaseResource.fapiClient.request as jest.Mock<any>).mock.calls[0][0]).toMatchObject({
body: { organizationId: 'newActiveOrganization' },
});
});
});

describe('isAuthorized()', () => {
Expand Down
Loading