From f3cf747cfee94062f7c1d7d50ddcdcf1ab046b79 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:57:43 +0000 Subject: [PATCH] possible login fixes (#5444) # Description of Changes Disable TLS checks and various cert checks to allow all sorts of selfhost machines to be connected via tauri app Version bump Crop tool correctly shows ghostscript as optional so its not disabled on java only installations --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --- .../SPDF/config/EndpointConfiguration.java | 2 + .../configuration/SecurityConfiguration.java | 25 +- build.gradle | 2 +- frontend/src-tauri/Cargo.lock | 217 ++++++++---------- frontend/src-tauri/Cargo.toml | 2 +- frontend/src-tauri/src/commands/auth.rs | 103 ++++++++- frontend/src-tauri/tauri.conf.json | 5 +- .../testing/serverExperienceSimulations.ts | 2 +- .../desktop/services/connectionModeService.ts | 20 +- .../src/desktop/services/tauriHttpClient.ts | 18 +- .../testing/serverExperienceSimulations.ts | 2 +- 11 files changed, 246 insertions(+), 152 deletions(-) diff --git a/app/common/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/app/common/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index c752485f0..bce6e1272 100644 --- a/app/common/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/app/common/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -516,6 +516,8 @@ public class EndpointConfiguration { addEndpointAlternative("compress-pdf", "qpdf"); addEndpointAlternative("compress-pdf", "Ghostscript"); addEndpointAlternative("compress-pdf", "Java"); + addEndpointAlternative("crop", "Ghostscript"); + addEndpointAlternative("crop", "Java"); addEndpointAlternative("ocr-pdf", "tesseract"); addEndpointAlternative("ocr-pdf", "OCRmyPDF"); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java index 8b61d9c3e..3f03dcbaf 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java @@ -208,21 +208,20 @@ public class SecurityConfiguration { } else { String xFrameOption = securityProperties.getXFrameOptions(); if (xFrameOption != null) { - http.headers(headers -> { - if ("DISABLED".equalsIgnoreCase(xFrameOption)) { - headers.frameOptions(frameOptions -> frameOptions.disable()); - } else if ("SAMEORIGIN".equalsIgnoreCase(xFrameOption)) { - headers.frameOptions(frameOptions -> frameOptions.sameOrigin()); - } else { - // Default to DENY - headers.frameOptions(frameOptions -> frameOptions.deny()); - } - }); + http.headers( + headers -> { + if ("DISABLED".equalsIgnoreCase(xFrameOption)) { + headers.frameOptions(frameOptions -> frameOptions.disable()); + } else if ("SAMEORIGIN".equalsIgnoreCase(xFrameOption)) { + headers.frameOptions(frameOptions -> frameOptions.sameOrigin()); + } else { + // Default to DENY + headers.frameOptions(frameOptions -> frameOptions.deny()); + } + }); } else { // If not configured, use default DENY - http.headers(headers -> - headers.frameOptions(frameOptions -> frameOptions.deny()) - ); + http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.deny())); } } diff --git a/build.gradle b/build.gradle index 2366c6a92..950705d4f 100644 --- a/build.gradle +++ b/build.gradle @@ -67,7 +67,7 @@ springBoot { allprojects { group = 'stirling.software' - version = '2.4.0' + version = '2.4.1' configurations.configureEach { exclude group: 'commons-logging', module: 'commons-logging' diff --git a/frontend/src-tauri/Cargo.lock b/frontend/src-tauri/Cargo.lock index d60b8f0b7..bda073963 100644 --- a/frontend/src-tauri/Cargo.lock +++ b/frontend/src-tauri/Cargo.lock @@ -689,7 +689,7 @@ dependencies = [ "bitflags 2.10.0", "core-foundation 0.10.1", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "libc", ] @@ -1168,15 +1168,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -1184,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -1198,12 +1189,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1832,6 +1817,20 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.7" @@ -1841,25 +1840,12 @@ dependencies = [ "http 1.4.0", "hyper 1.8.1", "hyper-util", - "rustls", + "rustls 0.23.36", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.32", - "native-tls", - "tokio", - "tokio-native-tls", + "webpki-roots 1.0.5", ] [[package]] @@ -1890,9 +1876,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2415,23 +2401,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework 2.11.1", - "security-framework-sys", - "tempfile", -] - [[package]] name = "ndk" version = "0.9.0" @@ -2750,50 +2719,12 @@ dependencies = [ "pathdiff", ] -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags 2.10.0", - "cfg-if", - "foreign-types 0.3.2", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-sys" -version = "0.9.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -3027,7 +2958,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.1", + "siphasher 1.0.2", ] [[package]] @@ -3254,7 +3185,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls", + "rustls 0.23.36", "socket2 0.6.2", "thiserror 2.0.18", "tokio", @@ -3274,7 +3205,7 @@ dependencies = [ "rand 0.9.2", "ring", "rustc-hash", - "rustls", + "rustls 0.23.36", "rustls-pki-types", "slab", "thiserror 2.0.18", @@ -3527,15 +3458,16 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", - "hyper-tls", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", "mime", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls 0.21.12", + "rustls-native-certs", "rustls-pemfile", "serde", "serde_json", @@ -3543,12 +3475,13 @@ dependencies = [ "sync_wrapper 0.1.2", "system-configuration 0.5.1", "tokio", - "tokio-native-tls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.25.4", "winreg 0.50.0", ] @@ -3570,7 +3503,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.8.1", - "hyper-rustls", + "hyper-rustls 0.27.7", "hyper-util", "js-sys", "log", @@ -3578,14 +3511,14 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls", + "rustls 0.23.36", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tokio-util", "tower", "tower-http", @@ -3595,7 +3528,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 1.0.5", ] [[package]] @@ -3695,6 +3628,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.23.36" @@ -3704,11 +3649,23 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.9", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework 2.11.1", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -3728,6 +3685,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.103.9" @@ -3826,6 +3793,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "seahash" version = "4.1.0" @@ -4145,9 +4122,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" @@ -5046,12 +5023,12 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "tokio-rustls" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "native-tls", + "rustls 0.21.12", "tokio", ] @@ -5061,7 +5038,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls", + "rustls 0.23.36", "tokio", ] @@ -5427,12 +5404,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.2.1" @@ -5641,6 +5612,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webpki-roots" version = "1.0.5" @@ -6483,18 +6460,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" dependencies = [ "proc-macro2", "quote", @@ -6563,9 +6540,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" +checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" [[package]] name = "zvariant" diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index 87c00bc8d..a415d879f 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -35,7 +35,7 @@ tauri-plugin-opener = "2.5.3" tauri-plugin-deep-link = "2.4.6" keyring = { version = "3.6.1", features = ["apple-native", "windows-native"] } tokio = { version = "1.0", features = ["time", "sync"] } -reqwest = { version = "0.11", features = ["json"] } +reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls", "rustls-tls-native-roots"] } tiny_http = "0.12" url = "2.5" urlencoding = "2.1" diff --git a/frontend/src-tauri/src/commands/auth.rs b/frontend/src-tauri/src/commands/auth.rs index 64be3ab17..43fa9128c 100644 --- a/frontend/src-tauri/src/commands/auth.rs +++ b/frontend/src-tauri/src/commands/auth.rs @@ -213,8 +213,20 @@ pub async fn login( // Detect if this is Supabase (SaaS) or Spring Boot (self-hosted) let is_supabase = server_url.trim_end_matches('/') == saas_server_url.trim_end_matches('/'); - // Create HTTP client - let client = reqwest::Client::new(); + // Create HTTP client with certificate bypass + // This handles: + // - Self-signed certificates + // - Missing intermediate certificates + // - Certificate hostname mismatches + // Note: Rustls only supports TLS 1.2 and TLS 1.3 + let client = reqwest::Client::builder() + .danger_accept_invalid_certs(true) + .timeout(std::time::Duration::from_secs(30)) + .build() + .map_err(|e| { + log::error!("Failed to create HTTP client: {}", e); + format!("Failed to create HTTP client: {}", e) + })?; if is_supabase { // Supabase authentication flow @@ -235,7 +247,24 @@ pub async fn login( .json(&request_body) .send() .await - .map_err(|e| format!("Network error: {}", e))?; + .map_err(|e| { + let error_msg = e.to_string(); + let error_lower = error_msg.to_lowercase(); + log::error!("Supabase login network error: {}", e); + + // Detect TLS version mismatch + if error_lower.contains("peer is incompatible") || + error_lower.contains("protocol version") || + error_lower.contains("peerincompatible") || + (error_lower.contains("handshake") && (error_lower.contains("tls") || error_lower.contains("ssl"))) { + format!( + "TLS version not supported: The Supabase server appears to require an unsupported TLS version. \ + Please contact support. Technical details: {}", e + ) + } else { + format!("Network error connecting to Supabase: {}", e) + } + })?; let status = response.status(); @@ -296,7 +325,39 @@ pub async fn login( .json(&payload) .send() .await - .map_err(|e| format!("Network error: {}", e))?; + .map_err(|e| { + let error_msg = e.to_string(); + let error_lower = error_msg.to_lowercase(); + log::error!("Spring Boot login network error: {}", e); + + // Detect TLS version mismatch (server using TLS 1.0/1.1) + if error_lower.contains("peer is incompatible") || + error_lower.contains("protocol version") || + error_lower.contains("peerincompatible") || + (error_lower.contains("handshake") && (error_lower.contains("tls") || error_lower.contains("ssl"))) { + format!( + "TLS version not supported: The server appears to be using TLS 1.0 or TLS 1.1, which are not supported by this desktop app. \ + Please upgrade your server to use TLS 1.2 or higher, or use the web version of Stirling-PDF instead. \ + Technical details: {}", e + ) + // Other TLS/SSL errors (certificate issues) + } else if error_lower.contains("tls") || error_lower.contains("ssl") || + error_lower.contains("certificate") || error_lower.contains("decrypt") { + format!( + "TLS/SSL connection error: This usually means the server has certificate issues. \ + The desktop app accepts self-signed certificates, so this might be a TLS version issue. \ + Technical details: {}", e + ) + } else if error_lower.contains("connection refused") { + format!("Connection refused: Server is not reachable at {}. Check if the server is running and the URL is correct.", login_url) + } else if error_lower.contains("timeout") { + format!("Connection timeout: Server at {} is not responding. Check your network connection.", login_url) + } else if error_lower.contains("dns") || error_lower.contains("resolve") { + format!("DNS resolution failed: Cannot resolve hostname. Check if the server URL is correct.") + } else { + format!("Network error: {}", e) + } + })?; let status = response.status(); log::debug!("Spring Boot login response status: {}", status); @@ -505,7 +566,20 @@ async fn exchange_code_for_token( ) -> Result { log::info!("Exchanging authorization code for access token with PKCE"); - let client = reqwest::Client::new(); + // Create HTTP client with certificate bypass + // This handles: + // - Self-signed certificates + // - Missing intermediate certificates + // - Certificate hostname mismatches + // Note: Rustls only supports TLS 1.2 and TLS 1.3 + let client = reqwest::Client::builder() + .danger_accept_invalid_certs(true) + .timeout(std::time::Duration::from_secs(30)) + .build() + .map_err(|e| { + log::error!("Failed to create HTTP client: {}", e); + format!("Failed to create HTTP client: {}", e) + })?; // grant_type goes in query string, not body! let token_url = format!("{}/auth/v1/token?grant_type=pkce", auth_server_url.trim_end_matches('/')); @@ -526,7 +600,24 @@ async fn exchange_code_for_token( .json(&body) .send() .await - .map_err(|e| format!("Failed to exchange code for token: {}", e))?; + .map_err(|e| { + let error_msg = e.to_string(); + let error_lower = error_msg.to_lowercase(); + log::error!("OAuth token exchange network error: {}", e); + + // Detect TLS version mismatch + if error_lower.contains("peer is incompatible") || + error_lower.contains("protocol version") || + error_lower.contains("peerincompatible") || + (error_lower.contains("handshake") && (error_lower.contains("tls") || error_lower.contains("ssl"))) { + format!( + "TLS version not supported: The authentication server appears to require an unsupported TLS version. \ + Please contact support. Technical details: {}", e + ) + } else { + format!("Failed to exchange code for token: {}", e) + } + })?; let status = response.status(); if !status.is_success() { diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index f22d2d44f..41c446709 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "Stirling-PDF", - "version": "2.4.0", + "version": "2.4.1", "identifier": "stirling.pdf.dev", "build": { "frontendDist": "../dist", @@ -16,7 +16,8 @@ "width": 1280, "height": 800, "resizable": true, - "fullscreen": false + "fullscreen": false, + "additionalBrowserArgs": "--enable-features=CertVerifierBuiltinFeature" } ] }, diff --git a/frontend/src/core/testing/serverExperienceSimulations.ts b/frontend/src/core/testing/serverExperienceSimulations.ts index 121c14fd1..a0948ba28 100644 --- a/frontend/src/core/testing/serverExperienceSimulations.ts +++ b/frontend/src/core/testing/serverExperienceSimulations.ts @@ -38,7 +38,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: '2.4.0', + appVersion: '2.4.1', serverCertificateEnabled: false, enableAlphaFunctionality: false, serverPort: 8080, diff --git a/frontend/src/desktop/services/connectionModeService.ts b/frontend/src/desktop/services/connectionModeService.ts index c11b27c2a..5f5101bd4 100644 --- a/frontend/src/desktop/services/connectionModeService.ts +++ b/frontend/src/desktop/services/connectionModeService.ts @@ -251,11 +251,15 @@ export class ConnectionModeService { diagnostics.push(stage2Result); if (stage2Result.success) { - console.log(`[ConnectionModeService] ⚠️ Certificate issue detected - works without validation`); + console.log(`[ConnectionModeService] ⚠️ Certificate issue detected - but connection works with bypass enabled`); + console.log(`[ConnectionModeService] ==================== DIAGNOSTIC SUMMARY ====================`); + console.log(`[ConnectionModeService] ✅ CONNECTION SUCCESSFUL (with certificate bypass)`); + console.log(`[ConnectionModeService] Protocol: HTTPS with certificate validation disabled`); + console.log(`[ConnectionModeService] Duration: ${stage2Result.duration}ms`); + console.log(`[ConnectionModeService] Note: Server has missing intermediate certificate or invalid cert`); + console.log(`[ConnectionModeService] ==================== DIAGNOSTIC SESSION END ====================`); return { - success: false, - error: 'SSL certificate validation failed. The server has an invalid, self-signed, or untrusted certificate.', - errorCode: 'SSL_CERTIFICATE_INVALID', + success: true, diagnostics, }; } @@ -487,7 +491,13 @@ export class ConnectionModeService { let detailedMessage = `Failed: ${errorMsg}`; - if (errorLower.includes('timeout') || errorLower.includes('timed out')) { + // Check for TLS version mismatch (TLS 1.0/1.1 not supported) + if (errorLower.includes('peer is incompatible') || + errorLower.includes('protocol version') || + errorLower.includes('peerincompatible') || + (errorLower.includes('handshake') && (errorLower.includes('tls') || errorLower.includes('ssl')))) { + detailedMessage = `TLS version not supported - Server appears to use TLS 1.0 or 1.1 (desktop app requires TLS 1.2+). Please upgrade your server's TLS configuration or use the web version.`; + } else if (errorLower.includes('timeout') || errorLower.includes('timed out')) { detailedMessage = `Timeout after ${duration}ms - server not responding`; } else if (errorLower.includes('certificate') || errorLower.includes('cert') || errorLower.includes('ssl') || errorLower.includes('tls')) { detailedMessage = `SSL/TLS error - ${errorMsg}`; diff --git a/frontend/src/desktop/services/tauriHttpClient.ts b/frontend/src/desktop/services/tauriHttpClient.ts index f83d46d1f..6fcec36bb 100644 --- a/frontend/src/desktop/services/tauriHttpClient.ts +++ b/frontend/src/desktop/services/tauriHttpClient.ts @@ -193,12 +193,26 @@ class TauriHttpClient { const credentials: RequestCredentials = finalConfig.withCredentials ? 'include' : 'omit'; // Make the request using Tauri's native HTTP client (standard Fetch API) - const response = await fetch(url, { + // Enable certificate bypass for HTTPS to handle missing intermediate certs and self-signed certs + const fetchOptions: any = { method, headers, body, credentials, - }); + }; + + // Always enable dangerous settings for HTTPS to allow connections to servers with: + // - Missing intermediate certificates + // - Self-signed certificates + // - Certificate hostname mismatches + if (url.startsWith('https://')) { + fetchOptions.danger = { + acceptInvalidCerts: true, + acceptInvalidHostnames: true, + }; + } + + const response = await fetch(url, fetchOptions); // Parse response based on responseType let data: T; diff --git a/frontend/src/proprietary/testing/serverExperienceSimulations.ts b/frontend/src/proprietary/testing/serverExperienceSimulations.ts index 1c3b5a6ad..056a4aa09 100644 --- a/frontend/src/proprietary/testing/serverExperienceSimulations.ts +++ b/frontend/src/proprietary/testing/serverExperienceSimulations.ts @@ -48,7 +48,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: '2.4.0', + appVersion: '2.4.1', serverCertificateEnabled: false, enableAlphaFunctionality: false, enableDesktopInstallSlide: true,