Skip to content

Commit d747598

Browse files
authored
Fix telemetry (#30)
<!-- If this pull request closes an issue, please mention the issue number below --> Closes # <!-- Issue # here --> ## 💸 TL;DR <!-- What's the three sentence summary of purpose of the PR --> The auth token logic didn't work for some reason, this is lifted from the CLI project and should work. ## 📜 Details [Design Doc](<!-- insert Google Doc link here if applicable -->) [Jira](<!-- insert Jira link if applicable -->) <!-- Add additional details required for the PR: breaking changes, screenshots, external dependency changes --> ## 🧪 Testing Steps / Validation <!-- add details on how this PR has been tested, include reproductions and screenshots where applicable --> Use the dev MCP against prod and saw a 401 coming from the events endpoint. I fixed the auth token and then got a 200. ## ✅ Checks <!-- Make sure your pr passes the CI checks and do check the following fields as needed - --> - [ ] CI tests (if present) are passing - [ ] Adheres to code style for repo - [ ] Contributor License Agreement (CLA) completed if not a Reddit employee
1 parent ff804f9 commit d747598

1 file changed

Lines changed: 73 additions & 16 deletions

File tree

src/utils/telemetry.ts

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
/** Lifted from: https://github.snooguts.net/reddit/reddit-devplatform-monorepo/tree/main/packages/cli/src */
2+
import crypto from 'crypto';
3+
import fs from 'fs/promises';
24
import os from 'os';
3-
import { version } from '../../package.json';
45
import path from 'path';
5-
import fs from 'fs/promises';
6-
import crypto from 'crypto';
6+
import { version } from '../../package.json';
77
import { logger } from './logger';
88

9-
async function isFile(path: string): Promise<boolean> {
9+
async function isFile(filename: string): Promise<boolean> {
1010
try {
11-
const stat = await fs.stat(path);
11+
const stat = await fs.stat(filename);
1212
return stat.isFile();
1313
} catch {
1414
return false;
1515
}
1616
}
1717

18+
const DEVVIT_AUTH_TOKEN = 'DEVVIT_AUTH_TOKEN';
19+
1820
/** @type {boolean} See envvar.md. */
1921
const MY_PORTAL_ENABLED = !!process.env.MY_PORTAL && process.env.MY_PORTAL !== '0';
2022

@@ -68,7 +70,6 @@ function getHeaders(): Headers {
6870
logger.warn(`Warning: setting devvit-canary to "${process.env.DEVVIT_CANARY}"`);
6971
headers.set(...HEADER_DEVVIT_CANARY(process.env.DEVVIT_CANARY));
7072
}
71-
7273
return headers;
7374
}
7475

@@ -107,18 +108,75 @@ async function getTelemetrySessionId(): Promise<string> {
107108
return sessionId;
108109
}
109110

110-
async function getToken(): Promise<string | undefined> {
111-
const tokenFilename = getTokenFilename();
112-
const isTokenFileCreated = await isFile(tokenFilename);
113-
114-
if (!isTokenFileCreated) return;
111+
type StoredTokenJSON = {
112+
readonly refreshToken: string;
113+
readonly accessToken: string;
114+
readonly expiresAt: number;
115+
readonly scope: string;
116+
readonly tokenType: string;
117+
};
115118

119+
/**
120+
* Copied from CLI: packages/cli/src/lib/auth/StoredToken.ts
121+
*
122+
* We only need the access token for telemetry.
123+
*/
124+
function storedTokenFromBase64(base64: string): StoredTokenJSON | undefined {
125+
let token: unknown;
116126
try {
117-
const contents = await fs.readFile(tokenFilename, 'utf-8');
118-
return JSON.parse(contents).token;
119-
} catch (error) {
127+
token = JSON.parse(Buffer.from(base64, 'base64').toString('utf8'));
128+
} catch {
129+
return undefined;
130+
}
131+
132+
if (!token || typeof token !== 'object') {
133+
return undefined;
134+
}
135+
136+
const candidate = token as Partial<StoredTokenJSON>;
137+
if (
138+
typeof candidate.accessToken !== 'string' ||
139+
typeof candidate.expiresAt !== 'number' ||
140+
typeof candidate.refreshToken !== 'string' ||
141+
typeof candidate.scope !== 'string' ||
142+
candidate.tokenType !== 'bearer'
143+
) {
120144
return undefined;
121145
}
146+
147+
return candidate as StoredTokenJSON;
148+
}
149+
150+
async function getAccessToken(): Promise<string | undefined> {
151+
let rawToken: string;
152+
153+
if (process.env[DEVVIT_AUTH_TOKEN]) {
154+
rawToken = process.env[DEVVIT_AUTH_TOKEN];
155+
} else {
156+
const tokenFilename = getTokenFilename();
157+
if (!(await isFile(tokenFilename))) {
158+
return undefined;
159+
}
160+
161+
rawToken = await fs.readFile(tokenFilename, { encoding: 'utf8' });
162+
if (rawToken == null || rawToken.length === 0) {
163+
return undefined;
164+
}
165+
}
166+
167+
try {
168+
const jsonParse = JSON.parse(rawToken) as { token?: unknown };
169+
if (typeof jsonParse.token !== 'string') {
170+
return undefined;
171+
}
172+
173+
const token = storedTokenFromBase64(jsonParse.token);
174+
return token?.accessToken;
175+
} catch {
176+
// Old format: file contents are base64(JSON(StoredToken))
177+
const token = storedTokenFromBase64(rawToken);
178+
return token?.accessToken;
179+
}
122180
}
123181

124182
export const sendEvent = async (
@@ -148,10 +206,9 @@ export const sendEvent = async (
148206
};
149207

150208
const headers = getHeaders();
151-
152209
headers.set('content-type', 'application/json');
153210

154-
const token = await getToken();
211+
const token = await getAccessToken();
155212
if (token) {
156213
headers.set('authorization', `bearer ${token}`);
157214
}

0 commit comments

Comments
 (0)