fix: recognize Codex credential-pool auth (#617)

This commit is contained in:
Zhicheng Han
2026-05-11 15:36:43 +02:00
committed by GitHub
parent 7907bbbf61
commit 5e608ea338
3 changed files with 181 additions and 18 deletions
@@ -33,6 +33,13 @@ function cleanupExpiredSessions() {
// --- Auth file helpers ---
interface AuthJson { version?: number; active_provider?: string; providers?: Record<string, any>; credential_pool?: Record<string, any[]>; updated_at?: string }
interface CodexCredentialRef {
accessToken: string
refreshToken?: string
lastRefresh?: string
provider?: any
poolEntry?: any
}
function loadAuthJson(authPath: string): AuthJson {
try { return JSON.parse(readFileSync(authPath, 'utf-8')) as AuthJson } catch { return { version: 1 } }
@@ -63,6 +70,35 @@ function decodeJwtExp(token: string): number | null {
} catch { return null }
}
function getCodexCredential(auth: AuthJson): CodexCredentialRef | null {
const provider = auth.providers?.['openai-codex']
const providerTokens = provider?.tokens
const providerAccessToken = providerTokens?.access_token || provider?.access_token
const pool = auth.credential_pool?.['openai-codex']
const poolEntry = Array.isArray(pool) ? pool.find(entry => entry?.access_token) : undefined
if (providerAccessToken) {
return {
accessToken: providerAccessToken,
refreshToken: providerTokens?.refresh_token || provider?.refresh_token,
lastRefresh: provider.last_refresh,
provider,
poolEntry,
}
}
if (poolEntry?.access_token) {
return {
accessToken: poolEntry.access_token,
refreshToken: poolEntry.refresh_token,
lastRefresh: poolEntry.last_refresh,
poolEntry,
}
}
return null
}
// --- Background login worker ---
async function codexLoginWorker(session: CodexSession, authPath: string): Promise<void> {
const startTime = Date.now()
@@ -145,32 +181,42 @@ export async function status(ctx: any) {
try {
const authPath = getActiveAuthPath()
const auth = loadAuthJson(authPath)
const tokens = auth.providers?.['openai-codex']?.tokens
if (!tokens?.access_token || !auth.providers) { ctx.body = { authenticated: false }; return }
const codexProvider = auth.providers['openai-codex']!
const exp = decodeJwtExp(tokens.access_token)
const credential = getCodexCredential(auth)
if (!credential) { ctx.body = { authenticated: false }; return }
const exp = decodeJwtExp(credential.accessToken)
if (exp && exp <= Date.now() / 1000 + 120) {
if (tokens.refresh_token) {
if (credential.refreshToken) {
try {
const refreshRes = await fetch(CODEX_OAUTH_TOKEN_URL, {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ grant_type: 'refresh_token', refresh_token: tokens.refresh_token, client_id: CODEX_CLIENT_ID }).toString(),
body: new URLSearchParams({ grant_type: 'refresh_token', refresh_token: credential.refreshToken, client_id: CODEX_CLIENT_ID }).toString(),
signal: AbortSignal.timeout(15000),
})
if (refreshRes.ok) {
const newTokens = await refreshRes.json() as { access_token: string; refresh_token?: string }
codexProvider.tokens.access_token = newTokens.access_token
if (newTokens.refresh_token) { codexProvider.tokens.refresh_token = newTokens.refresh_token }
codexProvider.last_refresh = new Date().toISOString()
const lastRefresh = new Date().toISOString()
if (credential.provider?.tokens) {
credential.provider.tokens.access_token = newTokens.access_token
if (newTokens.refresh_token) { credential.provider.tokens.refresh_token = newTokens.refresh_token }
credential.provider.last_refresh = lastRefresh
} else if (credential.provider) {
credential.provider.access_token = newTokens.access_token
if (newTokens.refresh_token) { credential.provider.refresh_token = newTokens.refresh_token }
credential.provider.last_refresh = lastRefresh
}
if (credential.poolEntry) {
credential.poolEntry.access_token = newTokens.access_token
if (newTokens.refresh_token) { credential.poolEntry.refresh_token = newTokens.refresh_token }
credential.poolEntry.last_refresh = lastRefresh
}
saveAuthJson(authPath, auth)
saveCodexCliTokens(newTokens.access_token, newTokens.refresh_token || tokens.refresh_token)
if (auth.credential_pool?.['openai-codex']?.[0]) { auth.credential_pool['openai-codex'][0].access_token = newTokens.access_token; saveAuthJson(authPath, auth) }
ctx.body = { authenticated: true, last_refresh: codexProvider.last_refresh }; return
saveCodexCliTokens(newTokens.access_token, newTokens.refresh_token || credential.refreshToken)
ctx.body = { authenticated: true, last_refresh: lastRefresh }; return
}
} catch { }
}
ctx.body = { authenticated: false }; return
}
ctx.body = { authenticated: true, last_refresh: codexProvider.last_refresh }
ctx.body = { authenticated: true, last_refresh: credential.lastRefresh }
} catch { ctx.body = { authenticated: false } }
}
@@ -132,12 +132,14 @@ export async function getAvailable(ctx: any) {
if (!existsSync(authPath)) return false
const auth = JSON.parse(readFileSync(authPath, 'utf-8'))
const provider = auth.providers?.[providerKey]
if (!provider) return false
// Codex: providers.openai-codex.tokens.access_token
// Nous: providers.nous.access_token
const pool = auth.credential_pool?.[providerKey]
// Legacy OAuth providers are stored under providers.*; newer Hermes
// credential pools store Codex-style OAuth entries under
// credential_pool.*. Treat either shape as an authorized provider.
return !!(
provider.tokens?.access_token ||
provider.access_token
provider?.tokens?.access_token ||
provider?.access_token ||
(Array.isArray(pool) && pool.some((entry: any) => entry?.access_token))
)
} catch { return false }
}