Skip to content

Commit 3a4cfaf

Browse files
committed
fix(oauth): read SENTRY_CLIENT_ID at call time instead of module load
The test preload deletes process.env.SENTRY_CLIENT_ID before oauth.ts loads, causing the module-level constant to capture an empty string. Tests that later set SENTRY_CLIENT_ID in beforeEach had no effect since the constant was already captured. This caused refreshAccessToken to throw ConfigError (not AuthError), which the ky afterResponse hook caught silently, preventing 401 retry from working. Replace the module-level constant with a getClientId() function that reads process.env at call time.
1 parent d15b2b9 commit 3a4cfaf

File tree

1 file changed

+18
-8
lines changed

1 file changed

+18
-8
lines changed

src/lib/oauth.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,20 @@ const SENTRY_URL = process.env.SENTRY_URL ?? "https://sentry.io";
2424
* Build-time: Injected via Bun.build({ define: { SENTRY_CLIENT_ID: "..." } })
2525
* Runtime: Can be overridden via SENTRY_CLIENT_ID env var (for self-hosted)
2626
*
27+
* Read at call time (not module load time) so tests can set process.env.SENTRY_CLIENT_ID
28+
* after module initialization.
29+
*
2730
* @see script/build.ts
2831
*/
2932
declare const SENTRY_CLIENT_ID_BUILD: string | undefined;
30-
const SENTRY_CLIENT_ID =
31-
process.env.SENTRY_CLIENT_ID ??
32-
(typeof SENTRY_CLIENT_ID_BUILD !== "undefined" ? SENTRY_CLIENT_ID_BUILD : "");
33+
function getClientId(): string {
34+
return (
35+
process.env.SENTRY_CLIENT_ID ??
36+
(typeof SENTRY_CLIENT_ID_BUILD !== "undefined"
37+
? SENTRY_CLIENT_ID_BUILD
38+
: "")
39+
);
40+
}
3341

3442
// OAuth scopes requested for the CLI
3543
const SCOPES = [
@@ -85,7 +93,8 @@ async function fetchWithConnectionError(
8593

8694
/** Request a device code from Sentry's device authorization endpoint */
8795
function requestDeviceCode() {
88-
if (!SENTRY_CLIENT_ID) {
96+
const clientId = getClientId();
97+
if (!clientId) {
8998
throw new ConfigError(
9099
"SENTRY_CLIENT_ID is required for authentication",
91100
"Set SENTRY_CLIENT_ID environment variable or use a pre-built binary"
@@ -99,7 +108,7 @@ function requestDeviceCode() {
99108
method: "POST",
100109
headers: { "Content-Type": "application/x-www-form-urlencoded" },
101110
body: new URLSearchParams({
102-
client_id: SENTRY_CLIENT_ID,
111+
client_id: clientId,
103112
scope: SCOPES,
104113
}),
105114
}
@@ -142,7 +151,7 @@ function pollForToken(deviceCode: string): Promise<TokenResponse> {
142151
method: "POST",
143152
headers: { "Content-Type": "application/x-www-form-urlencoded" },
144153
body: new URLSearchParams({
145-
client_id: SENTRY_CLIENT_ID,
154+
client_id: getClientId(),
146155
device_code: deviceCode,
147156
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
148157
}),
@@ -313,7 +322,8 @@ export async function setApiToken(token: string): Promise<void> {
313322
export function refreshAccessToken(
314323
refreshToken: string
315324
): Promise<TokenResponse> {
316-
if (!SENTRY_CLIENT_ID) {
325+
const clientId = getClientId();
326+
if (!clientId) {
317327
throw new ConfigError(
318328
"SENTRY_CLIENT_ID is required for token refresh",
319329
"Set SENTRY_CLIENT_ID environment variable or use a pre-built binary"
@@ -327,7 +337,7 @@ export function refreshAccessToken(
327337
method: "POST",
328338
headers: { "Content-Type": "application/x-www-form-urlencoded" },
329339
body: new URLSearchParams({
330-
client_id: SENTRY_CLIENT_ID,
340+
client_id: clientId,
331341
grant_type: "refresh_token",
332342
refresh_token: refreshToken,
333343
}),

0 commit comments

Comments
 (0)