Skip to content

Commit 51197cb

Browse files
dimkldesiprisg
authored andcommitted
chore(repo): v5 extra cleanups (#2169)
* chore(clerk-expo): Use isomorphicAtob/isomorhpicBtoa to drop base-64 dep * chore(clerk-sdk-node): Add comment and drop todo in interstitial response errors * fix(clerk-react): Rename `HeadlessBrowserClerkConstrutor` / `HeadlessBrowserClerkConstructor` typo * fix(clerk-react): Drop unused import * chore(backend): Refactor merging of props in request/factory * chore(clerk-react,nextjs): Update JSDoc examples with `withServerSideAuth` * chore(shared): Drop `isLegacyFrontendApiKey` * chore(clerk-js): Drop forgotten default exports * chore(clerk-js): Drop node-fetch dependency * chore(backend): Duplicate test * chore(remix): Rename `noSecretKeyOrApiKeyError` to `noSecretKeyError` * chore(clerk-sdk-node): Re-use `isDevelopmentFromApiKey` from `@clerk/shared` * chore(backend): Drop unused `LoadResourcesOptions` export * chore(*): Rename `isDevelopmentFromApiKey` to `isDevelopmentFromSecretKey` * chore(*): Rename `isProductionFromApiKey` to `isProductionFromSecretKey` * chore(repo): Add changeset
1 parent eb96c6f commit 51197cb

File tree

33 files changed

+232
-193
lines changed

33 files changed

+232
-193
lines changed

.changeset/red-worms-fetch.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
'@clerk/clerk-js': major
3+
'@clerk/shared': major
4+
'@clerk/clerk-sdk-node': minor
5+
'@clerk/backend': minor
6+
'@clerk/nextjs': minor
7+
'@clerk/clerk-react': minor
8+
'@clerk/clerk-expo': minor
9+
---
10+
11+
Breaking Changes:
12+
13+
- Drop `isLegacyFrontendApiKey` from `@clerk/shared`
14+
- Drop default exports from `@clerk/clerk-js`
15+
- on headless Clerk type
16+
- on ui and ui.retheme `Portal`
17+
- Use `isProductionFromSecretKey` instead of `isProductionFromApiKey`
18+
- Use `isDevelopmentFromSecretKey` instead of `isDevelopmentFromApiKey`
19+
20+
Changes:
21+
22+
- Rename `HeadlessBrowserClerkConstrutor` / `HeadlessBrowserClerkConstructor` (typo)
23+
- Use `isomorphicAtob` / `isomorhpicBtoa` to replace `base-64` in `@clerk/expo`
24+
- Refactor merging build-time and runtime props in `@clerk/backend` clerk client
25+
- Drop `node-fetch` dependency from `@clerk/backend`
26+
- Drop duplicate test in `@clerk/backend`

package-lock.json

Lines changed: 1 addition & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend/src/tokens/authStatus.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export type AuthReason = AuthErrorReason | TokenVerificationErrorReason;
8181

8282
export type RequestState = SignedInState | SignedOutState | InterstitialState | UnknownState;
8383

84-
export type LoadResourcesOptions = {
84+
type LoadResourcesOptions = {
8585
loadSession?: boolean;
8686
loadUser?: boolean;
8787
loadOrganization?: boolean;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import type QUnit from 'qunit';
2+
3+
import type { ApiClient } from '../api';
4+
import { createAuthenticateRequest } from './factory';
5+
6+
export default (QUnit: QUnit) => {
7+
const { module, test } = QUnit;
8+
9+
module('createAuthenticateRequest({ options, apiClient })', hooks => {
10+
let fakeAuthenticateRequest;
11+
hooks.afterEach(() => {
12+
fakeAuthenticateRequest?.restore();
13+
});
14+
15+
test('fallbacks to build-time options', async assert => {
16+
const buildTimeOptions = {
17+
secretKey: 'sk',
18+
jwtKey: 'jwtKey',
19+
apiUrl: 'apiUrl',
20+
apiVersion: 'apiVersion',
21+
proxyUrl: 'proxyUrl',
22+
publishableKey: 'pk',
23+
isSatellite: false,
24+
domain: 'domain',
25+
audience: 'domain',
26+
};
27+
28+
const { authenticateRequest } = createAuthenticateRequest({
29+
options: buildTimeOptions,
30+
apiClient: {} as ApiClient,
31+
});
32+
33+
const requestState = await authenticateRequest({ request: new Request('http://example.com/') });
34+
assert.propContains(requestState.toAuth()?.debug(), buildTimeOptions);
35+
});
36+
37+
test('overrides build-time options with runtime options', async assert => {
38+
const buildTimeOptions = {
39+
secretKey: 'sk',
40+
jwtKey: 'jwtKey',
41+
apiUrl: 'apiUrl',
42+
apiVersion: 'apiVersion',
43+
proxyUrl: 'proxyUrl',
44+
publishableKey: 'pk',
45+
isSatellite: false,
46+
domain: 'domain',
47+
audience: 'domain',
48+
};
49+
50+
const { authenticateRequest } = createAuthenticateRequest({
51+
options: buildTimeOptions,
52+
apiClient: {} as ApiClient,
53+
});
54+
55+
const overrides = {
56+
secretKey: 'r-sk',
57+
publishableKey: 'r-pk',
58+
};
59+
const requestState = await authenticateRequest({
60+
request: new Request('http://example.com/'),
61+
...overrides,
62+
});
63+
assert.propContains(requestState.toAuth()?.debug(), {
64+
...buildTimeOptions,
65+
...overrides,
66+
});
67+
});
68+
69+
test('ignore runtime apiUrl and apiVersion options', async assert => {
70+
const buildTimeOptions = {
71+
secretKey: 'sk',
72+
jwtKey: 'jwtKey',
73+
apiUrl: 'apiUrl',
74+
apiVersion: 'apiVersion',
75+
proxyUrl: 'proxyUrl',
76+
publishableKey: 'pk',
77+
isSatellite: false,
78+
domain: 'domain',
79+
audience: 'domain',
80+
};
81+
82+
const { authenticateRequest } = createAuthenticateRequest({
83+
options: buildTimeOptions,
84+
apiClient: {} as ApiClient,
85+
});
86+
87+
const requestState = await authenticateRequest({
88+
request: new Request('http://example.com/'),
89+
// @ts-expect-error is used to check runtime code
90+
apiUrl: 'r-apiUrl',
91+
apiVersion: 'r-apiVersion',
92+
});
93+
assert.propContains(requestState.toAuth()?.debug(), buildTimeOptions);
94+
});
95+
});
96+
};

packages/backend/src/tokens/factory.ts

Lines changed: 43 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,65 @@
11
import type { ApiClient } from '../api';
2-
import { API_URL, API_VERSION } from '../constants';
2+
import { mergePreDefinedOptions } from '../util/mergePreDefinedOptions';
33
import type { LoadInterstitialOptions } from './interstitial';
44
import { loadInterstitialFromLocal } from './interstitial';
55
import type { AuthenticateRequestOptions } from './request';
66
import { authenticateRequest as authenticateRequestOriginal, debugRequestState } from './request';
77

8+
type RunTimeOptions = Omit<AuthenticateRequestOptions, 'apiUrl' | 'apiVersion'>;
9+
10+
type BuildTimeOptions = Partial<
11+
Pick<
12+
AuthenticateRequestOptions,
13+
| 'apiUrl'
14+
| 'apiVersion'
15+
| 'audience'
16+
| 'domain'
17+
| 'isSatellite'
18+
| 'jwtKey'
19+
| 'proxyUrl'
20+
| 'publishableKey'
21+
| 'secretKey'
22+
>
23+
>;
24+
25+
const defaultOptions = {
26+
secretKey: '',
27+
jwtKey: '',
28+
apiUrl: undefined,
29+
apiVersion: undefined,
30+
proxyUrl: '',
31+
publishableKey: '',
32+
isSatellite: false,
33+
domain: '',
34+
audience: '',
35+
} satisfies BuildTimeOptions;
36+
837
export type CreateAuthenticateRequestOptions = {
9-
options: Partial<
10-
Pick<
11-
AuthenticateRequestOptions,
12-
| 'audience'
13-
| 'secretKey'
14-
| 'apiUrl'
15-
| 'apiVersion'
16-
| 'publishableKey'
17-
| 'jwtKey'
18-
| 'proxyUrl'
19-
| 'domain'
20-
| 'isSatellite'
21-
>
22-
>;
38+
options: BuildTimeOptions;
2339
apiClient: ApiClient;
2440
};
2541

2642
export function createAuthenticateRequest(params: CreateAuthenticateRequestOptions) {
2743
const { apiClient } = params;
28-
const {
29-
secretKey: buildtimeSecretKey = '',
30-
jwtKey: buildtimeJwtKey = '',
31-
apiUrl = API_URL,
32-
apiVersion = API_VERSION,
33-
proxyUrl: buildProxyUrl = '',
34-
publishableKey: buildtimePublishableKey = '',
35-
isSatellite: buildtimeIsSatellite = false,
36-
domain: buildtimeDomain = '',
37-
audience: buildtimeAudience = '',
38-
} = params.options;
44+
const buildTimeOptions = mergePreDefinedOptions(defaultOptions, params.options);
3945

40-
const authenticateRequest = ({
41-
secretKey: runtimeSecretKey,
42-
audience: runtimeAudience,
43-
proxyUrl: runtimeProxyUrl,
44-
publishableKey: runtimePublishableKey,
45-
jwtKey: runtimeJwtKey,
46-
isSatellite: runtimeIsSatellite,
47-
domain: runtimeDomain,
48-
...rest
49-
}: Omit<AuthenticateRequestOptions, 'apiUrl' | 'apiVersion'>) => {
46+
const authenticateRequest = (options: RunTimeOptions) => {
47+
const { apiUrl, apiVersion } = buildTimeOptions;
48+
const runTimeOptions = mergePreDefinedOptions(buildTimeOptions, options);
5049
return authenticateRequestOriginal({
51-
...rest,
52-
secretKey: runtimeSecretKey || buildtimeSecretKey,
53-
audience: runtimeAudience || buildtimeAudience,
50+
...options,
51+
...runTimeOptions,
52+
// We should add all the omitted props from options here (eg apiUrl / apiVersion)
53+
// to avoid runtime options override them.
5454
apiUrl,
5555
apiVersion,
56-
proxyUrl: runtimeProxyUrl || buildProxyUrl,
57-
publishableKey: runtimePublishableKey || buildtimePublishableKey,
58-
isSatellite: runtimeIsSatellite || buildtimeIsSatellite,
59-
domain: runtimeDomain || buildtimeDomain,
60-
jwtKey: runtimeJwtKey || buildtimeJwtKey,
6156
});
6257
};
6358

64-
const localInterstitial = ({
65-
publishableKey: runtimePublishableKey,
66-
proxyUrl: runtimeProxyUrl,
67-
isSatellite: runtimeIsSatellite,
68-
domain: runtimeDomain,
69-
...rest
70-
}: Omit<LoadInterstitialOptions, 'apiUrl'>) =>
71-
loadInterstitialFromLocal({
72-
...rest,
73-
proxyUrl: runtimeProxyUrl || buildProxyUrl,
74-
publishableKey: runtimePublishableKey || buildtimePublishableKey,
75-
isSatellite: runtimeIsSatellite || buildtimeIsSatellite,
76-
domain: runtimeDomain || buildtimeDomain,
77-
});
59+
const localInterstitial = (options: Omit<LoadInterstitialOptions, 'apiUrl'>) => {
60+
const runTimeOptions = mergePreDefinedOptions(buildTimeOptions, options);
61+
return loadInterstitialFromLocal({ ...options, ...runTimeOptions });
62+
};
7863

7964
// TODO: Replace this function with remotePublicInterstitial
8065
const remotePrivateInterstitial = () => apiClient.interstitial.getInterstitial();

packages/backend/src/tokens/interstitialRule.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { checkCrossOrigin } from '../util/request';
2-
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
2+
import { isDevelopmentFromSecretKey, isProductionFromSecretKey } from '../util/shared';
33
import type { AuthStatusOptionsType, RequestState } from './authStatus';
44
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
55
import { verifyToken } from './verify';
@@ -45,7 +45,7 @@ const isBrowser = (userAgent: string | undefined) => VALID_USER_AGENTS.test(user
4545
// automatically treated as signed out. This exception is needed for development, because the any // missing uat throws an interstitial in development.
4646
export const nonBrowserRequestInDevRule: InterstitialRule = options => {
4747
const { secretKey, userAgent } = options;
48-
if (isDevelopmentFromApiKey(secretKey || '') && !isBrowser(userAgent)) {
48+
if (isDevelopmentFromSecretKey(secretKey || '') && !isBrowser(userAgent)) {
4949
return signedOut(options, AuthErrorReason.HeaderMissingNonBrowser);
5050
}
5151
return undefined;
@@ -70,7 +70,7 @@ export const crossOriginRequestWithoutHeader: InterstitialRule = options => {
7070

7171
export const isPrimaryInDevAndRedirectsToSatellite: InterstitialRule = options => {
7272
const { secretKey = '', isSatellite, searchParams } = options;
73-
const isDev = isDevelopmentFromApiKey(secretKey);
73+
const isDev = isDevelopmentFromSecretKey(secretKey);
7474

7575
if (isDev && !isSatellite && shouldRedirectToSatelliteUrl(searchParams)) {
7676
return interstitial(options, AuthErrorReason.PrimaryRespondsToSyncing);
@@ -80,7 +80,7 @@ export const isPrimaryInDevAndRedirectsToSatellite: InterstitialRule = options =
8080

8181
export const potentialFirstLoadInDevWhenUATMissing: InterstitialRule = options => {
8282
const { secretKey = '', clientUat } = options;
83-
const res = isDevelopmentFromApiKey(secretKey);
83+
const res = isDevelopmentFromSecretKey(secretKey);
8484
if (res && !clientUat) {
8585
return interstitial(options, AuthErrorReason.CookieUATMissing);
8686
}
@@ -96,7 +96,7 @@ export const potentialRequestAfterSignInOrOutFromClerkHostedUiInDev: Interstitia
9696
const crossOriginReferrer =
9797
referrer && checkCrossOrigin({ originURL: new URL(referrer), host, forwardedHost, forwardedProto });
9898

99-
if (isDevelopmentFromApiKey(secretKey) && crossOriginReferrer) {
99+
if (isDevelopmentFromSecretKey(secretKey) && crossOriginReferrer) {
100100
return interstitial(options, AuthErrorReason.CrossOriginReferrer);
101101
}
102102
return undefined;
@@ -105,7 +105,7 @@ export const potentialRequestAfterSignInOrOutFromClerkHostedUiInDev: Interstitia
105105
export const potentialFirstRequestOnProductionEnvironment: InterstitialRule = options => {
106106
const { secretKey = '', clientUat, cookieToken } = options;
107107

108-
if (isProductionFromApiKey(secretKey) && !clientUat && !cookieToken) {
108+
if (isProductionFromSecretKey(secretKey) && !clientUat && !cookieToken) {
109109
return signedOut(options, AuthErrorReason.CookieAndUATMissing);
110110
}
111111
return undefined;

packages/backend/src/tokens/request.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { constants } from '../constants';
22
import { assertValidSecretKey } from '../util/assertValidSecretKey';
33
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
4-
import { isDevelopmentFromApiKey } from '../util/shared';
5-
import type { AuthStatusOptionsType, LoadResourcesOptions, RequestState } from './authStatus';
4+
import { isDevelopmentFromSecretKey } from '../util/shared';
5+
import type { AuthStatusOptionsType, RequestState } from './authStatus';
66
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
77
import type { TokenCarrier } from './errors';
88
import { TokenVerificationError, TokenVerificationErrorReason } from './errors';
@@ -32,7 +32,7 @@ export type OptionalVerifyTokenOptions = Partial<
3232
export type AuthenticateRequestOptions = AuthStatusOptionsType & OptionalVerifyTokenOptions & { request: Request };
3333

3434
function assertSignInUrlExists(signInUrl: string | undefined, key: string): asserts signInUrl is string {
35-
if (!signInUrl && isDevelopmentFromApiKey(key)) {
35+
if (!signInUrl && isDevelopmentFromSecretKey(key)) {
3636
throw new Error(`Missing signInUrl. Pass a signInUrl for dev instances if an app is satellite`);
3737
}
3838
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function mergePreDefinedOptions<T extends Record<string, any>>(preDefinedOptions: T, options: Partial<T>): T {
2+
return Object.keys(preDefinedOptions).reduce(
3+
(obj: T, key: string) => {
4+
return { ...obj, [key]: options[key] || obj[key] };
5+
},
6+
{ ...preDefinedOptions },
7+
);
8+
}

packages/backend/src/util/shared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { addClerkPrefix, getScriptUrl, getClerkJsMajorVersionOrTag } from '@clerk/shared/url';
22
export { callWithRetry } from '@clerk/shared/callWithRetry';
3-
export { isDevelopmentFromApiKey, isProductionFromApiKey, parsePublishableKey } from '@clerk/shared/keys';
3+
export { isDevelopmentFromSecretKey, isProductionFromSecretKey, parsePublishableKey } from '@clerk/shared/keys';
44
export { deprecated, deprecatedProperty } from '@clerk/shared/deprecated';
55

66
import { buildErrorThrower } from '@clerk/shared/error';

0 commit comments

Comments
 (0)