Skip to content

Commit 944b9de

Browse files
committed
feat(*): Set PACKAGE_NAME and PACKAGE_VERSION as the user-agent header in BAPI requests
1 parent 2a615bf commit 944b9de

31 files changed

+358
-193
lines changed

.changeset/fresh-boats-fry.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'gatsby-plugin-clerk': minor
3+
'@clerk/clerk-sdk-node': minor
4+
'@clerk/backend': minor
5+
'@clerk/fastify': minor
6+
'@clerk/nextjs': minor
7+
'@clerk/remix': minor
8+
---
9+
10+
Replace the `Clerk-Backend-SDK` header with `User-Agent` in BAPI requests and update it's value to contain both the package name and the package version of the clerk package
11+
executing the request. Eg request from `@clerk/nextjs` to BAPI with append `User-Agent: @clerk/[email protected]` using the latest version.
12+
13+
Miscellaneous changes: The backend test build changed to use tsup.

packages/backend/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"build:declarations": "tsc -p tsconfig.declarations.json",
4242
"publish:local": "npx yalc push --replace --sig",
4343
"build:lib": "tsup --env.NODE_ENV production",
44-
"build:tests": "tsc -p tsconfig.test.json",
44+
"build:tests": "tsup --config tsup.config.test.ts",
4545
"build:runtime": "rsync -r --include '*/' --include '*.js' --include '*.mjs' --exclude='*' src/runtime dist",
4646
"clean": "rimraf ./dist",
4747
"clean:tests": "rimraf ./tests/dist",

packages/backend/src/api/factory.test.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default (QUnit: QUnit) => {
4747
headers: {
4848
Authorization: 'Bearer deadbeef',
4949
'Content-Type': 'application/json',
50-
'Clerk-Backend-SDK': '@clerk/backend',
50+
'User-Agent': '@clerk/backend@0.0.0-test',
5151
},
5252
}),
5353
);
@@ -78,7 +78,7 @@ export default (QUnit: QUnit) => {
7878
headers: {
7979
Authorization: 'Bearer deadbeef',
8080
'Content-Type': 'application/json',
81-
'Clerk-Backend-SDK': '@clerk/backend',
81+
'User-Agent': '@clerk/backend@0.0.0-test',
8282
},
8383
}),
8484
);
@@ -112,7 +112,7 @@ export default (QUnit: QUnit) => {
112112
headers: {
113113
Authorization: 'Bearer deadbeef',
114114
'Content-Type': 'application/json',
115-
'Clerk-Backend-SDK': '@clerk/backend',
115+
'User-Agent': '@clerk/backend@0.0.0-test',
116116
},
117117
}),
118118
);
@@ -136,7 +136,7 @@ export default (QUnit: QUnit) => {
136136
headers: {
137137
Authorization: 'Bearer deadbeef',
138138
'Content-Type': 'application/json',
139-
'Clerk-Backend-SDK': '@clerk/backend',
139+
'User-Agent': '@clerk/backend@0.0.0-test',
140140
},
141141
body: JSON.stringify({
142142
first_name: 'John',
@@ -180,7 +180,7 @@ export default (QUnit: QUnit) => {
180180
headers: {
181181
Authorization: 'Bearer deadbeef',
182182
'Content-Type': 'application/json',
183-
'Clerk-Backend-SDK': '@clerk/backend',
183+
'User-Agent': '@clerk/backend@0.0.0-test',
184184
},
185185
}),
186186
);
@@ -204,7 +204,7 @@ export default (QUnit: QUnit) => {
204204
headers: {
205205
Authorization: 'Bearer deadbeef',
206206
'Content-Type': 'application/json',
207-
'Clerk-Backend-SDK': '@clerk/backend',
207+
'User-Agent': '@clerk/backend@0.0.0-test',
208208
},
209209
}),
210210
);
@@ -229,7 +229,7 @@ export default (QUnit: QUnit) => {
229229
headers: {
230230
Authorization: 'Bearer deadbeef',
231231
'Content-Type': 'application/json',
232-
'Clerk-Backend-SDK': '@clerk/backend',
232+
'User-Agent': '@clerk/backend@0.0.0-test',
233233
},
234234
}),
235235
);

