Skip to content

Commit

Permalink
fix(clerk-js): Pass dev_browser to AP via query param, fix AP origin …
Browse files Browse the repository at this point in the history
…detection util
  • Loading branch information
Mark Pitsilos committed Aug 9, 2023
1 parent 8538cd0 commit e313934
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-countries-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Pass dev_browser to AP via query param, fix AP origin detection util
36 changes: 36 additions & 0 deletions packages/clerk-js/src/core/clerk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1494,5 +1494,41 @@ describe('Clerk singleton', () => {
const url = sut.buildUrlWithAuth('foo');
expect(url).toBe('foo');
});

it('uses the hash to propagate the dev_browser JWT by default on dev', async () => {
mockUsesUrlBasedSessionSync.mockReturnValue(true);
const sut = new Clerk(devFrontendApi);
await sut.load();

const url = sut.buildUrlWithAuth('https://example.com/some-path');
expect(url).toBe('https://example.com/some-path#__clerk_db_jwt[deadbeef]');
});

it('uses the query param to propagate the dev_browser JWT if specified by option on dev', async () => {
mockUsesUrlBasedSessionSync.mockReturnValue(true);
const sut = new Clerk(devFrontendApi);
await sut.load();

const url = sut.buildUrlWithAuth('https://example.com/some-path', { useQueryParam: true });
expect(url).toBe('https://example.com/some-path?__dev_session=deadbeef');
});

it('uses the query param to propagate the dev_browser JWT to Account Portal pages on dev - non-kima', async () => {
mockUsesUrlBasedSessionSync.mockReturnValue(true);
const sut = new Clerk(devFrontendApi);
await sut.load();

const url = sut.buildUrlWithAuth('https://accounts.abcef.12345.dev.lclclerk.com');
expect(url).toBe('https://accounts.abcef.12345.dev.lclclerk.com/?__dev_session=deadbeef');
});

