Repository navigation
Fix OAuth client_secret issues (#103) & Codex Business quotas (#101) - #104
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@codex review |
|
To use Codex here, create a Codex account and connect to github. |
Summary of ChangesHello @diegosouzapw, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request resolves two distinct issues: one related to the flexible handling of OAuth Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
The pull request introduces improvements for handling OAuth client secrets by making them conditional, which enhances flexibility and robustness for different OAuth provider configurations. It also adds logic to fetch and utilize ChatGPT account IDs for Codex usage tracking, including support for 'biz' plan types. The changes are generally well-implemented and address the stated issues.
| try { | ||
| let accountId = null; | ||
| try { | ||
| const accountsRes = await fetch("https://chatgpt.com/backend-api/accounts/check/v4", { |
There was a problem hiding this comment.
The URL for fetching ChatGPT accounts is hardcoded here. It would be more maintainable to define this URL as a constant, similar to CODEX_CONFIG.usageUrl, to centralize configuration and make it easier to update if the endpoint changes in the future.
| const accountsRes = await fetch("https://chatgpt.com/backend-api/accounts/check/v4", { | |
| const accountsRes = await fetch(CODEX_CONFIG.accountsCheckUrl || "https://chatgpt.com/backend-api/accounts/check/v4", { |
There was a problem hiding this comment.
Pull request overview
This PR addresses OAuth token exchange failures when client_secret is missing/empty (notably in remote server mode) and improves OpenAI Codex Business quota detection/display by selecting the correct workspace/account context.
Changes:
- Make OAuth token exchange for iFlow/Gemini/Antigravity omit
client_secret(and iFlow Basic auth) when the secret is not configured. - Improve plan-tier normalization to recognize “biz” / “BIZ” as Business.
- For Codex usage, attempt to resolve a non-free (preferably biz) workspace account id and pass it via
chatgpt-account-idto fetch correct quotas.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/oauth/providers/iflow.ts | Conditionalizes Basic auth header and client_secret body param to avoid invalid credentials when unset. |
| src/lib/oauth/providers/gemini.ts | Avoids sending an empty client_secret during token exchange. |
| src/lib/oauth/providers/antigravity.ts | Avoids sending an empty client_secret during token exchange. |
| src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx | Treats “BIZ” plan strings as Business tier for UI labeling/filtering. |
| open-sse/services/usage.ts | Adds Codex workspace discovery and sends chatgpt-account-id header to retrieve correct quota data for Business accounts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const accountsArray = Object.values(accountsData.accounts) as any[]; | ||
| const targetWorkspace = | ||
| accountsArray.find((a) => a.account?.plan_type === "biz") || | ||
| accountsArray.find((a) => a.account?.plan_type !== "free") || |
There was a problem hiding this comment.
In the workspace selection, accountsArray.find((a) => a.account?.plan_type !== "free") will also match entries where plan_type is undefined/missing (because undefined !== "free" is true), which can pick an arbitrary workspace/accountId and still produce incorrect quota results. Consider tightening this predicate to require a defined plan_type (e.g., a.account?.plan_type && a.account.plan_type !== "free") or explicitly prefer known plan types in a allowlist.
| accountsArray.find((a) => a.account?.plan_type !== "free") || | |
| accountsArray.find( | |
| (a) => a.account?.plan_type && a.account.plan_type !== "free" | |
| ) || |
| async function getCodexUsage(accessToken) { | ||
| try { | ||
| let accountId = null; | ||
| try { | ||
| const accountsRes = await fetch("https://chatgpt.com/backend-api/accounts/check/v4", { | ||
| method: "GET", | ||
| headers: { | ||
| Authorization: `Bearer ${accessToken}`, | ||
| Accept: "application/json", | ||
| }, | ||
| }); | ||
| if (accountsRes.ok) { | ||
| const accountsData = await accountsRes.json(); | ||
| if (accountsData.accounts) { | ||
| const accountsArray = Object.values(accountsData.accounts) as any[]; | ||
| const targetWorkspace = | ||
| accountsArray.find((a) => a.account?.plan_type === "biz") || | ||
| accountsArray.find((a) => a.account?.plan_type !== "free") || | ||
| accountsArray.find((a) => a.is_default) || | ||
| accountsArray[0]; | ||
| if (targetWorkspace && targetWorkspace.account?.id) { | ||
| accountId = targetWorkspace.account.id; | ||
| } | ||
| } | ||
| } | ||
| } catch (err) { | ||
| console.warn("Could not fetch ChatGPT accounts for quota:", err); |
There was a problem hiding this comment.
This adds an extra network round-trip (/backend-api/accounts/check/v4) on every Codex usage fetch. Since the dashboard refreshes periodically, this can noticeably increase latency and load (and may be rate-limited). Consider caching the resolved accountId for a short TTL (similar to the Antigravity subscription cache in this file) keyed by a truncated access token, and only refetching when the cache expires or the usage call indicates the wrong workspace.
| async function getCodexUsage(accessToken) { | |
| try { | |
| let accountId = null; | |
| try { | |
| const accountsRes = await fetch("https://chatgpt.com/backend-api/accounts/check/v4", { | |
| method: "GET", | |
| headers: { | |
| Authorization: `Bearer ${accessToken}`, | |
| Accept: "application/json", | |
| }, | |
| }); | |
| if (accountsRes.ok) { | |
| const accountsData = await accountsRes.json(); | |
| if (accountsData.accounts) { | |
| const accountsArray = Object.values(accountsData.accounts) as any[]; | |
| const targetWorkspace = | |
| accountsArray.find((a) => a.account?.plan_type === "biz") || | |
| accountsArray.find((a) => a.account?.plan_type !== "free") || | |
| accountsArray.find((a) => a.is_default) || | |
| accountsArray[0]; | |
| if (targetWorkspace && targetWorkspace.account?.id) { | |
| accountId = targetWorkspace.account.id; | |
| } | |
| } | |
| } | |
| } catch (err) { | |
| console.warn("Could not fetch ChatGPT accounts for quota:", err); | |
| // Short-lived cache for Codex account IDs, keyed by truncated access token | |
| type CodexAccountCacheEntry = { | |
| accountId: string; | |
| expiresAt: number; | |
| }; | |
| const CODEX_ACCOUNT_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes | |
| const codexAccountCache = new Map<string, CodexAccountCacheEntry>(); | |
| function getCodexAccountCacheKey(accessToken: string): string | null { | |
| if (typeof accessToken !== "string" || accessToken.length === 0) { | |
| return null; | |
| } | |
| // Use a truncated token to avoid storing the full secret as a key | |
| return accessToken.slice(0, 16); | |
| } | |
| async function getCodexUsage(accessToken) { | |
| try { | |
| const cacheKey = getCodexAccountCacheKey(accessToken); | |
| let accountId: string | null = null; | |
| // Try to use cached accountId if available and not expired | |
| if (cacheKey) { | |
| const cached = codexAccountCache.get(cacheKey); | |
| if (cached && cached.expiresAt > Date.now()) { | |
| accountId = cached.accountId; | |
| } | |
| } | |
| // Only fetch accounts if we don't have a valid cached accountId | |
| if (!accountId) { | |
| try { | |
| const accountsRes = await fetch("https://chatgpt.com/backend-api/accounts/check/v4", { | |
| method: "GET", | |
| headers: { | |
| Authorization: `Bearer ${accessToken}`, | |
| Accept: "application/json", | |
| }, | |
| }); | |
| if (accountsRes.ok) { | |
| const accountsData = await accountsRes.json(); | |
| if (accountsData.accounts) { | |
| const accountsArray = Object.values(accountsData.accounts) as any[]; | |
| const targetWorkspace = | |
| accountsArray.find((a) => a.account?.plan_type === "biz") || | |
| accountsArray.find((a) => a.account?.plan_type !== "free") || | |
| accountsArray.find((a) => a.is_default) || | |
| accountsArray[0]; | |
| if (targetWorkspace && targetWorkspace.account?.id) { | |
| accountId = targetWorkspace.account.id; | |
| if (cacheKey) { | |
| codexAccountCache.set(cacheKey, { | |
| accountId, | |
| expiresAt: Date.now() + CODEX_ACCOUNT_CACHE_TTL_MS, | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| } catch (err) { | |
| console.warn("Could not fetch ChatGPT accounts for quota:", err); | |
| } |
Fixes #103, Fixes #101