packages/backend/src/api/request.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export function buildRequest(options: CreateBackendApiOptions) {
115115
// Build headers
116116
const headers: Record<string, any> = {
117117
Authorization: `Bearer ${key}`,
118-
'Clerk-Backend-SDK': userAgent,
118+
'User-Agent': userAgent,
119119
...headerParams,
120120
};
121121

packages/backend/src/constants.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
export const API_URL = 'https://api.clerk.dev';
22
export const API_VERSION = 'v1';
33

4-
// TODO: Get information from package.json or define them from ESBuild
5-
export const USER_AGENT = `@clerk/backend`;
4+
export const USER_AGENT = `${PACKAGE_NAME}@${PACKAGE_VERSION}`;
65
export const MAX_CACHE_LAST_UPDATED_AT_SECONDS = 5 * 60;
76

87
const Attributes = {
8+
AuthToken: '__clerkAuthToken',
99
AuthStatus: '__clerkAuthStatus',
1010
AuthReason: '__clerkAuthReason',
1111
AuthMessage: '__clerkAuthMessage',
@@ -17,6 +17,7 @@ const Cookies = {
1717
} as const;
1818

1919
const Headers = {
20+
AuthToken: 'x-clerk-auth-token',
2021
AuthStatus: 'x-clerk-auth-status',
2122
AuthReason: 'x-clerk-auth-reason',
2223
AuthMessage: 'x-clerk-auth-message',
@@ -36,6 +37,7 @@ const Headers = {
3637

3738
const SearchParams = {
3839
AuthStatus: Headers.AuthStatus,
40+
AuthToken: Headers.AuthToken,
3941
} as const;
4042

4143
const ContentTypes = {

packages/backend/src/tokens/authObjects.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type { RequestState } from './authStatus';
1515
import type { AuthenticateRequestOptions } from './request';
1616

1717
type AuthObjectDebugData = Partial<AuthenticateRequestOptions & RequestState>;
18-
type CreateAuthObjectDebug = (data?: AuthObjectDebugData) => AuthObjectDebug;
18+
type CreateAuthObjectDebug = (data?: Record<string, unknown>) => AuthObjectDebug;
1919
type AuthObjectDebug = () => unknown;
2020

2121
export type SignedInAuthObjectOptions = {
@@ -71,9 +71,9 @@ export type AuthObject = SignedInAuthObject | SignedOutAuthObject;
7171
const createDebug: CreateAuthObjectDebug = data => {
7272
return () => {
7373
const res = { ...data } || {};
74-
res.apiKey = (res.apiKey || '').substring(0, 7);
75-
res.secretKey = (res.secretKey || '').substring(0, 7);
76-
res.jwtKey = (res.jwtKey || '').substring(0, 7);
74+
res.apiKey = ((res.apiKey as string) || '').substring(0, 7);
75+
res.secretKey = ((res.secretKey as string) || '').substring(0, 7);
76+
res.jwtKey = ((res.jwtKey as string) || '').substring(0, 7);
7777
return { ...res };
7878
};
7979
};

packages/backend/src/tokens/authStatus.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export type SignedInState = {
2929
isInterstitial: false;
3030
isUnknown: false;
3131
toAuth: () => SignedInAuthObject;
32+
token: string;
3233
};
3334

3435
export type SignedOutState = {
@@ -48,6 +49,7 @@ export type SignedOutState = {
4849
isInterstitial: false;
4950
isUnknown: false;
5051
toAuth: () => SignedOutAuthObject;
52+
token: null;
5153
};
5254

5355
export type InterstitialState = Omit<SignedOutState, 'isInterstitial' | 'status' | 'toAuth'> & {
@@ -83,7 +85,10 @@ export type AuthReason = AuthErrorReason | TokenVerificationErrorReason;
8385

8486
export type RequestState = SignedInState | SignedOutState | InterstitialState | UnknownState;
8587

86-
export async function signedIn<T>(options: T, sessionClaims: JwtPayload): Promise<SignedInState> {
88+
export async function signedIn<T extends { token: string }>(
89+
options: T,
90+
sessionClaims: JwtPayload,
91+
): Promise<SignedInState> {
8792
const {
8893
apiKey,
8994
secretKey,
@@ -103,6 +108,7 @@ export async function signedIn<T>(options: T, sessionClaims: JwtPayload): Promis
103108
signUpUrl,
104109
afterSignInUrl,
105110
afterSignUpUrl,
111+
token,
106112
} = options as any;
107113

108114
const { sid: sessionId, org_id: orgId, sub: userId } = sessionClaims;
@@ -159,6 +165,7 @@ export async function signedIn<T>(options: T, sessionClaims: JwtPayload): Promis
159165
isInterstitial: false,
160166
isUnknown: false,
161167
toAuth: () => authObject,
168+
token,
162169
};
163170
}
164171

@@ -192,6 +199,7 @@ export function signedOut<T>(options: T, reason: AuthReason, message = ''): Sign
192199
isInterstitial: false,
193200
isUnknown: false,
194201
toAuth: () => signedOutAuthObject({ ...options, status: AuthStatus.SignedOut, reason, message }),
202+
token: null,
195203
};
196204
}
197205

@@ -224,6 +232,7 @@ export function interstitial<T>(options: T, reason: AuthReason, message = ''): I
224232
isInterstitial: true,
225233
isUnknown: false,
226234
toAuth: () => null,
235+
token: null,
227236
};
228237
}
229238

@@ -246,5 +255,6 @@ export function unknownState<T>(options: T, reason: AuthReason, message = ''): U
246255
isInterstitial: false,
247256
isUnknown: true,
248257
toAuth: () => null,
258+
token: null,
249259
};
250260
}

packages/backend/src/tokens/interstitialRule.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,13 @@ export const hasPositiveClientUatButCookieIsMissing: InterstitialRule = options
123123
export const hasValidHeaderToken: InterstitialRule = async options => {
124124
const { headerToken } = options as any;
125125
const sessionClaims = await verifyRequestState(options, headerToken);
126-
return await signedIn(options, sessionClaims);
126+
return await signedIn({ ...options, token: headerToken }, sessionClaims);
127127
};
128128

129129
export const hasValidCookieToken: InterstitialRule = async options => {
130130
const { cookieToken, clientUat } = options as any;
131131
const sessionClaims = await verifyRequestState(options, cookieToken);
132-
const state = await signedIn(options, sessionClaims);
132+
const state = await signedIn({ ...options, token: cookieToken }, sessionClaims);
133133

134134
const jwt = state.toAuth().sessionClaims;
135135
const cookieTokenIsOutdated = jwt.iat < Number.parseInt(clientUat);

packages/backend/src/tokens/keys.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default (QUnit: QUnit) => {
5959
headers: {
6060
Authorization: 'Bearer deadbeef',
6161
'Content-Type': 'application/json',
62-
'Clerk-Backend-SDK': '@clerk/backend',
62+
'User-Agent': '@clerk/backend@0.0.0-test',
6363
},
6464
});
6565
assert.propEqual(jwk, mockRsaJwk);
@@ -78,7 +78,7 @@ export default (QUnit: QUnit) => {
7878
headers: {
7979
Authorization: 'Bearer deadbeef',
8080
'Content-Type': 'application/json',
81-
'Clerk-Backend-SDK': '@clerk/backend',
81+
'User-Agent': '@clerk/backend@0.0.0-test',
8282
},
8383
});
8484
assert.propEqual(jwk, mockRsaJwk);
@@ -96,7 +96,7 @@ export default (QUnit: QUnit) => {
9696
method: 'GET',
9797
headers: {
9898
'Content-Type': 'application/json',
99-
'Clerk-Backend-SDK': '@clerk/backend',
99+
'User-Agent': '@clerk/backend@0.0.0-test',
100100
},
101101
});
102102
assert.propEqual(jwk, mockRsaJwk);
@@ -116,7 +116,7 @@ export default (QUnit: QUnit) => {
116116
headers: {
117117
Authorization: 'Bearer sk_test_deadbeef',
118118
'Content-Type': 'application/json',
119-
'Clerk-Backend-SDK': '@clerk/backend',
119+
'User-Agent': '@clerk/backend@0.0.0-test',
120120
},
121121
});
122122
assert.propEqual(jwk, mockRsaJwk);
@@ -203,7 +203,7 @@ export default (QUnit: QUnit) => {
203203
action: 'Contact [email protected]',
204204
});
205205
assert.propContains(err, {
206-
message: `Unable to find a signing key in JWKS that matches the kid='${kid}' of the provided session token. Please make sure that the __session cookie or the HTTP authorization header contain a Clerk-generated session JWT. The following kid are available: ${mockRsaJwkKid}, local`,
206+
message: `Unable to find a signing key in JWKS that matches the kid='${kid}' of the provided session token. Please make sure that the __session cookie or the HTTP authorization header contain a Clerk-generated session JWT. The following kid are available: local, ${mockRsaJwkKid}`,
207207
});
208208
} else {
209209
// This should never be reached. If it does, the suite should fail
@@ -229,7 +229,7 @@ export default (QUnit: QUnit) => {
229229
action: 'Contact [email protected]',
230230
});
231231
assert.propContains(err, {
232-
message: `Unable to find a signing key in JWKS that matches the kid='${kid}' of the provided session token. Please make sure that the __session cookie or the HTTP authorization header contain a Clerk-generated session JWT. The following kid are available: ${mockRsaJwkKid}, local`,
232+
message: `Unable to find a signing key in JWKS that matches the kid='${kid}' of the provided session token. Please make sure that the __session cookie or the HTTP authorization header contain a Clerk-generated session JWT. The following kid are available: local, ${mockRsaJwkKid}`,
233233
});
234234
} else {
235235
// This should never be reached. If it does, the suite should fail

packages/backend/src/tokens/request.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ function assertSignedOut(
3636
domain: '',
3737
message: '',
3838
toAuth: {},
39+
token: null,
3940
...expectedState,
4041
});
4142
}
@@ -80,6 +81,7 @@ function assertInterstitial(
8081
afterSignUpUrl: '',
8182
domain: '',
8283
toAuth: {},
84+
token: null,
8385
...expectedState,
8486
});
8587
}

