Skip to content

Commit b9cb6e5

Browse files
brkalowjacekradko
andauthored
fix(clerk-js): Respect proxyUrl and domain in non-browser environments (#8095)
Co-authored-by: Jacek Radko <jacek@clerk.dev>
1 parent 94e9165 commit b9cb6e5

File tree

7 files changed

+102
-3
lines changed

7 files changed

+102
-3
lines changed

.changeset/proud-animals-happen.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
'@clerk/shared': patch
4+
'@clerk/expo': patch
5+
---
6+
7+
Ensure clerk-js accepts `proxyUrl` and `domain` in non-browser environments.

packages/clerk-js/src/core/__tests__/clerk.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2477,6 +2477,19 @@ describe('Clerk singleton', () => {
24772477

24782478
expect(sut.getFapiClient().buildUrl({ path: '/me' }).href).toContain('https://clerk.satellite.com/v1/me');
24792479
});
2480+
2481+
mockNativeRuntime(() => {
2482+
test('fapiClient should use Clerk.domain as its baseUrl in non-browser runtimes', async () => {
2483+
const sut = new Clerk(productionPublishableKey, {
2484+
domain: 'satellite.com',
2485+
});
2486+
await sut.load({
2487+
isSatellite: true,
2488+
});
2489+
2490+
expect(sut.getFapiClient().buildUrl({ path: '/me' }).href).toContain('https://satellite.com/v1/me');
2491+
});
2492+
});
24802493
});
24812494
});
24822495

@@ -2490,6 +2503,17 @@ describe('Clerk singleton', () => {
24902503

24912504
expect(sut.getFapiClient().buildUrl({ path: '/me' }).href).toContain('https://proxy.com/api/__clerk/v1/me');
24922505
});
2506+
2507+
mockNativeRuntime(() => {
2508+
test('fapiClient should use Clerk.proxyUrl as its baseUrl in non-browser runtimes', async () => {
2509+
const sut = new Clerk(productionPublishableKey, {
2510+
proxyUrl: 'https://proxy.com/api/__clerk',
2511+
});
2512+
await sut.load({});
2513+
2514+
expect(sut.getFapiClient().buildUrl({ path: '/me' }).href).toContain('https://proxy.com/api/__clerk/v1/me');
2515+
});
2516+
});
24932517
});
24942518
});
24952519

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,13 @@ export class Clerk implements ClerkInterface {
342342
}
343343
return strippedDomainString;
344344
}
345-
return '';
345+
346+
if (typeof this.#domain === 'function') {
347+
logger.warnOnce(warnings.domainAsFunctionNotSupported);
348+
return '';
349+
}
350+
351+
return stripScheme(this.#domain || '');
346352
}
347353

348354
get proxyUrl(): string {
@@ -353,7 +359,13 @@ export class Clerk implements ClerkInterface {
353359
}
354360
return proxyUrlToAbsoluteURL(_unfilteredProxy);
355361
}
356-
return '';
362+
363+
if (typeof this.#proxyUrl === 'function') {
364+
logger.warnOnce(warnings.proxyUrlAsFunctionNotSupported);
365+
return '';
366+
}
367+
368+
return this.#proxyUrl || '';
357369
}
358370

359371
get frontendApi(): string {

packages/expo/src/provider/singleton/__tests__/createClerkInstance.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,23 @@ describe('createClerkInstance', () => {
220220
domain: undefined,
221221
});
222222
});
223+
224+
test('throws when proxyUrl is not absolute', async () => {
225+
const createClerkInstance = await loadCreateClerkInstance();
226+
const getClerkInstance = createClerkInstance(MockClerk as unknown as typeof Clerk);
227+
228+
expect(() =>
229+
getClerkInstance({
230+
publishableKey: 'pk_test_123',
231+
proxyUrl: '/api/__clerk',
232+
}),
233+
).toThrow(/`proxyUrl` must be an absolute URL/);
234+
235+
expect(() =>
236+
getClerkInstance({
237+
publishableKey: 'pk_test_123',
238+
proxyUrl: () => '/api/__clerk',
239+
}),
240+
).toThrow(/`proxyUrl` must be a string/);
241+
});
223242
});

packages/expo/src/provider/singleton/createClerkInstance.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import { MemoryTokenCache } from '../../cache/MemoryTokenCache';
2121
import { CLERK_CLIENT_JWT_KEY } from '../../constants';
2222
import { errorThrower } from '../../errorThrower';
23-
import { isNative } from '../../utils';
23+
import { assertValidProxyUrl, isNative } from '../../utils';
2424
import type { BuildClerkOptions } from './types';
2525

2626
/**
@@ -100,12 +100,15 @@ export function createClerkInstance(ClerkClass: typeof Clerk) {
100100
}
101101

102102
if (!__internal_clerk || hasConfigChanged) {
103+
assertValidProxyUrl(proxyUrl);
104+
103105
if (hasConfigChanged) {
104106
tokenCache.clearToken?.(CLERK_CLIENT_JWT_KEY);
105107
}
106108

107109
const getToken = tokenCache.getToken;
108110
const saveToken = tokenCache.saveToken;
111+
109112
__internal_clerkOptions = { publishableKey, proxyUrl, domain };
110113
__internal_clerk = new ClerkClass(publishableKey, { proxyUrl, domain }) as unknown as BrowserClerk;
111114

packages/expo/src/utils/errors.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
11
import { buildErrorThrower } from '@clerk/shared/error';
2+
import { isHttpOrHttps } from '@clerk/shared/proxy';
3+
import type { DomainOrProxyUrl } from '@clerk/shared/types';
4+
5+
import { isNative } from './runtime';
26

37
export const errorThrower = buildErrorThrower({ packageName: PACKAGE_NAME });
8+
9+
function assert(condition: boolean, thrower: () => never): asserts condition {
10+
if (!condition) {
11+
thrower();
12+
}
13+
}
14+
15+
export const assertValidProxyUrl = (proxyUrl: DomainOrProxyUrl['proxyUrl']) => {
16+
if (!proxyUrl) {
17+
return;
18+
}
19+
20+
if (isNative()) {
21+
assert(typeof proxyUrl === 'string', () =>
22+
errorThrower.throw('`proxyUrl` must be a string in non-browser environments.'),
23+
);
24+
25+
if (!isHttpOrHttps(proxyUrl)) {
26+
errorThrower.throw('`proxyUrl` must be an absolute URL in non-browser environments.');
27+
}
28+
}
29+
};

packages/shared/src/internal/clerk-js/warnings.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ const createMessageForDisabledBilling = (componentName: 'PricingTable' | 'Checko
2222
);
2323
};
2424

25+
const propertyAsFunctionNotSupported = (property: 'proxyUrl' | 'domain') => {
26+
return formatWarning(
27+
`${property} as a function is not supported in this environment. The value will be ignored. Provide an absolute URL instead.`,
28+
);
29+
};
30+
2531
const warnings = {
32+
proxyUrlAsFunctionNotSupported: propertyAsFunctionNotSupported('proxyUrl'),
33+
domainAsFunctionNotSupported: propertyAsFunctionNotSupported('domain'),
2634
cannotRenderComponentWhenSessionExists:
2735
'The <SignUp/> and <SignIn/> components cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the Home URL instead.',
2836
cannotRenderSignUpComponentWhenSessionExists:

0 commit comments

Comments
 (0)