mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
Route OpenAI OAuth callback through desktop server
The ChatGPT Official OAuth start flow already generated the Codex /auth/callback redirect, but the desktop server only handled the older /callback/openai path. Real HTTP and UI E2E exposed the mismatch, so the server now routes the Codex callback path while retaining the legacy alias. Constraint: OpenAI Codex OAuth client code owns OPENAI_CODEX_REDIRECT_PATH. Rejected: Change the generated redirect back to /callback/openai | that would diverge from the shared OpenAI Codex OAuth client path. Confidence: high Scope-risk: narrow Tested: bun test src/server/__tests__/providers.test.ts src/server/__tests__/settings.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/proxy-transform.test.ts src/utils/__tests__/providerManagedEnvCompat.test.ts src/services/api/client.test.ts src/services/openaiAuth/fetch.test.ts src/server/__tests__/haha-openai-oauth-service.test.ts src/server/__tests__/haha-openai-oauth-api.test.ts Tested: HTTP E2E passed at /var/folders/sx/hl8zlmxd7gx9b58_1t2zhp5r0000gn/T/cc-haha-openai-oauth-e2e-4uWfok/result.json. Tested: UI E2E passed at /var/folders/sx/hl8zlmxd7gx9b58_1t2zhp5r0000gn/T/cc-haha-openai-oauth-ui-e2e-ZyQ60q/result.json.
This commit is contained in:
parent
9e45724290
commit
5b0ab7aa8a
@ -6,8 +6,11 @@ import { describe, test, expect, beforeEach, afterEach } from 'bun:test'
|
||||
import * as fs from 'fs/promises'
|
||||
import * as path from 'path'
|
||||
import * as os from 'os'
|
||||
import { createServer } from 'net'
|
||||
import { handleHahaOpenAIOAuthApi } from '../api/haha-openai-oauth.js'
|
||||
import { hahaOpenAIOAuthService } from '../services/hahaOpenAIOAuthService.js'
|
||||
import { startServer } from '../index.js'
|
||||
import { ProviderService } from '../services/providerService.js'
|
||||
|
||||
let tmpDir: string
|
||||
let originalConfigDir: string | undefined
|
||||
@ -44,6 +47,22 @@ function buildReq(
|
||||
return { req, url, segments }
|
||||
}
|
||||
|
||||
async function getFreePort(): Promise<number> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const server = createServer()
|
||||
server.on('error', reject)
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
const address = server.address()
|
||||
if (!address || typeof address === 'string') {
|
||||
server.close(() => reject(new Error('Failed to allocate test port')))
|
||||
return
|
||||
}
|
||||
const port = address.port
|
||||
server.close(() => resolve(port))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('POST /api/haha-openai-oauth/start', () => {
|
||||
beforeEach(setup)
|
||||
afterEach(teardown)
|
||||
@ -157,3 +176,24 @@ describe('DELETE /api/haha-openai-oauth', () => {
|
||||
expect(await hahaOpenAIOAuthService.loadTokens()).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /auth/callback', () => {
|
||||
beforeEach(setup)
|
||||
afterEach(teardown)
|
||||
|
||||
test('routes the OpenAI Codex redirect path to the desktop callback page', async () => {
|
||||
const port = await getFreePort()
|
||||
const originalServerPort = ProviderService.getServerPort()
|
||||
const server = startServer(port, '127.0.0.1')
|
||||
try {
|
||||
const res = await fetch(`http://127.0.0.1:${port}/auth/callback`)
|
||||
expect(res.status).toBe(200)
|
||||
const html = await res.text()
|
||||
expect(html).toContain('OpenAI Login Failed')
|
||||
expect(html).toContain('Missing code or state parameter')
|
||||
} finally {
|
||||
server.stop(true)
|
||||
ProviderService.setServerPort(originalServerPort)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
* Haha OpenAI OAuth REST API
|
||||
*
|
||||
* POST /api/haha-openai-oauth/start — 生成 PKCE+state,返回 authorize URL
|
||||
* GET /callback/openai — 用户浏览器 redirect 到此,完成 token 交换
|
||||
* GET /auth/callback — 用户浏览器 redirect 到此,完成 token 交换
|
||||
* GET /callback/openai — 兼容旧路径
|
||||
* GET /api/haha-openai-oauth — 查询当前登录状态(不回传 token 本体)
|
||||
* DELETE /api/haha-openai-oauth — 登出,删除 token 文件
|
||||
*/
|
||||
|
||||
@ -15,6 +15,7 @@ import { handleProxyRequest } from './proxy/handler.js'
|
||||
import { ProviderService } from './services/providerService.js'
|
||||
import { handleHahaOAuthCallback } from './api/haha-oauth.js'
|
||||
import { handleHahaOpenAIOAuthCallback } from './api/haha-openai-oauth.js'
|
||||
import { OPENAI_CODEX_REDIRECT_PATH } from '../services/openaiAuth/client.js'
|
||||
import { ensureDesktopCliLauncherInstalled } from './services/desktopCliLauncherService.js'
|
||||
import { enableConfigs } from '../utils/config.js'
|
||||
import { diagnosticsService } from './services/diagnosticsService.js'
|
||||
@ -267,7 +268,10 @@ export function startServer(port = PORT, host = HOST) {
|
||||
return handleHahaOAuthCallback(url)
|
||||
}
|
||||
|
||||
if (url.pathname === '/callback/openai') {
|
||||
if (
|
||||
url.pathname === OPENAI_CODEX_REDIRECT_PATH ||
|
||||
url.pathname === '/callback/openai'
|
||||
) {
|
||||
return handleHahaOpenAIOAuthCallback(url)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user