fix(voice): validate Codex OAuth and report unknown usage

This commit is contained in:
devv-shayan committed 2026-09-27 21:09:26 +05:00
1 parent 881e24485c
commit a398a25934
8 files changed
+265 -19

No files matched your search

+78 -7
View File
@@ -1,5 +1,10 @@
import { spawn } from 'node:child_process';
import { accessSync, constants as fsConstants, readFileSync } from 'node:fs';
import {
accessSync,
constants as fsConstants,
readFileSync,
statSync,
} from 'node:fs';
import os from 'node:os';
import path from 'node:path';
@@ -16,22 +21,88 @@ export function codexAuthPath({
);
}
/** An optional operator lock that refuses the metered API-key voice lane. */
export function preferCodexOAuth(environment = process.env) {
if (!Object.hasOwn(environment, 'GEV_PREFER_CODEX_OAUTH')) return false;
const value = String(environment.GEV_PREFER_CODEX_OAUTH || '')
.trim()
.toLowerCase();
if (['1', 'true', 'yes', 'on'].includes(value)) return true;
if (['0', 'false', 'no', 'off'].includes(value)) return false;
throw new Error(
'GEV_PREFER_CODEX_OAUTH must be true or false. Refusing to choose a voice auth mode.',
);
}
export function readCodexOAuthAccessToken({
authPath = codexAuthPath(),
readFile = readFileSync,
statFile = statSync,
nowMs = Date.now(),
} = {}) {
const unavailable =
'ChatGPT sign-in is unavailable. Sign in to Codex or the ChatGPT desktop app and try again.';
let parsed;
try {
parsed = JSON.parse(readFile(authPath, 'utf8'));
} catch {
throw new Error(
'ChatGPT sign-in is unavailable. Sign in to Codex or the ChatGPT desktop app and try again.',
);
throw new Error(unavailable);
}
const token = String(parsed?.tokens?.access_token || '').trim();
if (!token) {
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(unavailable);
}
const mode = String(parsed.auth_mode || '')
.trim()
.toLowerCase();
const chatGptMode = mode
? mode === 'chatgpt' || mode === 'chatgptauthtokens'
: typeof parsed.OPENAI_API_KEY !== 'string';
const token = parsed.tokens?.access_token;
const refresh = parsed.tokens?.refresh_token;
if (
!chatGptMode ||
typeof token !== 'string' ||
!token.trim() ||
typeof refresh !== 'string' ||
!refresh.trim()
) {
throw new Error(unavailable);
}
// This only checks token shape and expiry. OpenAI verifies the signature when
// minting the short-lived Realtime credential; we never modify auth.json.
const parts = token.split('.');
if (
parts.length !== 3 ||
parts.some((part) => !/^[A-Za-z0-9_-]+$/.test(part))
) {
throw new Error(unavailable);
}
let payload;
try {
payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
} catch {
throw new Error(unavailable);
}
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
throw new Error(unavailable);
}
let expiresAtMs;
if (Object.hasOwn(payload, 'exp')) {
if (typeof payload.exp !== 'number' || !Number.isFinite(payload.exp)) {
throw new Error(unavailable);
}
expiresAtMs = payload.exp * 1000;
} else {
try {
expiresAtMs = statFile(authPath).mtimeMs + 60 * 60 * 1000;
} catch {
throw new Error(unavailable);
}
}
if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs + 60_000) {
throw new Error(
'ChatGPT sign-in is unavailable. Sign in to Codex or the ChatGPT desktop app and try again.',
'ChatGPT sign-in has expired. Run codex login and try again.',
);
}
return token;