packages/backend/src/tokens/verify.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export default (QUnit: QUnit) => {
5151
headers: {
5252
Authorization: 'Bearer a-valid-key',
5353
'Content-Type': 'application/json',
54-
'Clerk-Backend-SDK': '@clerk/backend',
54+
'User-Agent': '@clerk/backend@0.0.0-test',
5555
},
5656
});
5757
assert.propEqual(payload, mockJwtPayload);
@@ -68,7 +68,7 @@ export default (QUnit: QUnit) => {
6868
method: 'GET',
6969
headers: {
7070
'Content-Type': 'application/json',
71-
'Clerk-Backend-SDK': '@clerk/backend',
71+
'User-Agent': '@clerk/backend@0.0.0-test',
7272
},
7373
});
7474
assert.propEqual(payload, mockJwtPayload);

packages/backend/tsup.config.test.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { defineConfig } from 'tsup';
2+
3+
// @ts-ignore
4+
import { name } from './package.json';
5+
6+
export default defineConfig(overrideOptions => {
7+
const isWatch = !!overrideOptions.watch;
8+
9+
return {
10+
entry: ['./src/**/*.{ts,js}'],
11+
outDir: 'tests/dist/',
12+
define: {
13+
PACKAGE_NAME: `"${name}"`,
14+
// use "test" instead of actual package version to avoid updating the tests
15+
// depending on it (eg userAgent related) on every version bump
16+
PACKAGE_VERSION: `"0.0.0-test"`,
17+
__DEV__: `${isWatch}`,
18+
},
19+
external: ['#crypto'],
20+
clean: true,
21+
minify: false,
22+
tsconfig: 'tsconfig.test.json',
23+
format: 'cjs',
24+
};
25+
});