it('uses the query param to propagate the dev_browser JWT to Account Portal pages on dev - kima', async () => {
mockUsesUrlBasedSessionSync.mockReturnValue(true);
const sut = new Clerk(devFrontendApi);
await sut.load();

const url = sut.buildUrlWithAuth('https://rested-anemone-14.accounts.dev');
expect(url).toBe('https://rested-anemone-14.accounts.dev/?__dev_session=deadbeef');
});
});
});
7 changes: 4 additions & 3 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import {
ignoreEventValue,
inActiveBrowserTab,
inBrowser,
isAccountsHostedPages,
isDevAccountPortalOrigin,
isDevOrStagingUrl,
isError,
isRedirectForFAPIInitiatedFlow,
Expand Down Expand Up @@ -668,7 +668,8 @@ export default class Clerk implements ClerkInterface {
return clerkMissingDevBrowserJwt();
}

const asQueryParam = !!options?.useQueryParam;
// Use query param for Account Portal pages so that SSR can access the dev_browser JWT
const asQueryParam = !!options?.useQueryParam || isDevAccountPortalOrigin(toURL.hostname);

return setDevBrowserJWTInURL(toURL.href, devBrowserJwt, asQueryParam);
}
Expand Down Expand Up @@ -1231,7 +1232,7 @@ export default class Clerk implements ClerkInterface {
this.#authService = new SessionCookieService(this);
this.#pageLifecycle = createPageLifecycle();

const isInAccountsHostedPages = isAccountsHostedPages(window?.location.hostname);
const isInAccountsHostedPages = isDevAccountPortalOrigin(window?.location.hostname);

this.#setupListeners();

Expand Down
16 changes: 8 additions & 8 deletions packages/clerk-js/src/utils/__tests__/url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,32 @@ import {
getSearchParameterFromHash,
hasBannedProtocol,
hasExternalAccountSignUpError,
isAccountsHostedPages,
isAllowedRedirectOrigin,
isDataUri,
isDevAccountPortalOrigin,
isRedirectForFAPIInitiatedFlow,
isValidUrl,
mergeFragmentIntoUrl,
requiresUserInput,
trimTrailingSlash,
} from '../url';

describe('isAccountsHostedPages(url)', () => {
describe('isDevAccountPortalOrigin(url)', () => {
const goodUrls: Array<[string | URL, boolean]> = [
['clerk.dev.lclclerk.com', false],
['clerk.prod.lclclerk.com', false],
['clerk.abc.efg.lclstage.dev', false],
['clerk.abc.efg.stgstage.dev', false],
['accounts.abc.efg.dev.lclclerk.com', true],
['https://accounts.abc.efg.stg.lclclerk.com', true],
[new URL('https://clerk.abc.efg.lcl.dev'), false],
[new URL('https://accounts.abc.efg.lcl.dev'), true],
[new URL('https://accounts.abc.efg.stg.dev'), true],
['rested-anemone-14.accounts.dev', true],
['rested-anemone-14.accounts.dev.accountstage.dev', true],
['rested-anemone-14.accounts.dev.accounts.lclclerk.com', true],
['rested-anemone-14.clerk.accounts.dev', false],
];

test.each(goodUrls)('.isAccountsHostedPages(%s)', (a, expected) => {
test.each(goodUrls)('.isDevAccountPortalOrigin(%s)', (a, expected) => {
// @ts-ignore
expect(isAccountsHostedPages(a)).toBe(expected);
expect(isDevAccountPortalOrigin(a)).toBe(expected);
});
});

Expand Down
41 changes: 34 additions & 7 deletions packages/clerk-js/src/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,53 @@ export const DEV_OR_STAGING_SUFFIXES = [
'accounts.dev',
];

export const LEGACY_DEV_SUFFIXES = ['.lcl.dev', '.lclstage.dev', '.lclclerk.com'];

export const KIMA_DEV_SUFFIXES = ['.accounts.dev', '.accountstage.dev', '.accounts.lclclerk.com'];

const BANNED_URI_PROTOCOLS = ['javascript:'] as const;

const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
export { isDevOrStagingUrl };
const accountsCache = new Map<string, boolean>();
const accountPortalCache = new Map<string, boolean>();

export function isAccountsHostedPages(url: string | URL = window.location.hostname): boolean {
if (!url) {
export function isDevAccountPortalOrigin(hostname: string = window.location.hostname): boolean {
if (!hostname) {
return false;
}

const hostname = typeof url === 'string' ? url : url.hostname;
let res = accountsCache.get(hostname);
let res = accountPortalCache.get(hostname);

if (res === undefined) {
res = DEV_OR_STAGING_SUFFIXES.some(s => /^(https?:\/\/)?accounts\./.test(hostname) && hostname.endsWith(s));
accountsCache.set(hostname, res);
res = isLegacyDevAccountPortalOrigin(hostname) || isKimaDevAccountPortalOrigin(hostname);
accountPortalCache.set(hostname, res);
}

return res;
}

// Returns true for hosts such as:
// * accounts.foo.bar-13.lcl.dev
// * accounts.foo.bar-13.lclstage.dev
// * accounts.foo.bar-13.dev.lclclerk.com
function isLegacyDevAccountPortalOrigin(host: string): boolean {
return LEGACY_DEV_SUFFIXES.some(legacyDevSuffix => {
return host.startsWith('accounts.') && host.endsWith(legacyDevSuffix);
});
}

// Returns true for hosts such as:
// * foo-bar-13.accounts.dev
// * foo-bar-13.accountstage.dev
// * foo-bar-13.accounts.lclclerk.com
// But false for:
// * foo-bar-13.clerk.accounts.lclclerk.com
function isKimaDevAccountPortalOrigin(host: string): boolean {
return KIMA_DEV_SUFFIXES.some(kimaDevSuffix => {
return host.endsWith(kimaDevSuffix) && !host.endsWith('.clerk' + kimaDevSuffix);
});
}

export function getETLDPlusOneFromFrontendApi(frontendApi: string): string {
return frontendApi.replace('clerk.', '');
}
Expand Down

0 comments on commit e313934

Please sign in to comment.