diff --git a/electron/main.cjs b/electron/main.cjs index 2ef230ae..1dfe8b24 100644 --- a/electron/main.cjs +++ b/electron/main.cjs @@ -127,6 +127,19 @@ ipcMain.handle('save-server-config', (event, config) => { } }); +// OIDC success/error handlers +ipcMain.handle('oidc-success', (event, data) => { + console.log('OIDC authentication successful:', data); + // You can add additional logic here if needed + return { success: true }; +}); + +ipcMain.handle('oidc-error', (event, data) => { + console.log('OIDC authentication error:', data); + // You can add additional logic here if needed + return { success: false, error: data.error }; +}); + ipcMain.handle('test-server-connection', async (event, serverUrl) => { try { // Use Node.js built-in fetch (available in Node 18+) or fallback to https module @@ -177,31 +190,47 @@ ipcMain.handle('test-server-connection', async (event, serverUrl) => { }; } - // Try multiple endpoints to test the connection - const testUrls = [ - `${serverUrl}/health`, - `${serverUrl}/version`, - `${serverUrl}/users/registration-allowed` - ]; + // Test the health endpoint specifically - this is required for a valid Termix server + const healthUrl = `${serverUrl}/health`; - for (const testUrl of testUrls) { - try { - const response = await fetch(testUrl, { - method: 'GET', - timeout: 5000 - }); - - if (response.ok) { - // If we get a 200 response, it's likely a valid Termix server - return { success: true, status: response.status, testedUrl: testUrl }; + try { + const response = await fetch(healthUrl, { + method: 'GET', + timeout: 5000 + }); + + if (response.ok) { + // Try to parse the response to ensure it's a valid health check + const data = await response.text(); + // A valid health check should return some JSON or text indicating the server is healthy + if (data && (data.includes('healthy') || data.includes('ok') || data.includes('status') || response.status === 200)) { + return { success: true, status: response.status, testedUrl: healthUrl }; } - } catch (urlError) { - // Continue to next URL if this one fails - continue; } + } catch (urlError) { + console.error('Health check failed:', urlError); } - return { success: false, error: 'Server is not responding or not a valid Termix server' }; + // If health check fails, try version endpoint as fallback + try { + const versionUrl = `${serverUrl}/version`; + const response = await fetch(versionUrl, { + method: 'GET', + timeout: 5000 + }); + + if (response.ok) { + const data = await response.text(); + // Check if it looks like a Termix version response + if (data && (data.includes('version') || data.includes('termix') || data.includes('1.') || response.status === 200)) { + return { success: true, status: response.status, testedUrl: versionUrl, warning: 'Health endpoint not available, but server appears to be running' }; + } + } + } catch (versionError) { + console.error('Version check failed:', versionError); + } + + return { success: false, error: 'Server is not responding or does not appear to be a valid Termix server. Please ensure the server is running and accessible.' }; } catch (error) { return { success: false, error: error.message }; } diff --git a/electron/preload.js b/electron/preload.js index 8ffd70d1..9ecd3300 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -28,7 +28,11 @@ contextBridge.exposeInMainWorld('electronAPI', { isDev: process.env.NODE_ENV === 'development', // Generic invoke method - invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args) + invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args), + + // OIDC handlers + oidcSuccess: (data) => ipcRenderer.invoke('oidc-success', data), + oidcError: (data) => ipcRenderer.invoke('oidc-error', data) }); // Also set the legacy IS_ELECTRON flag for backward compatibility diff --git a/src/backend/database/routes/users.ts b/src/backend/database/routes/users.ts index f8c92880..fcd5b342 100644 --- a/src/backend/database/routes/users.ts +++ b/src/backend/database/routes/users.ts @@ -570,8 +570,9 @@ router.get('/oidc/callback', async (req, res) => { const isElectron = userAgent.includes('Electron') || req.get('X-Electron-App') === 'true'; if (isElectron) { - // For Electron, redirect back to the same server URL (the frontend is served from there) - frontendUrl = redirectUri.replace('/users/oidc/callback', ''); + // For Electron, we need to redirect to a special endpoint that will handle the token + // and then redirect to the Electron app using a custom protocol or file URL + frontendUrl = redirectUri.replace('/users/oidc/callback', '/electron-oidc-success'); } else if (frontendUrl.includes('localhost')) { frontendUrl = 'http://localhost:5173'; } @@ -592,8 +593,8 @@ router.get('/oidc/callback', async (req, res) => { const isElectron = userAgent.includes('Electron') || req.get('X-Electron-App') === 'true'; if (isElectron) { - // For Electron, redirect back to the same server URL (the frontend is served from there) - frontendUrl = redirectUri.replace('/users/oidc/callback', ''); + // For Electron, we need to redirect to a special endpoint that will handle the error + frontendUrl = redirectUri.replace('/users/oidc/callback', '/electron-oidc-error'); } else if (frontendUrl.includes('localhost')) { frontendUrl = 'http://localhost:5173'; } @@ -605,6 +606,140 @@ router.get('/oidc/callback', async (req, res) => { } }); +// Route: Electron OIDC success handler +// GET /electron-oidc-success +router.get('/electron-oidc-success', (req, res) => { + const { success, token, error } = req.query; + + if (success === 'true' && token) { + // Return an HTML page that will communicate with the Electron app + res.send(` + + +
+You can close this window and return to the Termix application.
+Error: ${error}
+You can close this window and try again.
+Error: ${error || 'Unknown error'}
+You can close this window and try again.
+