Configuration options including port and redirect URI
Callback function called with tokens on successful authentication
Callback function called with error details on authentication failure
// 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));
}
}
Creates an HTTP server to handle the OAuth callback
Establishes a temporary HTTP server that:
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.