-
Notifications
You must be signed in to change notification settings - Fork 14.3k
refactor: replace Bun.serve with Node http.createServer in OAuth handlers #18327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import os from "os" | |
| import { ProviderTransform } from "@/provider/transform" | ||
| import { ModelID, ProviderID } from "@/provider/schema" | ||
| import { setTimeout as sleep } from "node:timers/promises" | ||
| import { createServer } from "http" | ||
|
|
||
| const log = Log.create({ service: "plugin.codex" }) | ||
|
|
||
|
|
@@ -241,85 +242,91 @@ interface PendingOAuth { | |
| reject: (error: Error) => void | ||
| } | ||
|
|
||
| let oauthServer: ReturnType<typeof Bun.serve> | undefined | ||
| let oauthServer: ReturnType<typeof createServer> | undefined | ||
| let pendingOAuth: PendingOAuth | undefined | ||
|
|
||
| async function startOAuthServer(): Promise<{ port: number; redirectUri: string }> { | ||
| if (oauthServer) { | ||
| return { port: OAUTH_PORT, redirectUri: `http://localhost:${OAUTH_PORT}/auth/callback` } | ||
| } | ||
|
|
||
| oauthServer = Bun.serve({ | ||
| port: OAUTH_PORT, | ||
| fetch(req) { | ||
| const url = new URL(req.url) | ||
| oauthServer = createServer((req, res) => { | ||
| const url = new URL(req.url || "/", `http://localhost:${OAUTH_PORT}`) | ||
|
|
||
| if (url.pathname === "/auth/callback") { | ||
| const code = url.searchParams.get("code") | ||
| const state = url.searchParams.get("state") | ||
| const error = url.searchParams.get("error") | ||
| const errorDescription = url.searchParams.get("error_description") | ||
| if (url.pathname === "/auth/callback") { | ||
| const code = url.searchParams.get("code") | ||
| const state = url.searchParams.get("state") | ||
| const error = url.searchParams.get("error") | ||
| const errorDescription = url.searchParams.get("error_description") | ||
|
|
||
| if (error) { | ||
| const errorMsg = errorDescription || error | ||
| pendingOAuth?.reject(new Error(errorMsg)) | ||
| pendingOAuth = undefined | ||
| return new Response(HTML_ERROR(errorMsg), { | ||
| headers: { "Content-Type": "text/html" }, | ||
| }) | ||
| } | ||
|
|
||
| if (!code) { | ||
| const errorMsg = "Missing authorization code" | ||
| pendingOAuth?.reject(new Error(errorMsg)) | ||
| pendingOAuth = undefined | ||
| return new Response(HTML_ERROR(errorMsg), { | ||
| status: 400, | ||
| headers: { "Content-Type": "text/html" }, | ||
| }) | ||
| } | ||
|
|
||
| if (!pendingOAuth || state !== pendingOAuth.state) { | ||
| const errorMsg = "Invalid state - potential CSRF attack" | ||
| pendingOAuth?.reject(new Error(errorMsg)) | ||
| pendingOAuth = undefined | ||
| return new Response(HTML_ERROR(errorMsg), { | ||
| status: 400, | ||
| headers: { "Content-Type": "text/html" }, | ||
| }) | ||
| } | ||
|
|
||
| const current = pendingOAuth | ||
| if (error) { | ||
| const errorMsg = errorDescription || error | ||
| pendingOAuth?.reject(new Error(errorMsg)) | ||
| pendingOAuth = undefined | ||
| res.writeHead(200, { "Content-Type": "text/html" }) | ||
| res.end(HTML_ERROR(errorMsg)) | ||
| return | ||
| } | ||
|
|
||
| exchangeCodeForTokens(code, `http://localhost:${OAUTH_PORT}/auth/callback`, current.pkce) | ||
| .then((tokens) => current.resolve(tokens)) | ||
| .catch((err) => current.reject(err)) | ||
|
|
||
| return new Response(HTML_SUCCESS, { | ||
| headers: { "Content-Type": "text/html" }, | ||
| }) | ||
| if (!code) { | ||
| const errorMsg = "Missing authorization code" | ||
| pendingOAuth?.reject(new Error(errorMsg)) | ||
| pendingOAuth = undefined | ||
| res.writeHead(400, { "Content-Type": "text/html" }) | ||
| res.end(HTML_ERROR(errorMsg)) | ||
| return | ||
| } | ||
|
|
||
| if (url.pathname === "/cancel") { | ||
| pendingOAuth?.reject(new Error("Login cancelled")) | ||
| if (!pendingOAuth || state !== pendingOAuth.state) { | ||
| const errorMsg = "Invalid state - potential CSRF attack" | ||
| pendingOAuth?.reject(new Error(errorMsg)) | ||
| pendingOAuth = undefined | ||
| return new Response("Login cancelled", { status: 200 }) | ||
| res.writeHead(400, { "Content-Type": "text/html" }) | ||
| res.end(HTML_ERROR(errorMsg)) | ||
| return | ||
| } | ||
|
|
||
| return new Response("Not found", { status: 404 }) | ||
| }, | ||
| const current = pendingOAuth | ||
| pendingOAuth = undefined | ||
|
|
||
| exchangeCodeForTokens(code, `http://localhost:${OAUTH_PORT}/auth/callback`, current.pkce) | ||
| .then((tokens) => current.resolve(tokens)) | ||
| .catch((err) => current.reject(err)) | ||
|
|
||
| res.writeHead(200, { "Content-Type": "text/html" }) | ||
| res.end(HTML_SUCCESS) | ||
| return | ||
| } | ||
|
|
||
| if (url.pathname === "/cancel") { | ||
| pendingOAuth?.reject(new Error("Login cancelled")) | ||
| pendingOAuth = undefined | ||
| res.writeHead(200) | ||
| res.end("Login cancelled") | ||
| return | ||
| } | ||
|
|
||
| res.writeHead(404) | ||
| res.end("Not found") | ||
| }) | ||
|
|
||
| await new Promise<void>((resolve, reject) => { | ||
| oauthServer!.listen(OAUTH_PORT, () => { | ||
| log.info("codex oauth server started", { port: OAUTH_PORT }) | ||
| resolve() | ||
| }) | ||
| oauthServer!.on("error", reject) | ||
| }) | ||
|
|
||
| log.info("codex oauth server started", { port: OAUTH_PORT }) | ||
| return { port: OAUTH_PORT, redirectUri: `http://localhost:${OAUTH_PORT}/auth/callback` } | ||
| } | ||
|
|
||
| function stopOAuthServer() { | ||
| if (oauthServer) { | ||
| oauthServer.stop() | ||
| oauthServer.close(() => { | ||
| log.info("codex oauth server stopped") | ||
| }) | ||
| oauthServer = undefined | ||
| log.info("codex oauth server stopped") | ||
| } | ||
| } | ||
|
Comment on lines
324
to
331
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
By contrast, await new Promise<void>((resolve) => server!.close(() => resolve()))
server = undefined
function stopOAuthServer() {
if (oauthServer) {
const closing = oauthServer
oauthServer = undefined
closing.close(() => {
log.info("codex oauth server stopped")
})
}
}Note: even the snippet above doesn't fully close the port before returning. If true synchronous-style safety is needed, the function should be made |
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import("http").IncomingMessageandimport("http").ServerResponseare used as inline type expressions, buthttpis already imported at line 2. The types can be imported alongsidecreateServerto keep the signature readable.Change the top-level import to:
Then the function signature becomes:
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!