Parent window that owns the authentication dialog
Spotify API client ID
Spotify API client secret
OAuth redirect URI for callback (must include protocol and port)
Whether to force account selection screen (default: false)
Promise resolving to authentication tokens or rejecting with error
// Start authentication flow from main application window
try {
const tokens = await startAuthFlow(
mainWindow,
"spotify-client-id",
"spotify-client-secret",
"http://localhost:8888/callback",
true // Force account selection
);
console.log("Authentication successful!", tokens.accessToken);
} catch (error) {
console.error("Authentication failed:", error.message);
}
export function startAuthFlow(
parentWindow: BrowserWindow,
clientId: string,
clientSecret: string,
redirectUri: string,
forceAccountSelection: boolean = false,
): Promise<AuthTokens> {
// Set credentials in the spotify API module
spotifyApi.setCredentials(clientId, clientSecret);
// Clear cookies if forcing account selection
if (forceAccountSelection) {
clearSpotifyAuthData();
}
return new Promise((resolve, reject) => {
// Save reject function for external cancellation
authPromiseReject = reject;
// Track processing state
isProcessingTokens = false;
try {
// Extract port from redirect URI for callback server
const redirectUrl = new URL(redirectUri);
const port = parseInt(redirectUrl.port) || 8888;
// Build authorization URL with proper scopes
const authScopes = [
"user-read-playback-state",
"user-modify-playback-state",
"user-library-modify",
"user-library-read",
"user-read-recently-played",
];
// Generate random state parameter
const stateParam = `${Math.random().toString(36).substring(2, 15)}_${Date.now()}`;
// Get authorization URL
const authUrl = spotifyApi.getAuthorizationUrl(
redirectUri,
authScopes,
stateParam,
);
// Set up the callback server to capture the OAuth redirect
const serverOptions: CallbackHandlerOptions = {
port,
parentWindow,
redirectUri,
};
// Create server and handle success/error
createCallbackServer(
serverOptions,
(tokens) => {
// Success handler
if (!isProcessingTokens) {
isProcessingTokens = true;
// Clean up window
closeAuthWindow();
resolve(tokens);
}
},
(error) => {
// Error handler
closeAuthWindow();
authPromiseReject = null;
reject(error);
},
);
// Create and show the authentication window
createAuthWindow(parentWindow, authUrl, () => {
// Window closed handler
if (!isProcessingTokens) {
shutdownServer();
authPromiseReject = null;
reject(new Error("Authentication canceled by user"));
}
});
} catch (error) {
// Clean up on any initialization errors
shutdownServer();
closeAuthWindow();
authPromiseReject = null;
saveLog(`Error starting authentication flow: ${error}`, "ERROR");
reject(new Error(`Failed to start authentication flow: ${error}`));
}
});
}
Starts the OAuth authentication flow
Initiates the complete Spotify OAuth 2.0 authorization code flow by:
The flow handles both successful authentication (resolving with tokens) and failure scenarios (rejecting with detailed error information). The implementation maintains state across multiple async operations, coordinating the window, server, and token exchange phases.