Skip to content

Commit 08b7a02

Browse files
committed
consolidate tokenId creation logic
1 parent 4b4886b commit 08b7a02

File tree

5 files changed

+58
-11
lines changed

5 files changed

+58
-11
lines changed

packages/clerk-js/src/core/resources/Session.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type {
2727

2828
import { unixEpochToDate } from '@/utils/date';
2929
import { debugLogger } from '@/utils/debug';
30+
import { buildTokenId } from '@/utils/tokenId';
3031
import {
3132
convertJSONToPublicKeyRequestOptions,
3233
serializePublicKeyCredentialAssertion,
@@ -144,7 +145,7 @@ export class Session extends BaseResource implements SessionResource {
144145
#getCacheId(template?: string, organizationId?: string | null) {
145146
const resolvedOrganizationId =
146147
typeof organizationId === 'undefined' ? this.lastActiveOrganizationId : organizationId;
147-
return [this.id, template, resolvedOrganizationId].filter(Boolean).join('-');
148+
return buildTokenId(this.id, template, resolvedOrganizationId);
148149
}
149150

150151
startVerification = async ({ level }: SessionVerifyCreateParams): Promise<SessionVerificationResource> => {

packages/clerk-js/src/core/tokenCache.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { TokenResource } from '@clerk/types';
22

33
import { debugLogger } from '@/utils/debug';
4+
import { buildTokenId } from '@/utils/tokenId';
45

56
import { Token } from './resources/internal';
67

@@ -141,14 +142,6 @@ export class TokenCacheKey {
141142
}
142143
}
143144

144-
/**
145-
* Generates a unique token identifier from session context.
146-
* Format: `sessionId-template-organizationId` (omitting falsy values).
147-
*/
148-
const computeTokenId = (sessionId: string, template?: string, organizationId?: string | null): string => {
149-
return [sessionId, template, organizationId].filter(Boolean).join('-');
150-
};
151-
152145
/**
153146
* Message format for BroadcastChannel token synchronization between tabs.
154147
*/
@@ -231,7 +224,7 @@ const MemoryTokenCache = (prefix = KEY_PREFIX): TokenCache => {
231224
* Validates token ID, parses JWT, and updates cache if token is newer than existing entry.
232225
*/
233226
const handleBroadcastMessage = async ({ data }: MessageEvent<SessionTokenEvent>) => {
234-
const expectedTokenId = computeTokenId(data.sessionId, data.template, data.organizationId);
227+
const expectedTokenId = buildTokenId(data.sessionId, data.template, data.organizationId);
235228
if (data.tokenId !== expectedTokenId) {
236229
debugLogger.warn(
237230
'Ignoring token broadcast with mismatched tokenId',
@@ -318,7 +311,7 @@ const MemoryTokenCache = (prefix = KEY_PREFIX): TokenCache => {
318311
setInternal(entry);
319312

320313
if (broadcast && channel) {
321-
const expectedTokenId = computeTokenId(broadcast.sessionId, broadcast.template, broadcast.organizationId);
314+
const expectedTokenId = buildTokenId(broadcast.sessionId, broadcast.template, broadcast.organizationId);
322315
if (entry.tokenId !== expectedTokenId) {
323316
return;
324317
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { buildTokenId } from '../tokenId';
4+
5+
describe('buildTokenId', () => {
6+
it('should build token ID with only sessionId', () => {
7+
expect(buildTokenId('sess_123')).toBe('sess_123');
8+
});
9+
10+
it('should build token ID with sessionId and template', () => {
11+
expect(buildTokenId('sess_123', 'custom')).toBe('sess_123-custom');
12+
});
13+
14+
it('should build token ID with sessionId and organizationId', () => {
15+
expect(buildTokenId('sess_123', undefined, 'org_456')).toBe('sess_123-org_456');
16+
});
17+
18+
it('should build token ID with all parameters', () => {
19+
expect(buildTokenId('sess_123', 'custom', 'org_456')).toBe('sess_123-custom-org_456');
20+
});
21+
22+
it('should omit null organizationId', () => {
23+
expect(buildTokenId('sess_123', 'custom', null)).toBe('sess_123-custom');
24+
});
25+
26+
it('should omit undefined template', () => {
27+
expect(buildTokenId('sess_123', undefined, 'org_456')).toBe('sess_123-org_456');
28+
});
29+
30+
it('should handle empty string values by omitting them', () => {
31+
expect(buildTokenId('sess_123', '', '')).toBe('sess_123');
32+
});
33+
34+
it('should handle all optional parameters being undefined', () => {
35+
expect(buildTokenId('sess_123', undefined, undefined)).toBe('sess_123');
36+
});
37+
});

packages/clerk-js/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export * from './props';
2323
export * from './queryStateParams';
2424
export * from './querystring';
2525
export * from './runtime';
26+
export * from './tokenId';
2627
export * from './url';
2728
export * from './web3';
2829
export * from './windowNavigate';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Generates a unique token identifier from session context by combining sessionId,
3+
* optional template, and optional organizationId with hyphens, omitting falsy values.
4+
*
5+
* @example
6+
* ```typescript
7+
* buildTokenId('sess_123') // 'sess_123'
8+
* buildTokenId('sess_123', 'custom') // 'sess_123-custom'
9+
* buildTokenId('sess_123', 'custom', 'org_456') // 'sess_123-custom-org_456'
10+
* buildTokenId('sess_123', undefined, 'org_456') // 'sess_123-org_456'
11+
* ```
12+
*/
13+
export const buildTokenId = (sessionId: string, template?: string, organizationId?: string | null): string => {
14+
return [sessionId, template, organizationId].filter(Boolean).join('-');
15+
};

0 commit comments

Comments
 (0)