From cbf1b387ddd71dee4af3f4897d13016835e32230 Mon Sep 17 00:00:00 2001 From: James Brunton Date: Tue, 16 Dec 2025 18:02:13 +0000 Subject: [PATCH] commit 2 which is probs further from working than 1 --- .../src/main/resources/static/index.html | 71 +++++++++++++++++ .../UserLicenseSettingsServiceTest.java | 3 +- frontend/src-tauri/capabilities/main.json | 19 +++++ frontend/src-tauri/tauri.conf.json | 5 +- .../SetupWizard/SelfHostedLoginScreen.tsx | 77 ++++++++++++------- 5 files changed, 146 insertions(+), 29 deletions(-) create mode 100644 app/proprietary/src/main/resources/static/index.html create mode 100644 frontend/src-tauri/capabilities/main.json diff --git a/app/proprietary/src/main/resources/static/index.html b/app/proprietary/src/main/resources/static/index.html new file mode 100644 index 0000000000..0dc414a279 --- /dev/null +++ b/app/proprietary/src/main/resources/static/index.html @@ -0,0 +1,71 @@ + + + + + Stirling PDF - Authentication + + + +
Completing authentication...
+ + + diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java index a7f8042f6f..3f0214573f 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java @@ -51,7 +51,8 @@ class UserLicenseSettingsServiceTest { when(applicationProperties.getPremium()).thenReturn(premium); when(applicationProperties.getAutomaticallyGenerated()).thenReturn(automaticallyGenerated); - when(automaticallyGenerated.getIsNewServer()).thenReturn(false); // Default: not a new server + when(automaticallyGenerated.getIsNewServer()) + .thenReturn(false); // Default: not a new server when(settingsRepository.findSettings()).thenReturn(Optional.of(mockSettings)); when(userService.getTotalUsersCount()).thenReturn(80L); when(settingsRepository.save(any(UserLicenseSettings.class))) diff --git a/frontend/src-tauri/capabilities/main.json b/frontend/src-tauri/capabilities/main.json new file mode 100644 index 0000000000..b1ac19e44d --- /dev/null +++ b/frontend/src-tauri/capabilities/main.json @@ -0,0 +1,19 @@ +{ + "identifier": "main", + "description": "Main window capability with remote access for self-hosted SSO.", + "windows": ["main"], + "permissions": [ + "core:default", + "http:default", + "http:allow-fetch", + "http:allow-fetch-send", + "http:allow-fetch-read-body", + "opener:default", + "opener:allow-open-url", + "core:webview:allow-internal-toggle-devtools", + "core:webview:allow-create-webview-window" + ], + "remote": { + "urls": ["http://localhost:8080/*"] + } +} diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index b10fec0e54..3f8bc1a7c3 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -18,7 +18,10 @@ "resizable": true, "fullscreen": false } - ] + ], + "security": { + "capabilities": ["main"] + } }, "bundle": { "active": true, diff --git a/frontend/src/desktop/components/SetupWizard/SelfHostedLoginScreen.tsx b/frontend/src/desktop/components/SetupWizard/SelfHostedLoginScreen.tsx index 70a44d9090..e1d3f75b35 100644 --- a/frontend/src/desktop/components/SetupWizard/SelfHostedLoginScreen.tsx +++ b/frontend/src/desktop/components/SetupWizard/SelfHostedLoginScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Text } from '@mantine/core'; import LoginHeader from '@app/routes/login/LoginHeader'; @@ -51,10 +51,54 @@ export const SelfHostedLoginScreen: React.FC = ({ setValidationError(errorMessage); }; - const waitForSsoCompletion = (popup: Window): Promise => { + const pollForSession = (timeoutMs = 60000): Promise => { + return new Promise((resolve, reject) => { + const start = Date.now(); + const interval = window.setInterval(async () => { + if (Date.now() - start > timeoutMs) { + window.clearInterval(interval); + reject(new Error('Timed out waiting for authentication')); + return; + } + + try { + const hasToken = localStorage.getItem('stirling_jwt'); + if (hasToken) { + window.clearInterval(interval); + resolve(); + return; + } + + const isAuthed = await authService.isAuthenticated(); + if (isAuthed) { + window.clearInterval(interval); + resolve(); + } + } catch (e) { + // Ignore transient errors and keep polling + } + }, 1000); + }); + }; + + const waitForSsoCompletion = (popup: Window, expectedOrigin?: string): Promise => { + // Accept messages from the backend origin (e.g., http://localhost:8080) since the wizard runs under the Tauri origin. + const allowedOrigin = (() => { + try { + return expectedOrigin ? new URL(expectedOrigin).origin : null; + } catch (_) { + return null; + } + })(); + return new Promise((resolve, reject) => { const messageHandler = (event: MessageEvent) => { - if (event.origin !== window.location.origin || typeof event.data !== 'object' || event.data === null) { + if (typeof event.data !== 'object' || event.data === null) { + return; + } + + // If we can compute the backend origin, ensure the message comes from there. + if (allowedOrigin && event.origin !== allowedOrigin) { return; } @@ -95,30 +139,9 @@ export const SelfHostedLoginScreen: React.FC = ({ // Mark SSO flow so the callback can short-circuit verification localStorage.setItem('desktop_sso_in_progress', JSON.stringify({ mode: 'selfhosted' })); - const popup = window.open(provider.url, '_blank', 'width=480,height=720'); - if (!popup) { - localStorage.removeItem('desktop_sso_in_progress'); - handleOAuthError(t('setup.login.error.oauthFailed', 'OAuth login failed. Please try again.')); - return; - } - - try { - const token = await waitForSsoCompletion(popup); - localStorage.removeItem('desktop_sso_in_progress'); - - await authService.applyExternalToken(token); - await onOAuthSuccess({ username: provider.label || provider.id }); - } catch (err) { - localStorage.removeItem('desktop_sso_in_progress'); - const message = err instanceof Error ? err.message : t('setup.login.error.oauthFailed', 'OAuth login failed. Please try again.'); - handleOAuthError(message); - } finally { - try { - popup.close(); - } catch (_) { - // ignore - } - } + console.debug('[Desktop SSO] Launching provider (same-window)', provider); + // Navigate the current webview to the provider URL; the callback page will store the token and reload the app. + window.location.assign(provider.url); }; const displayError = error || validationError;