1- import * as http from "http" ;
2- import { URL } from "url" ;
3-
41/**
52 * OAuth callback result
63 */
@@ -13,7 +10,7 @@ export interface CallbackResult {
1310 * Callback server structure
1411 */
1512export interface CallbackServer {
16- server : http . Server ;
13+ server : { stop : ( ) => void } ;
1714 promise : Promise < CallbackResult > ;
1815 getPort : ( ) => number ;
1916}
@@ -34,7 +31,7 @@ function escapeHtml(str: string | null): string {
3431}
3532
3633/**
37- * Create and start callback server, returning server object and promise
34+ * Create and start callback server using Bun.serve
3835 * @param port - Port to listen on (0 for random available port)
3936 * @param expectedState - Expected state parameter for CSRF protection
4037 * @param timeoutMs - Timeout in milliseconds
@@ -46,42 +43,42 @@ export function createCallbackServer(
4643 timeoutMs : number = 300000
4744) : CallbackServer {
4845 let resolved = false ;
49- let server : http . Server | null = null ;
5046 let actualPort = port ;
5147 let resolveCallback : ( value : CallbackResult ) => void ;
5248 let rejectCallback : ( reason : Error ) => void ;
53-
49+ let serverInstance : ReturnType < typeof Bun . serve > | null = null ;
50+
5451 const promise = new Promise < CallbackResult > ( ( resolve , reject ) => {
5552 resolveCallback = resolve ;
5653 rejectCallback = reject ;
5754 } ) ;
58-
55+
5956 // Timeout handler
6057 const timeout = setTimeout ( ( ) => {
6158 if ( ! resolved ) {
6259 resolved = true ;
63- if ( server ) {
64- server . close ( ) ;
60+ if ( serverInstance ) {
61+ serverInstance . stop ( ) ;
6562 }
6663 rejectCallback ( new Error ( "Authentication timeout. Please try again." ) ) ;
6764 }
6865 } , timeoutMs ) ;
69-
70- // Request handler
71- const requestHandler = ( req : http . IncomingMessage , res : http . ServerResponse ) : void => {
72- if ( resolved ) {
73- return ;
74- }
7566
76- // Only handle /callback path
77- if ( ! req . url || ! req . url . startsWith ( "/callback" ) ) {
78- res . writeHead ( 404 , { "Content-Type" : "text/plain" } ) ;
79- res . end ( "Not Found" ) ;
80- return ;
81- }
67+ serverInstance = Bun . serve ( {
68+ port : port ,
69+ hostname : "127.0.0.1" ,
70+ fetch ( req ) {
71+ if ( resolved ) {
72+ return new Response ( "Already handled" , { status : 200 } ) ;
73+ }
74+
75+ const url = new URL ( req . url ) ;
76+
77+ // Only handle /callback path
78+ if ( ! url . pathname . startsWith ( "/callback" ) ) {
79+ return new Response ( "Not Found" , { status : 404 } ) ;
80+ }
8281
83- try {
84- const url = new URL ( req . url , `http://localhost:${ actualPort } ` ) ;
8582 const code = url . searchParams . get ( "code" ) ;
8683 const state = url . searchParams . get ( "state" ) ;
8784 const error = url . searchParams . get ( "error" ) ;
@@ -91,9 +88,11 @@ export function createCallbackServer(
9188 if ( error ) {
9289 resolved = true ;
9390 clearTimeout ( timeout ) ;
94-
95- res . writeHead ( 400 , { "Content-Type" : "text/html" } ) ;
96- res . end ( `
91+
92+ setTimeout ( ( ) => serverInstance ?. stop ( ) , 100 ) ;
93+ rejectCallback ( new Error ( `OAuth error: ${ error } ${ errorDescription ? ` - ${ errorDescription } ` : "" } ` ) ) ;
94+
95+ return new Response ( `
9796<!DOCTYPE html>
9897<html>
9998<head>
@@ -114,19 +113,12 @@ export function createCallbackServer(
114113 </div>
115114</body>
116115</html>
117- ` ) ;
118-
119- if ( server ) {
120- server . close ( ) ;
121- }
122- rejectCallback ( new Error ( `OAuth error: ${ error } ${ errorDescription ? ` - ${ errorDescription } ` : "" } ` ) ) ;
123- return ;
116+ ` , { status : 400 , headers : { "Content-Type" : "text/html" } } ) ;
124117 }
125118
126119 // Validate required parameters
127120 if ( ! code || ! state ) {
128- res . writeHead ( 400 , { "Content-Type" : "text/html" } ) ;
129- res . end ( `
121+ return new Response ( `
130122<!DOCTYPE html>
131123<html>
132124<head>
@@ -145,17 +137,18 @@ export function createCallbackServer(
145137 </div>
146138</body>
147139</html>
148- ` ) ;
149- return ;
140+ ` , { status : 400 , headers : { "Content-Type" : "text/html" } } ) ;
150141 }
151142
152143 // Validate state (CSRF protection)
153144 if ( expectedState && state !== expectedState ) {
154145 resolved = true ;
155146 clearTimeout ( timeout ) ;
156-
157- res . writeHead ( 400 , { "Content-Type" : "text/html" } ) ;
158- res . end ( `
147+
148+ setTimeout ( ( ) => serverInstance ?. stop ( ) , 100 ) ;
149+ rejectCallback ( new Error ( "State mismatch (possible CSRF attack)" ) ) ;
150+
151+ return new Response ( `
159152<!DOCTYPE html>
160153<html>
161154<head>
@@ -174,21 +167,17 @@ export function createCallbackServer(
174167 </div>
175168</body>
176169</html>
177- ` ) ;
178-
179- if ( server ) {
180- server . close ( ) ;
181- }
182- rejectCallback ( new Error ( "State mismatch (possible CSRF attack)" ) ) ;
183- return ;
170+ ` , { status : 400 , headers : { "Content-Type" : "text/html" } } ) ;
184171 }
185172
186173 // Success!
187174 resolved = true ;
188175 clearTimeout ( timeout ) ;
189-
190- res . writeHead ( 200 , { "Content-Type" : "text/html" } ) ;
191- res . end ( `
176+
177+ setTimeout ( ( ) => serverInstance ?. stop ( ) , 100 ) ;
178+ resolveCallback ( { code, state } ) ;
179+
180+ return new Response ( `
192181<!DOCTYPE html>
193182<html>
194183<head>
@@ -207,61 +196,24 @@ export function createCallbackServer(
207196 </div>
208197</body>
209198</html>
210- ` ) ;
211-
212- if ( server ) {
213- server . close ( ) ;
214- }
215- resolveCallback ( { code, state } ) ;
216- } catch ( err ) {
217- if ( ! resolved ) {
218- resolved = true ;
219- clearTimeout ( timeout ) ;
220- res . writeHead ( 500 , { "Content-Type" : "text/plain" } ) ;
221- res . end ( "Internal Server Error" ) ;
222- if ( server ) {
223- server . close ( ) ;
224- }
225- rejectCallback ( err instanceof Error ? err : new Error ( String ( err ) ) ) ;
226- }
227- }
228- } ;
229-
230- // Create server
231- server = http . createServer ( requestHandler ) ;
232-
233- server . on ( "error" , ( err : Error ) => {
234- if ( ! resolved ) {
235- resolved = true ;
236- clearTimeout ( timeout ) ;
237- rejectCallback ( err ) ;
238- }
199+ ` , { status : 200 , headers : { "Content-Type" : "text/html" } } ) ;
200+ } ,
239201 } ) ;
240202
241- server . listen ( port , "127.0.0.1" , ( ) => {
242- const address = server ?. address ( ) ;
243- if ( address && typeof address === "object" ) {
244- actualPort = address . port ;
245- }
246- } ) ;
247-
203+ actualPort = serverInstance . port ;
204+
248205 return {
249- server,
206+ server : { stop : ( ) => serverInstance ?. stop ( ) } ,
250207 promise,
251- getPort : ( ) => {
252- const address = server ?. address ( ) ;
253- return address && typeof address === "object" ? address . port : 0 ;
254- } ,
208+ getPort : ( ) => actualPort ,
255209 } ;
256210}
257211
258212/**
259213 * Get the actual port the server is listening on
260- * @param server - HTTP server instance
214+ * @param server - Bun server instance
261215 * @returns Port number
262216 */
263- export function getServerPort ( server : http . Server ) : number {
264- const address = server . address ( ) ;
265- return address && typeof address === "object" ? address . port : 0 ;
217+ export function getServerPort ( server : ReturnType < typeof Bun . serve > ) : number {
218+ return server . port ;
266219}
267-
0 commit comments