feat: add ChatGPT Pro OAuth as LLM provider - #476
Merged
Conversation
Adds OAuth 2.0 (Authorization Code + PKCE) flow so users can authenticate with their ChatGPT Pro subscription to power BrowserOS's agent, matching the pattern used by Codex CLI, OpenCode, and Pi. Server: - OAuth token lifecycle (PKCE, exchange, refresh, SQLite storage) - Dedicated callback server on port 1455 (Codex client ID registration) - Codex fetch wrapper routing API calls to chatgpt.com/backend-api - Config resolution + provider factories for all code paths (chat, test, refine) Extension: - ChatGPT Pro template card with OAuth flow trigger - Status polling hook + auto-create provider on auth success - Model list with Codex-supported models (gpt-5.x-codex family)
Contributor
Greptile SummaryThis PR adds ChatGPT Pro (OpenAI Codex) as an OAuth-based LLM provider, wiring together a full PKCE authorization flow: a new local Bun callback server on port 1455, SQLite-backed token storage with mutex-protected refresh, a custom Key concerns:
Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant UI as AISettingsPage (Extension)
participant AgentServer as Agent Server (Hono)
participant CallbackServer as OAuth Callback Server (port 1455)
participant OpenAI as OpenAI Auth (auth.openai.com)
participant TokenStore as SQLite (oauth_tokens)
UI->>AgentServer: GET /oauth/chatgpt-pro/start?redirect=...
AgentServer->>AgentServer: Generate PKCE verifier + challenge + state
AgentServer->>AgentServer: Store PendingOAuthFlow in memory
AgentServer-->>UI: 302 Redirect to OpenAI auth URL
UI->>OpenAI: User authenticates in new tab
OpenAI-->>CallbackServer: GET /auth/callback?code=...&state=...
CallbackServer->>AgentServer: tokenManager.handleCallback(code, state)
AgentServer->>OpenAI: POST /oauth/token (code + verifier)
OpenAI-->>AgentServer: access_token + refresh_token
AgentServer->>AgentServer: parseAccessTokenClaims (accountId, email)
AgentServer->>TokenStore: upsertTokens(browseros_id, chatgpt-pro, tokens)
AgentServer-->>CallbackServer: StoredOAuthTokens
CallbackServer-->>UI: HTML success page
UI->>AgentServer: GET /oauth/chatgpt-pro/status (polling every 2s)
AgentServer->>TokenStore: getStatus(browseros_id, chatgpt-pro)
TokenStore-->>AgentServer: {authenticated: true, email}
AgentServer-->>UI: {authenticated: true, email}
UI->>UI: saveProvider({type: chatgpt-pro, ...})
Note over UI,TokenStore: On LLM request
UI->>AgentServer: POST /chat (provider: chatgpt-pro)
AgentServer->>TokenStore: refreshIfExpired(chatgpt-pro)
TokenStore-->>AgentServer: valid access_token
AgentServer->>OpenAI: POST chatgpt.com/backend-api/codex/responses
OpenAI-->>AgentServer: streamed response
AgentServer-->>UI: streamed response
Prompt To Fix All With AIThis is a comment left during a code review.
Path: packages/browseros-agent/packages/shared/src/constants/timeouts.ts
Line: 1686-1687
Comment:
**Dead constants never consumed**
`OAUTH_POLL_INTERVAL` and `OAUTH_POLL_TIMEOUT` were added here, but `useOAuthStatus.ts` hardcodes the same values (`2_000` and `300_000`) directly instead of importing these constants. The shared values are effectively dead code.
```suggestion
OAUTH_POLL_INTERVAL: 2_000,
OAUTH_POLL_TIMEOUT: 300_000,
```
These constants should be imported and used in `useOAuthStatus.ts`:
```ts
import { TIMEOUTS } from '@browseros/shared/constants/timeouts'
// ...
}, TIMEOUTS.OAUTH_POLL_INTERVAL)
pollTimeoutRef.current = setTimeout(stopPolling, TIMEOUTS.OAUTH_POLL_TIMEOUT)
```
Otherwise remove them from `timeouts.ts` to avoid confusion.
**Rule Used:** Remove unused/dead code rather than leaving it in ... ([source](https://app.greptile.com/review/custom-context?memory=9b045db4-2630-428c-95b7-ccf048d34547))
**Learnt From**
[browseros-ai/BrowserOS-agent#126](https://github.com/browseros-ai/BrowserOS-agent/pull/126)
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/lib/clients/oauth/providers.ts
Line: 1227
Comment:
**Reusing a third-party client ID**
`'app_EMoamEEZ73f0CkXaXp7hrann'` is the client ID registered by the Codex CLI, not by BrowserOS. The `callback-server.ts` comment even acknowledges this: _"matching the Codex CLI client ID registration."_
Relying on another application's OAuth registration creates real fragility:
- OpenAI can revoke or rotate this client ID at any time, breaking BrowserOS's entire ChatGPT Pro integration with no warning.
- It may violate OpenAI's developer Terms of Service, which typically prohibit impersonating another registered application.
- Rate limits and usage quotas applied to the Codex CLI will be shared with all BrowserOS users.
BrowserOS should register its own OAuth application with OpenAI and use its own client ID here.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/lib/db/schema.ts
Line: 1648
Comment:
**`NOT NULL` constraint inconsistent with empty-string fallback**
The column is declared `NOT NULL`, but `token-manager.ts` stores `data.refresh_token ?? ''` when the authorization server omits `refresh_token`. This means the "absent" state is represented by an empty string rather than `NULL`, which makes the constraint semantically misleading and could silently obscure missing tokens in direct DB queries.
The `executeRefresh` method correctly handles the empty-string case (deletes the row and throws), but the schema itself should reflect the real semantics:
```suggestion
refresh_token TEXT,
```
And update `StoredOAuthTokens.refreshToken` to `refreshToken?: string` accordingly. The `executeRefresh` guard (`if (!tokens.refreshToken)`) already handles both `''` and `undefined`/`null` correctly, so no other logic changes would be needed.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/agent/provider-factory.ts
Line: 574-583
Comment:
**Duplicated ChatGPT Pro model-creation logic**
`createChatGPTProFactory` here and `createChatGPTProModel` in `lib/clients/llm/provider.ts` (lines 879–886) are byte-for-byte identical in logic: both check `config.apiKey`, call `createOpenAI({ apiKey, fetch: createCodexFetch(config.accountId) }).responses`, and throw the same error message. If the Codex endpoint URL or auth flow changes, both files must be updated in sync.
Consider extracting a shared `buildChatGPTProClient(apiKey, accountId)` helper into `oauth/codex-fetch.ts` or a new `chatgpt-pro.ts` utility, then calling it from both factories.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "fix: address Greptil..." |
- Wire OAuth callback server stop handle into onShutdown (P1: port 1455 leak) - Guard against missing refresh token + clear stale tokens on failed refresh (P1) - Add logger.warn to silent catch in codex-fetch body mutation - Document JWT trust assumption in parseAccessTokenClaims - Source model ID from provider template instead of hard-coding
- Revert OAuthHandle interface — callback server port releases on process exit - Remove stopCallbackServer from shutdown flow (dead code) - Remove all useCallback from useOAuthStatus per CLAUDE.md guidance
Contributor
Author
|
@greptile-ai review |
- Pass providerOptions { openai: { store: false } } to ToolLoopAgent
so the AI SDK inlines content instead of using item_reference
- Strip item IDs and previous_response_id in codex-fetch (safety net)
- Use .responses() model (Codex only speaks Responses API format)
- Strip temperature, max_tokens, top_p from Codex requests (unsupported) - Add all available Codex models including gpt-5.4, gpt-5.2, gpt-5.1
- Add reasoningEffort (none/low/medium/high) and reasoningSummary (auto/concise/detailed) dropdowns in the Edit Provider dialog - Pass through extension → chat request → agent config → providerOptions - Defaults: effort=high, summary=auto
Contributor
Author
|
@greptile-ai review |
- Fix default model fallback: gpt-4o → gpt-5.3-codex (Codex endpoint) - Clear stale tokens on refresh failure (prevents infinite retry loop) - Only auto-create provider after explicit OAuth flow, not on page load - Add catch block to auto-create effect with error toast
Contributor
Author
|
@greptile-ai review |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.