packages/fastify/jest.config.js

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
/** @type {import('ts-jest').JestConfigWithTsJest} */
22
module.exports = {
3+
globals: {
4+
PACKAGE_NAME: '@clerk/fastify',
5+
PACKAGE_VERSION: '0.0.0-test',
6+
},
37
displayName: 'fastify',
48
injectGlobals: true,
59
roots: ['<rootDir>/src'],

packages/fastify/src/__snapshots__/clerkClient.test.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ exports[`clerk initializes clerk with constants 1`] = `
88
"apiVersion": "v1",
99
"jwtKey": "",
1010
"secretKey": "TEST_API_KEY",
11+
"userAgent": "@clerk/[email protected]",
1112
},
1213
],
1314
]

packages/fastify/src/__snapshots__/constants.test.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ exports[`constants from environment variables 1`] = `
1414
"AuthMessage": "x-clerk-auth-message",
1515
"AuthReason": "x-clerk-auth-reason",
1616
"AuthStatus": "x-clerk-auth-status",
17+
"AuthToken": "x-clerk-auth-token",
1718
"Authorization": "authorization",
1819
"ClerkRedirectTo": "x-clerk-redirect-to",
1920
"CloudFrontForwardedProto": "cloudfront-forwarded-proto",

packages/fastify/src/clerkClient.ts

+2
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ export const clerkClient = createClerkClient({
99
apiUrl: API_URL,
1010
apiVersion: API_VERSION,
1111
jwtKey: JWT_KEY,
12+
// @ts-ignore - defined by tsup config
13+
userAgent: `${PACKAGE_NAME}@${PACKAGE_VERSION}`,
1214
});

packages/gatsby-plugin-clerk/src/ssr/clerkClient.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const clerkClient = Clerk({
99
apiUrl: API_URL,
1010
apiVersion: API_VERSION,
1111
// TODO: Fetch version from package.json
12-
userAgent: 'gatsby-plugin-clerk',
12+
userAgent: `${PACKAGE_NAME}@${PACKAGE_VERSION}`,
1313
});
1414

1515
const createClerkClient = Clerk;

packages/nextjs/jest.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/** @type {import('ts-jest').JestConfigWithTsJest} */
22
module.exports = {
33
globals: {
4+
PACKAGE_NAME: '@clerk/nextjs',
45
PACKAGE_VERSION: '0.0.0-test',
56
},
67
displayName: 'nextjs',

0 commit comments

Comments
 (0)