• Creates an HTTP server to handle the OAuth callback

    Establishes a temporary HTTP server that:

    1. Listens on the specified port for the OAuth redirect
    2. Validates incoming requests against the expected redirect path
    3. Extracts the authorization code from query parameters
    4. Exchanges the code for access and refresh tokens
    5. Provides appropriate visual feedback to the user
    6. Notifies the application of success or failure

    The server implements comprehensive error handling for various failure scenarios (missing code, API errors, user denial) and automatically shuts down once the authentication process completes.

    Parameters

    • options: CallbackHandlerOptions

      Configuration options including port and redirect URI

    • onSuccess: (tokens: AuthTokens) => void

      Callback function called with tokens on successful authentication

    • onError: (error: Error) => void

      Callback function called with error details on authentication failure

    Returns void

    // Create a callback server for authentication
    createCallbackServer(
    { port: 8888, redirectUri: 'http://localhost:8888/callback' },
    (tokens) => handleSuccessfulAuth(tokens),
    (error) => handleAuthError(error)
    );
    export function createCallbackServer(
    options: CallbackHandlerOptions,
    onSuccess: (tokens: AuthTokens) => void,
    onError: (error: Error) => void,
    ): void {
    const { port, redirectUri } = options;

    try {
    // Create HTTP server to handle the OAuth callback
    server = createServer(async (req, res) => {
    try {
    // Only handle requests to the redirect path
    const reqUrl = new URL(req.url || "", `http://localhost:${port}`);
    const redirectUrl = new URL(redirectUri);

    // Check if the request path matches our redirect path
    if (reqUrl.pathname !== redirectUrl.pathname) {
    res.writeHead(404);
    res.end("Not found");
    return;
    }

    // Check for error parameter in callback
    const error = reqUrl.searchParams.get("error");
    if (error) {
    const errorMsg = `Spotify authentication error: ${error}`;
    saveLog(errorMsg, "ERROR");

    // Send error response
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end(getErrorHtml(errorMsg));

    // Notify application
    onError(new Error(errorMsg));
    return;
    }

    // Get authorization code from query parameters
    const code = reqUrl.searchParams.get("code");
    if (!code) {
    const errorMsg = "No authorization code in callback";
    saveLog(errorMsg, "ERROR");

    // Send error response
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end(getErrorHtml(errorMsg));

    // Notify application
    onError(new Error(errorMsg));
    return;
    }

    // Exchange authorization code for tokens
    const spotifyTokens = await spotifyApi.exchangeCodeForTokens(
    code,
    redirectUri,
    );

    // Convert Spotify token format to our internal format
    const tokens: AuthTokens = {
    accessToken: spotifyTokens.access_token,
    refreshToken: spotifyTokens.refresh_token,
    expiresIn: spotifyTokens.expires_in,
    };

    // Send success response
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end(getSuccessHtml());

    // Notify application of successful authentication
    onSuccess(tokens);

    // Shutdown server since we're done
    shutdownServer();
    } catch (err) {
    const errorMsg = `Error handling authentication callback: ${err}`;
    saveLog(errorMsg, "ERROR");

    // Send error response
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end(getErrorHtml(errorMsg));

    // Notify application
    onError(new Error(errorMsg));
    }
    });

    // Handle server errors
    server.on("error", (err) => {
    const errorMsg = `OAuth callback server error: ${err}`;
    saveLog(errorMsg, "ERROR");
    onError(new Error(errorMsg));
    });

    // Start listening
    server.listen(port, () => {
    saveLog(`OAuth callback server listening on port ${port}`, "DEBUG");
    });
    } catch (err) {
    const errorMsg = `Failed to create callback server: ${err}`;
    saveLog(errorMsg, "ERROR");
    onError(new Error(errorMsg));
    }
    }