File size: 2,483 Bytes
5d0a52f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347f81b
 
 
 
 
 
 
 
 
 
 
 
 
 
5d0a52f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
 * JWT decode utilities for Codex Desktop proxy.
 * No signature verification — just payload extraction.
 */

export function decodeJwtPayload(token: string): Record<string, unknown> | null {
  const parts = token.split(".");
  if (parts.length < 2) return null;
  try {
    const payload = Buffer.from(parts[1], "base64url").toString("utf8");
    const parsed = JSON.parse(payload);
    if (typeof parsed === "object" && parsed !== null) {
      return parsed as Record<string, unknown>;
    }
    return null;
  } catch {
    return null;
  }
}

export function extractChatGptAccountId(token: string): string | null {
  const payload = decodeJwtPayload(token);
  if (!payload) return null;
  try {
    const auth = payload["https://api.openai.com/auth"];
    if (auth && typeof auth === "object" && auth !== null) {
      const accountId = (auth as Record<string, unknown>).chatgpt_account_id;
      return typeof accountId === "string" ? accountId : null;
    }
  } catch {
    // ignore
  }
  return null;
}

export function extractUserProfile(
  token: string,
): { email?: string; chatgpt_user_id?: string; chatgpt_plan_type?: string } | null {
  const payload = decodeJwtPayload(token);
  if (!payload) return null;
  try {
    const profile = payload["https://api.openai.com/profile"] as Record<string, unknown> | undefined;
    const auth = payload["https://api.openai.com/auth"] as Record<string, unknown> | undefined;

    const email = typeof profile?.email === "string" ? profile.email : undefined;
    // chatgpt_plan_type lives in the /auth claim, not /profile
    const chatgpt_plan_type =
      (typeof auth?.chatgpt_plan_type === "string" ? auth.chatgpt_plan_type : undefined) ??
      (typeof profile?.chatgpt_plan_type === "string" ? profile.chatgpt_plan_type : undefined);
    const chatgpt_user_id =
      (typeof auth?.chatgpt_user_id === "string" ? auth.chatgpt_user_id : undefined) ??
      (typeof profile?.chatgpt_user_id === "string" ? profile.chatgpt_user_id : undefined);

    if (email || chatgpt_plan_type || chatgpt_user_id) {
      return { email, chatgpt_user_id, chatgpt_plan_type };
    }
  } catch {
    // ignore
  }
  return null;
}

export function isTokenExpired(token: string, marginSeconds = 0): boolean {
  const payload = decodeJwtPayload(token);
  if (!payload) return true;
  const exp = payload.exp;
  if (typeof exp !== "number") return true;
  const now = Math.floor(Date.now() / 1000);
  return now >= exp - marginSeconds;
}