feat(auth): implement port 51121 with OS fallback
Add port fallback logic to OAuth callback server: - Try port 51121 (ANTIGRAVITY_CALLBACK_PORT) first - Fallback to OS-assigned port on EADDRINUSE - Add redirectUri property to CallbackServerHandle - Return actual bound port in handle.port Add comprehensive port handling tests (5 new tests): - Should prefer port 51121 - Should return actual bound port - Should fallback when port occupied - Should cleanup and release port on close - Should provide redirect URI with actual port All 16 tests passing (11 existing + 5 new). 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
parent
534142da12
commit
e27bceb97e
@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"
|
||||
import { buildAuthURL, exchangeCode } from "./oauth"
|
||||
import { ANTIGRAVITY_CLIENT_ID, GOOGLE_TOKEN_URL } from "./constants"
|
||||
import { buildAuthURL, exchangeCode, startCallbackServer } from "./oauth"
|
||||
import { ANTIGRAVITY_CLIENT_ID, GOOGLE_TOKEN_URL, ANTIGRAVITY_CALLBACK_PORT } from "./constants"
|
||||
|
||||
describe("OAuth PKCE Removal", () => {
|
||||
describe("buildAuthURL", () => {
|
||||
@ -188,4 +188,75 @@ describe("OAuth PKCE Removal", () => {
|
||||
expect(result1.state).not.toBe(result2.state)
|
||||
})
|
||||
})
|
||||
|
||||
describe("startCallbackServer Port Handling", () => {
|
||||
it("should prefer port 51121", () => {
|
||||
// #given
|
||||
// Port 51121 should be free
|
||||
|
||||
// #when
|
||||
const handle = startCallbackServer()
|
||||
|
||||
// #then
|
||||
// If 51121 is available, should use it
|
||||
// If not available, should use valid fallback
|
||||
expect(handle.port).toBeGreaterThan(0)
|
||||
expect(handle.port).toBeLessThan(65536)
|
||||
handle.close()
|
||||
})
|
||||
|
||||
it("should return actual bound port", () => {
|
||||
// #when
|
||||
const handle = startCallbackServer()
|
||||
|
||||
// #then
|
||||
expect(typeof handle.port).toBe("number")
|
||||
expect(handle.port).toBeGreaterThan(0)
|
||||
handle.close()
|
||||
})
|
||||
|
||||
it("should fallback to OS-assigned port if 51121 is occupied (EADDRINUSE)", async () => {
|
||||
// #given - Occupy port 51121 first
|
||||
const blocker = Bun.serve({
|
||||
port: ANTIGRAVITY_CALLBACK_PORT,
|
||||
fetch: () => new Response("blocked")
|
||||
})
|
||||
|
||||
try {
|
||||
// #when
|
||||
const handle = startCallbackServer()
|
||||
|
||||
// #then
|
||||
expect(handle.port).not.toBe(ANTIGRAVITY_CALLBACK_PORT)
|
||||
expect(handle.port).toBeGreaterThan(0)
|
||||
handle.close()
|
||||
} finally {
|
||||
// Cleanup blocker
|
||||
blocker.stop()
|
||||
}
|
||||
})
|
||||
|
||||
it("should cleanup server on close", () => {
|
||||
// #given
|
||||
const handle = startCallbackServer()
|
||||
const port = handle.port
|
||||
|
||||
// #when
|
||||
handle.close()
|
||||
|
||||
// #then - port should be released (can bind again)
|
||||
const testServer = Bun.serve({ port, fetch: () => new Response("test") })
|
||||
expect(testServer.port).toBe(port)
|
||||
testServer.stop()
|
||||
})
|
||||
|
||||
it("should provide redirect URI with actual port", () => {
|
||||
// #given
|
||||
const handle = startCallbackServer()
|
||||
|
||||
// #then
|
||||
expect(handle.redirectUri).toBe(`http://localhost:${handle.port}/oauth-callback`)
|
||||
handle.close()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -148,6 +148,7 @@ export async function fetchUserInfo(
|
||||
|
||||
export interface CallbackServerHandle {
|
||||
port: number
|
||||
redirectUri: string
|
||||
waitForCallback: () => Promise<CallbackResult>
|
||||
close: () => void
|
||||
}
|
||||
@ -171,43 +172,53 @@ export function startCallbackServer(
|
||||
}
|
||||
}
|
||||
|
||||
server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(request: Request): Response {
|
||||
const url = new URL(request.url)
|
||||
const fetchHandler = (request: Request): Response => {
|
||||
const url = new URL(request.url)
|
||||
|
||||
if (url.pathname === "/oauth-callback") {
|
||||
const code = url.searchParams.get("code") || ""
|
||||
const state = url.searchParams.get("state") || ""
|
||||
const error = url.searchParams.get("error") || undefined
|
||||
if (url.pathname === "/oauth-callback") {
|
||||
const code = url.searchParams.get("code") || ""
|
||||
const state = url.searchParams.get("state") || ""
|
||||
const error = url.searchParams.get("error") || undefined
|
||||
|
||||
let responseBody: string
|
||||
if (code && !error) {
|
||||
responseBody =
|
||||
"<html><body><h1>Login successful</h1><p>You can close this window.</p></body></html>"
|
||||
} else {
|
||||
responseBody =
|
||||
"<html><body><h1>Login failed</h1><p>Please check the CLI output.</p></body></html>"
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
cleanup()
|
||||
if (resolveCallback) {
|
||||
resolveCallback({ code, state, error })
|
||||
}
|
||||
}, 100)
|
||||
|
||||
return new Response(responseBody, {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "text/html" },
|
||||
})
|
||||
let responseBody: string
|
||||
if (code && !error) {
|
||||
responseBody =
|
||||
"<html><body><h1>Login successful</h1><p>You can close this window.</p></body></html>"
|
||||
} else {
|
||||
responseBody =
|
||||
"<html><body><h1>Login failed</h1><p>Please check the CLI output.</p></body></html>"
|
||||
}
|
||||
|
||||
return new Response("Not Found", { status: 404 })
|
||||
},
|
||||
})
|
||||
setTimeout(() => {
|
||||
cleanup()
|
||||
if (resolveCallback) {
|
||||
resolveCallback({ code, state, error })
|
||||
}
|
||||
}, 100)
|
||||
|
||||
return new Response(responseBody, {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "text/html" },
|
||||
})
|
||||
}
|
||||
|
||||
return new Response("Not Found", { status: 404 })
|
||||
}
|
||||
|
||||
try {
|
||||
server = Bun.serve({
|
||||
port: ANTIGRAVITY_CALLBACK_PORT,
|
||||
fetch: fetchHandler,
|
||||
})
|
||||
} catch (error) {
|
||||
server = Bun.serve({
|
||||
port: 0,
|
||||
fetch: fetchHandler,
|
||||
})
|
||||
}
|
||||
|
||||
const actualPort = server.port as number
|
||||
const redirectUri = `http://localhost:${actualPort}/oauth-callback`
|
||||
|
||||
const waitForCallback = (): Promise<CallbackResult> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -223,6 +234,7 @@ export function startCallbackServer(
|
||||
|
||||
return {
|
||||
port: actualPort,
|
||||
redirectUri,
|
||||
waitForCallback,
|
||||
close: cleanup,
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user