Skip to content
Open
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
7 changes: 7 additions & 0 deletions .changeset/proud-animals-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/clerk-js': patch
'@clerk/shared': patch
'@clerk/expo': patch
---

Ensure clerk-js accepts `proxyUrl` and `domain` in non-browser environments.
24 changes: 24 additions & 0 deletions packages/clerk-js/src/core/__tests__/clerk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2477,6 +2477,19 @@ describe('Clerk singleton', () => {

expect(sut.getFapiClient().buildUrl({ path: '/me' }).href).toContain('https://clerk.satellite.com/v1/me');
});

mockNativeRuntime(() => {
test('fapiClient should use Clerk.domain as its baseUrl in non-browser runtimes', async () => {
const sut = new Clerk(productionPublishableKey, {
domain: 'satellite.com',
});
await sut.load({
isSatellite: true,
});

expect(sut.getFapiClient().buildUrl({ path: '/me' }).href).toContain('https://satellite.com/v1/me');
});
});
});
});

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

expect(sut.getFapiClient().buildUrl({ path: '/me' }).href).toContain('https://proxy.com/api/__clerk/v1/me');
});

mockNativeRuntime(() => {
test('fapiClient should use Clerk.proxyUrl as its baseUrl in non-browser runtimes', async () => {
const sut = new Clerk(productionPublishableKey, {
proxyUrl: 'https://proxy.com/api/__clerk',
});
await sut.load({});

expect(sut.getFapiClient().buildUrl({ path: '/me' }).href).toContain('https://proxy.com/api/__clerk/v1/me');
});
});
});
});

Expand Down
16 changes: 14 additions & 2 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,13 @@ export class Clerk implements ClerkInterface {
}
return strippedDomainString;
}
return '';

if (typeof this.#domain === 'function') {
logger.warnOnce(warnings.domainAsFunctionNotSupported);
return '';
}

return this.#domain || '';
}

get proxyUrl(): string {
Expand All @@ -353,7 +359,13 @@ export class Clerk implements ClerkInterface {
}
return proxyUrlToAbsoluteURL(_unfilteredProxy);
}
return '';

if (typeof this.#proxyUrl === 'function') {
logger.warnOnce(warnings.proxyUrlAsFunctionNotSupported);
return '';
}

return this.#proxyUrl || '';
}

get frontendApi(): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,23 @@ describe('createClerkInstance', () => {
domain: undefined,
});
});

test('throws when proxyUrl is not absolute', async () => {
const createClerkInstance = await loadCreateClerkInstance();
const getClerkInstance = createClerkInstance(MockClerk as unknown as typeof Clerk);

expect(() =>
getClerkInstance({
publishableKey: 'pk_test_123',
proxyUrl: '/api/__clerk',
}),
).toThrow(/`proxyUrl` must be an absolute URL/);

expect(() =>
getClerkInstance({
publishableKey: 'pk_test_123',
proxyUrl: () => '/api/__clerk',
}),
).toThrow(/`proxyUrl` must be a string/);
});
});
5 changes: 4 additions & 1 deletion packages/expo/src/provider/singleton/createClerkInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { MemoryTokenCache } from '../../cache/MemoryTokenCache';
import { CLERK_CLIENT_JWT_KEY } from '../../constants';
import { errorThrower } from '../../errorThrower';
import { isNative } from '../../utils';
import { assertValidProxyUrl, isNative } from '../../utils';
import type { BuildClerkOptions } from './types';

/**
Expand Down Expand Up @@ -100,12 +100,15 @@ export function createClerkInstance(ClerkClass: typeof Clerk) {
}

if (!__internal_clerk || hasConfigChanged) {
assertValidProxyUrl(proxyUrl);

if (hasConfigChanged) {
tokenCache.clearToken?.(CLERK_CLIENT_JWT_KEY);
}

const getToken = tokenCache.getToken;
const saveToken = tokenCache.saveToken;

__internal_clerkOptions = { publishableKey, proxyUrl, domain };
__internal_clerk = new ClerkClass(publishableKey, { proxyUrl, domain }) as unknown as BrowserClerk;

Expand Down
20 changes: 20 additions & 0 deletions packages/expo/src/utils/errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
import { buildErrorThrower } from '@clerk/shared/error';
import { isHttpOrHttps } from '@clerk/shared/proxy';
import type { DomainOrProxyUrl } from '@clerk/shared/types';

import { isNative } from './runtime';

export const errorThrower = buildErrorThrower({ packageName: PACKAGE_NAME });

export const assertValidProxyUrl = (proxyUrl: DomainOrProxyUrl['proxyUrl']) => {
if (!proxyUrl) {
return;
}

if (isNative()) {
if (typeof proxyUrl !== 'string') {
return errorThrower.throw('`proxyUrl` must be a string in non-browser environments.');
}

if (!isHttpOrHttps(proxyUrl)) {
errorThrower.throw('`proxyUrl` must be an absolute URL in non-browser environments.');
}
}
};
8 changes: 8 additions & 0 deletions packages/shared/src/internal/clerk-js/warnings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ const createMessageForDisabledBilling = (componentName: 'PricingTable' | 'Checko
);
};

const propertyAsFunctionNotSupported = (property: 'proxyUrl' | 'domain') => {
return formatWarning(
`${property} as a function is not supported in this environment. The value will be ignored. Provide an absolute URL instead.`,
);
};

const warnings = {
proxyUrlAsFunctionNotSupported: propertyAsFunctionNotSupported('proxyUrl'),
domainAsFunctionNotSupported: propertyAsFunctionNotSupported('domain'),
cannotRenderComponentWhenSessionExists:
'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.',
cannotRenderSignUpComponentWhenSessionExists:
Expand Down
Loading