From f5d74b46644d1ac89c4d4f590dfbd830595bb236 Mon Sep 17 00:00:00 2001 From: le king fu Date: Mon, 13 Apr 2026 15:26:17 -0400 Subject: [PATCH] fix: use on_open_url for OAuth deep-link callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The listener `app.listen("deep-link://new-url", ...)` did not reliably fire when tauri-plugin-single-instance (deep-link feature) forwarded a simpl-resultat://auth/callback URL to the running instance. The user saw the browser complete the OAuth flow, the app regain focus, and then sit in "loading" forever because the listener never received the URL. Switch to the canonical Tauri v2 API — `app.deep_link().on_open_url()` via DeepLinkExt — which is directly coupled to the deep-link plugin and catches URLs from both initial launch and single-instance forwards. Also surface OAuth error responses: if the callback URL contains an `error` parameter instead of a `code`, emit `auth-callback-error` so the UI can show the error instead of staying stuck in "loading". Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.fr.md | 6 ++++ CHANGELOG.md | 6 ++++ package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/lib.rs | 69 +++++++++++++++++++++++++++------------ src-tauri/tauri.conf.json | 2 +- 7 files changed, 65 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md index f29076a..b0df60d 100644 --- a/CHANGELOG.fr.md +++ b/CHANGELOG.fr.md @@ -2,6 +2,12 @@ ## [Non publié] +## [0.7.3] - 2026-04-13 + +### Corrigé +- Connexion Compte Maximus : le callback deep-link utilise maintenant l'API canonique Tauri v2 `on_open_url`, donc le code d'autorisation parvient bien à l'app en cours d'exécution au lieu de laisser l'interface bloquée en « chargement » (#51, #65) +- Les callbacks OAuth contenant un paramètre `error` remontent maintenant l'erreur à l'interface au lieu d'être ignorés silencieusement (#51) + ## [0.7.2] - 2026-04-13 ### Modifié diff --git a/CHANGELOG.md b/CHANGELOG.md index 19e94ca..02a946d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] +## [0.7.3] - 2026-04-13 + +### Fixed +- Maximus Account sign-in: the deep-link callback now uses the canonical Tauri v2 `on_open_url` API, so the auth code is properly received by the running app instead of leaving the UI stuck in "loading" (#51, #65) +- OAuth callbacks containing an `error` parameter now surface the error to the UI instead of being silently ignored (#51) + ## [0.7.2] - 2026-04-13 ### Changed diff --git a/package.json b/package.json index f607f84..b109012 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "simpl_result_scaffold", "private": true, - "version": "0.7.2", + "version": "0.7.3", "license": "GPL-3.0-only", "type": "module", "scripts": { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 518f14c..c8b4e79 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4280,7 +4280,7 @@ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simpl-result" -version = "0.7.2" +version = "0.7.3" dependencies = [ "aes-gcm", "argon2", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index f14e219..885eb7e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "simpl-result" -version = "0.7.2" +version = "0.7.3" description = "Personal finance management app" license = "GPL-3.0-only" authors = ["you"] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2d7846f..b0bad42 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2,7 +2,8 @@ mod commands; mod database; use std::sync::Mutex; -use tauri::{Emitter, Listener, Manager}; +use tauri::{Emitter, Manager}; +use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_sql::{Migration, MigrationKind}; #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -103,26 +104,40 @@ pub fn run() { #[cfg(desktop)] app.handle().plugin(tauri_plugin_updater::Builder::new().build())?; - // Listen for deep-link events (simpl-resultat://auth/callback?code=...) + // Register the custom scheme at runtime on Linux (the .desktop file + // handles it in prod, but register_all is a no-op there and required + // for AppImage/dev builds). + #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] + { + let _ = app.deep_link().register_all(); + } + + // Canonical Tauri v2 pattern: on_open_url fires for both initial-launch + // URLs and subsequent URLs forwarded by tauri-plugin-single-instance + // (with the `deep-link` feature). let handle = app.handle().clone(); - app.listen("deep-link://new-url", move |event| { - let payload = event.payload(); - // payload is a JSON-serialized array of URL strings - if let Ok(urls) = serde_json::from_str::>(payload) { - for url in urls { - if let Some(code) = extract_auth_code(&url) { - let h = handle.clone(); - tauri::async_runtime::spawn(async move { - match commands::handle_auth_callback(h.clone(), code).await { - Ok(account) => { - let _ = h.emit("auth-callback-success", &account); - } - Err(err) => { - let _ = h.emit("auth-callback-error", &err); - } + app.deep_link().on_open_url(move |event| { + for url in event.urls() { + let url_str = url.as_str(); + let h = handle.clone(); + if let Some(code) = extract_auth_code(url_str) { + tauri::async_runtime::spawn(async move { + match commands::handle_auth_callback(h.clone(), code).await { + Ok(account) => { + let _ = h.emit("auth-callback-success", &account); } - }); - } + Err(err) => { + let _ = h.emit("auth-callback-error", &err); + } + } + }); + } else { + // No `code` param — likely an OAuth error response. Surface + // it to the frontend instead of leaving the UI stuck in + // "loading" forever. + let err_msg = extract_auth_error(url_str) + .unwrap_or_else(|| "OAuth callback did not include a code".to_string()); + let _ = h.emit("auth-callback-error", &err_msg); } } }); @@ -177,6 +192,20 @@ pub fn run() { /// Extract the `code` query parameter from a deep-link callback URL. /// e.g. "simpl-resultat://auth/callback?code=abc123&state=xyz" → Some("abc123") fn extract_auth_code(url: &str) -> Option { + extract_query_param(url, "code") +} + +/// Extract an OAuth error description from a callback URL. Returns a +/// formatted string combining `error` and `error_description` when present. +fn extract_auth_error(url: &str) -> Option { + let error = extract_query_param(url, "error")?; + match extract_query_param(url, "error_description") { + Some(desc) => Some(format!("{}: {}", error, desc)), + None => Some(error), + } +} + +fn extract_query_param(url: &str, key: &str) -> Option { let url = url.trim(); if !url.starts_with("simpl-resultat://auth/callback") { return None; @@ -184,7 +213,7 @@ fn extract_auth_code(url: &str) -> Option { let query = url.split('?').nth(1)?; for pair in query.split('&') { let mut kv = pair.splitn(2, '='); - if kv.next()? == "code" { + if kv.next()? == key { return kv.next().map(|v| { urlencoding::decode(v).map(|s| s.into_owned()).unwrap_or_else(|_| v.to_string()) }); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 534c4f4..4238cf1 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Simpl Resultat", - "version": "0.7.2", + "version": "0.7.3", "identifier": "com.simpl.resultat", "build": { "beforeDevCommand": "npm run dev",