[fractal/fractal-next] secret: Switch to libsecret



commit 60a0207ee2c0be3a4dd2e25f3918abd3c9ccb4fb
Author: Kévin Commaille <zecakeh tedomum fr>
Date:   Mon Feb 14 21:53:11 2022 +0100

    secret: Switch to libsecret

 Cargo.lock                                 | 367 ++++++-----------------------
 Cargo.toml                                 |   6 +-
 build-aux/org.gnome.FractalNext.Devel.json |   1 -
 data/resources/resources.gresource.xml     |   1 +
 data/resources/style.css                   |   3 +-
 data/resources/ui/error-page.ui            | 203 ++++++++++++++++
 data/resources/ui/window.ui                |   8 +
 meson.build                                |   2 +
 po/POTFILES.in                             |   2 +
 src/error_page.rs                          | 133 +++++++++++
 src/main.rs                                |  10 +-
 src/secret.rs                              | 312 +++++++++++++++++-------
 src/session/mod.rs                         |  53 +++--
 src/user_facing_error.rs                   |  10 -
 src/window.rs                              |  54 +++--
 15 files changed, 722 insertions(+), 443 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index bb9e7eee3..72e1413e7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,17 +23,6 @@ dependencies = [
  "generic-array",
 ]
 
-[[package]]
-name = "aes"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
-dependencies = [
- "aes-soft",
- "aesni",
- "cipher 0.2.5",
-]
-
 [[package]]
 name = "aes"
 version = "0.7.5"
@@ -41,7 +30,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
 dependencies = [
  "cfg-if 1.0.0",
- "cipher 0.3.0",
+ "cipher",
  "cpufeatures",
  "ctr",
  "opaque-debug",
@@ -54,33 +43,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
 dependencies = [
  "aead",
- "aes 0.7.5",
- "cipher 0.3.0",
+ "aes",
+ "cipher",
  "ctr",
  "ghash",
  "subtle",
 ]
 
-[[package]]
-name = "aes-soft"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
-dependencies = [
- "cipher 0.2.5",
- "opaque-debug",
-]
-
-[[package]]
-name = "aesni"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
-dependencies = [
- "cipher 0.2.5",
- "opaque-debug",
-]
-
 [[package]]
 name = "ahash"
 version = "0.7.6"
@@ -149,7 +118,7 @@ version = "0.2.0-beta-1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "d0e317c257d4733c44475b3cefd53adb2540771ba8bd4adf37eabef3bbf8298e"
 dependencies = [
- "enumflags2 0.7.3",
+ "enumflags2",
  "futures",
  "gdk4-wayland",
  "gdk4-x11",
@@ -160,10 +129,10 @@ dependencies = [
  "serde",
  "serde_repr",
  "tracing",
- "zbus 2.0.1",
- "zbus_macros 2.0.1",
- "zvariant 3.1.0",
- "zvariant_derive 3.1.0",
+ "zbus",
+ "zbus_macros",
+ "zvariant",
+ "zvariant_derive",
 ]
 
 [[package]]
@@ -396,22 +365,6 @@ dependencies = [
  "generic-array",
 ]
 
-[[package]]
-name = "block-modes"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0"
-dependencies = [
- "block-padding",
- "cipher 0.2.5",
-]
-
-[[package]]
-name = "block-padding"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
-
 [[package]]
 name = "bs58"
 version = "0.4.0"
@@ -524,7 +477,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91"
 dependencies = [
  "cfg-if 1.0.0",
- "cipher 0.3.0",
+ "cipher",
  "cpufeatures",
  "zeroize",
 ]
@@ -537,7 +490,7 @@ checksum = "3b84ed6d1d5f7aa9bdde921a5090e0ca4d934d250ea3b402a5fab3a994e28a2a"
 dependencies = [
  "aead",
  "chacha20",
- "cipher 0.3.0",
+ "cipher",
  "poly1305",
  "zeroize",
 ]
@@ -560,15 +513,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "cipher"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
-dependencies = [
- "generic-array",
-]
-
 [[package]]
 name = "cipher"
 version = "0.3.0"
@@ -727,23 +671,13 @@ dependencies = [
  "generic-array",
 ]
 
-[[package]]
-name = "crypto-mac"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
-dependencies = [
- "generic-array",
- "subtle",
-]
-
 [[package]]
 name = "ctr"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
 dependencies = [
- "cipher 0.3.0",
+ "cipher",
 ]
 
 [[package]]
@@ -863,37 +797,16 @@ dependencies = [
  "cfg-if 1.0.0",
 ]
 
-[[package]]
-name = "enumflags2"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0"
-dependencies = [
- "enumflags2_derive 0.6.4",
- "serde",
-]
-
 [[package]]
 name = "enumflags2"
 version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "a25c90b056b3f84111cf183cbeddef0d3a0bbe9a674f057e1a1533c315f24def"
 dependencies = [
- "enumflags2_derive 0.7.3",
+ "enumflags2_derive",
  "serde",
 ]
 
-[[package]]
-name = "enumflags2_derive"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
-dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.14",
- "syn 1.0.85",
-]
-
 [[package]]
 name = "enumflags2_derive"
 version = "0.7.3"
@@ -1014,6 +927,7 @@ dependencies = [
  "image",
  "indexmap",
  "libadwaita",
+ "libsecret",
  "log",
  "matrix-sdk",
  "mime",
@@ -1023,7 +937,6 @@ dependencies = [
  "qrcode",
  "rand 0.8.4",
  "regex",
- "secret-service",
  "serde",
  "serde_json",
  "sourceview5",
@@ -1429,9 +1342,9 @@ dependencies = [
 
 [[package]]
 name = "glib"
-version = "0.15.2"
+version = "0.15.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "30a3e6b2e5ef2ecfb10c1e083074a81160b8f6408fcc08195c872e9de37a361b"
+checksum = "41dcfbdb6cc6c02aee163339465d8a40d6f3f64c3a43f729a4195f0e153338b7"
 dependencies = [
  "bitflags",
  "futures-channel",
@@ -1455,7 +1368,7 @@ checksum = "54c0c7194ee4c597b334e3ce84d22d929405e94497828e1c54da762cbe8e3d93"
 dependencies = [
  "anyhow",
  "heck 0.4.0",
- "proc-macro-crate 1.1.0",
+ "proc-macro-crate",
  "proc-macro-error",
  "proc-macro2 1.0.36",
  "quote 1.0.14",
@@ -1714,9 +1627,9 @@ checksum = "da5bf7748fd4cd0b2490df8debcc911809dbcbee4ece9531b96c29a9c729de5a"
 
 [[package]]
 name = "gtk4"
-version = "0.4.4"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b5020aeb559e0431930d66d85b8f16fc453fc0863466b13b7e1e1b800cf1a47b"
+checksum = "9e841556e3fe55d8a43ada76b7b08a5f65570bbdfe3b8f72c333053b8832c626"
 dependencies = [
  "bitflags",
  "cairo-rs",
@@ -1742,7 +1655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "573db42bb64973a4d5f718b73caa7204285a1a665308a23b11723d0ee56ec305"
 dependencies = [
  "anyhow",
- "proc-macro-crate 1.1.0",
+ "proc-macro-crate",
  "proc-macro-error",
  "proc-macro2 1.0.36",
  "quote 1.0.14",
@@ -1826,26 +1739,6 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
-[[package]]
-name = "hkdf"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
-dependencies = [
- "digest 0.9.0",
- "hmac 0.10.1",
-]
-
-[[package]]
-name = "hmac"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
-dependencies = [
- "crypto-mac",
- "digest 0.9.0",
-]
-
 [[package]]
 name = "hmac"
 version = "0.12.0"
@@ -2151,6 +2044,34 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "libsecret"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "7b8d814edb0cb306ffded3f1bfe72d6689529f95f7313a718331802723a73e5a"
+dependencies = [
+ "bitflags",
+ "gio",
+ "glib",
+ "libc",
+ "libsecret-sys",
+ "once_cell",
+]
+
+[[package]]
+name = "libsecret-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "287d2a0fcd95e4d7b0ac6fc9f802691a790d7e522138713b0cacebc4e63cab91"
+dependencies = [
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pkg-config",
+ "system-deps 6.0.0",
+]
+
 [[package]]
 name = "libspa"
 version = "0.4.1"
@@ -2343,7 +2264,7 @@ dependencies = [
  "futures-channel",
  "futures-core",
  "futures-util",
- "hmac 0.12.0",
+ "hmac",
  "lru 0.7.2",
  "matrix-sdk-common",
  "matrix-sdk-crypto",
@@ -2383,7 +2304,7 @@ name = "matrix-sdk-crypto"
 version = "0.4.1"
 source = 
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=enable_ruma_unstable_pre_spec#0dd623970bb1927db3c8d187f26c42cdfaf21bcc";
 dependencies = [
- "aes 0.7.5",
+ "aes",
  "aes-gcm",
  "atomic",
  "base64",
@@ -2392,7 +2313,7 @@ dependencies = [
  "dashmap",
  "futures-util",
  "getrandom 0.2.4",
- "hmac 0.12.0",
+ "hmac",
  "matrix-qrcode",
  "matrix-sdk-common",
  "olm-rs",
@@ -2510,16 +2431,6 @@ dependencies = [
  "tempfile",
 ]
 
-[[package]]
-name = "nb-connect"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b1bb540dc6ef51cfe1916ec038ce7a620daf3a111e2502d745197cd53d6bca15"
-dependencies = [
- "libc",
- "socket2",
-]
-
 [[package]]
 name = "new_debug_unreachable"
 version = "1.0.4"
@@ -2539,19 +2450,6 @@ dependencies = [
  "void",
 ]
 
-[[package]]
-name = "nix"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
-dependencies = [
- "bitflags",
- "cc",
- "cfg-if 0.1.10",
- "libc",
- "void",
-]
-
 [[package]]
 name = "nix"
 version = "0.23.1"
@@ -2598,40 +2496,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "num"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
-dependencies = [
- "num-bigint",
- "num-complex",
- "num-integer",
- "num-iter",
- "num-rational 0.3.2",
- "num-traits",
-]
-
-[[package]]
-name = "num-bigint"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-complex"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
-dependencies = [
- "num-traits",
-]
-
 [[package]]
 name = "num-integer"
 version = "0.1.44"
@@ -2660,7 +2524,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
 dependencies = [
  "autocfg",
- "num-bigint",
  "num-integer",
  "num-traits",
 ]
@@ -2710,7 +2573,7 @@ version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "0d992b768490d7fe0d8586d9b5745f6c49f557da6d81dc982b1d167ad4edbb21"
 dependencies = [
- "proc-macro-crate 1.1.0",
+ "proc-macro-crate",
  "proc-macro2 1.0.36",
  "quote 1.0.14",
  "syn 1.0.85",
@@ -3085,15 +2948,6 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131"
 
-[[package]]
-name = "proc-macro-crate"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
-dependencies = [
- "toml",
-]
-
 [[package]]
 name = "proc-macro-crate"
 version = "1.1.0"
@@ -3429,7 +3283,7 @@ name = "ruma-api-macros"
 version = "0.18.5"
 source = 
"git+https://github.com/ruma/ruma/?rev=b9f32bc6327542d382d4eb42ec43623495c50e66#b9f32bc6327542d382d4eb42ec43623495c50e66";
 dependencies = [
- "proc-macro-crate 1.1.0",
+ "proc-macro-crate",
  "proc-macro2 1.0.36",
  "quote 1.0.14",
  "syn 1.0.85",
@@ -3493,7 +3347,7 @@ name = "ruma-events-macros"
 version = "0.24.6"
 source = 
"git+https://github.com/ruma/ruma/?rev=b9f32bc6327542d382d4eb42ec43623495c50e66#b9f32bc6327542d382d4eb42ec43623495c50e66";
 dependencies = [
- "proc-macro-crate 1.1.0",
+ "proc-macro-crate",
  "proc-macro2 1.0.36",
  "quote 1.0.14",
  "syn 1.0.85",
@@ -3567,7 +3421,7 @@ name = "ruma-serde-macros"
 version = "0.5.0"
 source = 
"git+https://github.com/ruma/ruma/?rev=b9f32bc6327542d382d4eb42ec43623495c50e66#b9f32bc6327542d382d4eb42ec43623495c50e66";
 dependencies = [
- "proc-macro-crate 1.1.0",
+ "proc-macro-crate",
  "proc-macro2 1.0.36",
  "quote 1.0.14",
  "syn 1.0.85",
@@ -3638,12 +3492,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "scoped-tls"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
-
 [[package]]
 name = "scoped_threadpool"
 version = "0.1.9"
@@ -3656,26 +3504,6 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
-[[package]]
-name = "secret-service"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "2400fb1bf2a87b303ada204946294f932ade4929477e9e2bf66d7b49a66656ec"
-dependencies = [
- "aes 0.6.0",
- "block-modes",
- "hkdf",
- "lazy_static",
- "num",
- "rand 0.8.4",
- "serde",
- "sha2 0.9.9",
- "zbus 1.9.1",
- "zbus_macros 1.9.1",
- "zvariant 2.10.0",
- "zvariant_derive 2.10.0",
-]
-
 [[package]]
 name = "security-framework"
 version = "2.5.0"
@@ -3893,11 +3721,13 @@ dependencies = [
 
 [[package]]
 name = "sourceview5"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a5c1e4191e6387d0542caf6761c9c101e4648cb588b965f8efec8a3943a1ad22"
+checksum = "ec66e5db143023da4dbe7dcb5f9d7bb50c0e5668c3ea91d40e8ba188f7f07a54"
 dependencies = [
  "bitflags",
+ "futures-channel",
+ "futures-core",
  "gdk-pixbuf",
  "gdk4",
  "gio",
@@ -3910,9 +3740,9 @@ dependencies = [
 
 [[package]]
 name = "sourceview5-sys"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "900965718bc3384c7d3f08976b35b47810ee73810b2882d30453b33ba7622337"
+checksum = "6bbf0b30cd67340b8fc695f460859dbadaec159850c260a0f766cc607bb14a86"
 dependencies = [
  "gdk-pixbuf-sys",
  "gdk4-sys",
@@ -4644,29 +4474,6 @@ dependencies = [
  "time",
 ]
 
-[[package]]
-name = "zbus"
-version = "1.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "2326acc379a3ac4e34b794089f5bdb17086bf29a5fdf619b7b4cc772dc2e9dad"
-dependencies = [
- "async-io",
- "byteorder",
- "derivative",
- "enumflags2 0.6.4",
- "fastrand",
- "futures",
- "nb-connect",
- "nix 0.17.0",
- "once_cell",
- "polling",
- "scoped-tls",
- "serde",
- "serde_repr",
- "zbus_macros 1.9.1",
- "zvariant 2.10.0",
-]
-
 [[package]]
 name = "zbus"
 version = "2.0.1"
@@ -4683,7 +4490,7 @@ dependencies = [
  "async-trait",
  "byteorder",
  "derivative",
- "enumflags2 0.7.3",
+ "enumflags2",
  "event-listener",
  "futures-core",
  "futures-sink",
@@ -4697,21 +4504,9 @@ dependencies = [
  "serde_repr",
  "sha1",
  "static_assertions",
- "zbus_macros 2.0.1",
+ "zbus_macros",
  "zbus_names",
- "zvariant 3.1.0",
-]
-
-[[package]]
-name = "zbus_macros"
-version = "1.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a482c56029e48681b89b92b5db3c446db0915e8dd1052c0328a574eda38d5f93"
-dependencies = [
- "proc-macro-crate 0.1.5",
- "proc-macro2 1.0.36",
- "quote 1.0.14",
- "syn 1.0.85",
+ "zvariant",
 ]
 
 [[package]]
@@ -4720,7 +4515,7 @@ version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "3e03af45fc15e2c977161c5ffea56c43c41f425a963affd7074bf91b5bf5a8cf"
 dependencies = [
- "proc-macro-crate 1.1.0",
+ "proc-macro-crate",
  "proc-macro2 1.0.36",
  "quote 1.0.14",
  "regex",
@@ -4735,7 +4530,7 @@ checksum = "45dfcdcf87b71dad505d30cc27b1b7b88a64b6d1c435648f48f9dbc1fdc4b7e1"
 dependencies = [
  "serde",
  "static_assertions",
- "zvariant 3.1.0",
+ "zvariant",
 ]
 
 [[package]]
@@ -4759,20 +4554,6 @@ dependencies = [
  "synstructure",
 ]
 
-[[package]]
-name = "zvariant"
-version = "2.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a68c7b55f2074489b7e8e07d2d0a6ee6b4f233867a653c664d8020ba53692525"
-dependencies = [
- "byteorder",
- "enumflags2 0.6.4",
- "libc",
- "serde",
- "static_assertions",
- "zvariant_derive 2.10.0",
-]
-
 [[package]]
 name = "zvariant"
 version = "3.1.0"
@@ -4780,23 +4561,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "bbb31b009e0b0c4f2c1283c9c23129e4f76020da4b3c4dfa032abfbfe30a2c02"
 dependencies = [
  "byteorder",
- "enumflags2 0.7.3",
+ "enumflags2",
  "libc",
  "serde",
  "static_assertions",
- "zvariant_derive 3.1.0",
-]
-
-[[package]]
-name = "zvariant_derive"
-version = "2.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e4ca5e22593eb4212382d60d26350065bf2a02c34b85bc850474a74b589a3de9"
-dependencies = [
- "proc-macro-crate 1.1.0",
- "proc-macro2 1.0.36",
- "quote 1.0.14",
- "syn 1.0.85",
+ "zvariant_derive",
 ]
 
 [[package]]
@@ -4805,7 +4574,7 @@ version = "3.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "5295bdc2688c7239423889191d730ad071f814dc36c48edf7cda23f38dd28b2a"
 dependencies = [
- "proc-macro-crate 1.1.0",
+ "proc-macro-crate",
  "proc-macro2 1.0.36",
  "quote 1.0.14",
  "syn 1.0.85",
diff --git a/Cargo.toml b/Cargo.toml
index a0068d40e..ce24394ad 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,7 +24,7 @@ serde = "1.0.130"
 serde_json = "1.0"
 tokio = { version = "1.15", features = ["rt", "rt-multi-thread", "sync"] }
 url = "2.2"
-secret-service = "2.0"
+libsecret = { version = "0.1.3", features = ["v0_19"] }
 html2pango = "0.4"
 futures = "0.3"
 rand = "0.8"
@@ -47,11 +47,11 @@ num_enum = "0.5.6"
 
 [dependencies.sourceview]
 package = "sourceview5"
-version = "0.4.0"
+version = "0.4.1"
 
 [dependencies.gtk]
 package = "gtk4"
-version = "0.4.4"
+version = "0.4.6"
 features = ["v4_6"]
 
 [dependencies.adw]
diff --git a/build-aux/org.gnome.FractalNext.Devel.json b/build-aux/org.gnome.FractalNext.Devel.json
index 0f44223b8..0ec20c56c 100644
--- a/build-aux/org.gnome.FractalNext.Devel.json
+++ b/build-aux/org.gnome.FractalNext.Devel.json
@@ -15,7 +15,6 @@
         "--share=network",
         "--share=ipc",
         "--device=dri",
-        "--talk-name=org.freedesktop.secrets",
         "--env=RUST_LOG=fractal=debug",
         "--env=G_MESSAGES_DEBUG=none",
         "--env=RUST_BACKTRACE=1"
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index fd9dd2e6b..766d01c7b 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -58,6 +58,7 @@
     <file compressed="true" preprocess="xml-stripblanks" 
alias="content-verification-info-bar.ui">ui/content-verification-info-bar.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" alias="content.ui">ui/content.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="context-menu-bin.ui">ui/context-menu-bin.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" alias="error-page.ui">ui/error-page.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" alias="event-menu.ui">ui/event-menu.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="event-source-dialog.ui">ui/event-source-dialog.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" alias="greeter.ui">ui/greeter.ui</file>
diff --git a/data/resources/style.css b/data/resources/style.css
index ba84a43c0..f6ce190a9 100644
--- a/data/resources/style.css
+++ b/data/resources/style.css
@@ -44,7 +44,8 @@ button.opaque.success {
   color: @error_bg_color;
 }
 
-preferencesgroup .body {
+preferencesgroup .body,
+.large-line-height {
   line-height: 140%;
 }
 
diff --git a/data/resources/ui/error-page.ui b/data/resources/ui/error-page.ui
new file mode 100644
index 000000000..bc1e5f818
--- /dev/null
+++ b/data/resources/ui/error-page.ui
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="ErrorPage" parent="AdwBin">
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHeaderBar">
+            <property name="title-widget">
+              <object class="AdwWindowTitle">
+                <property name="title">Error</property>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="AdwStatusPage" id="page">
+            <property name="title">Secret Service Error</property>
+            <property name="icon-name">dialog-password-symbolic</property>
+            <property name="vexpand">true</property>
+            <child>
+              <object class="AdwClamp">
+                <child>
+                  <object class="GtkStack" id="stack">
+                    <child>
+                      <object class="GtkStackPage">
+                        <property name="name">secret-error-session</property>
+                        <property name="child">
+                          <object class="GtkBox">
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">24</property>
+                            <child>
+                              <object class="GtkLabel">
+                                <style>
+                                  <class name="large-line-height"/>
+                                </style>
+                                <property name="wrap">true</property>
+                                <property name="wrap-mode">word-char</property>
+                                <property name="xalign">0.0</property>   
+                                <property name="label" translatable="yes">It seems like one of the Fractal 
sessions stored in the Secret Service is corrupted. If you know how to fix it you should do so.</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel">
+                                <style>
+                                  <class name="large-line-height"/>
+                                </style>
+                                <property name="wrap">true</property>
+                                <property name="wrap-mode">word-char</property>
+                                <property name="xalign">0.0</property>
+                                <property name="label" translatable="yes">Alternatively, we could delete 
this session for you. This means you will have to login again and you will lose access to your encrypted 
messages, unless you have a session open in another client or you have already backed up your encryption 
keys.</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkBox">
+                                <style>
+                                  <class name="warning"/>
+                                </style>
+                                <property name="halign">center</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <object class="GtkImage">
+                                    <property name="icon-name">dialog-warning-symbolic</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <style>
+                                      <class name="large-line-height"/>
+                                    </style>
+                                    <property name="wrap">true</property>
+                                    <property name="wrap-mode">word-char</property>
+                                    <property name="xalign">0.0</property>
+                                    <property name="label" translatable="yes">Clicking this button might 
delete more than one session!</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkButton">
+                                <style>
+                                  <class name="destructive-action"/>
+                                  <class name="pill"/>
+                                </style>
+                                <property name="halign">center</property>
+                                <property name="label" translatable="yes">Delete the corrupted 
session</property>
+                                <property 
name="action-name">error-page.remove-secret-error-session</property>
+                              </object>
+                            </child>
+                          </object>
+                        </property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkStackPage">
+                        <property name="name">secret-error-other</property>
+                        <property name="child">
+                          <object class="GtkBox">
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">24</property>
+                            <child>
+                              <object class="GtkLabel">
+                                <style>
+                                  <class name="large-line-height"/>
+                                </style>
+                                <property name="wrap">true</property>
+                                <property name="wrap-mode">word-char</property>
+                                <property name="xalign">0.0</property>   
+                                <property name="label" translatable="yes">Fractal relies on a Secret Service 
Provider to manage your sensitive session information and an error occurred while we where trying to store or 
get your session.</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="orientation">vertical</property>
+                                <property name="spacing">12</property>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <style>
+                                      <class name="large-line-height"/>
+                                    </style>
+                                    <property name="wrap">true</property>
+                                    <property name="wrap-mode">word-char</property>
+                                    <property name="xalign">0.0</property>
+                                    <property name="label" translatable="yes">Here are a few things that 
might help you fix issues with the Secret Service:</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkBox">
+                                    <property name="spacing">6</property>
+                                    <child>
+                                      <object class="GtkLabel">
+                                        <style>
+                                          <class name="large-line-height"/>
+                                        </style>
+                                        <property name="valign">start</property>
+                                        <property name="label">•</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel">
+                                        <style>
+                                          <class name="large-line-height"/>
+                                        </style>
+                                        <property name="wrap">true</property>
+                                        <property name="wrap-mode">word-char</property>
+                                        <property name="xalign">0.0</property>
+                                        <property name="label" translatable="yes">Make sure you have a 
Secret Service Provider installed, like gnome-keyring.</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkBox">
+                                    <property name="spacing">6</property>
+                                    <child>
+                                      <object class="GtkLabel">
+                                        <style>
+                                          <class name="large-line-height"/>
+                                        </style>
+                                        <property name="valign">start</property>
+                                        <property name="label">•</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel">
+                                        <style>
+                                          <class name="large-line-height"/>
+                                        </style>
+                                        <property name="wrap">true</property>
+                                        <property name="wrap-mode">word-char</property>
+                                        <property name="xalign">0.0</property>
+                                        <property name="label" translatable="yes">Check that you have a 
default keyring and that is is unlocked.</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel">
+                                <style>
+                                  <class name="large-line-height"/>
+                                </style>
+                                <property name="wrap">true</property>
+                                <property name="wrap-mode">word-char</property>
+                                <property name="xalign">0.0</property>   
+                                <property name="label" translatable="yes">Check the application logs and 
your distribution's documentation for more details.</property>
+                              </object>
+                            </child>
+                          </object>
+                        </property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/resources/ui/window.ui b/data/resources/ui/window.ui
index 372ffccda..851e4364e 100644
--- a/data/resources/ui/window.ui
+++ b/data/resources/ui/window.ui
@@ -57,6 +57,14 @@
                 <property name="transition-type">crossfade</property>
               </object>
             </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">error-page</property>
+                <property name="child">
+                  <object class="ErrorPage" id="error_page"/>
+                </property>
+              </object>
+            </child>
           </object>
         </child>
       </object>
diff --git a/meson.build b/meson.build
index af0a9ce4d..315311b86 100644
--- a/meson.build
+++ b/meson.build
@@ -26,6 +26,8 @@ dependency('gstreamer-1.0', version: '>= 1.18')
 dependency('gstreamer-base-1.0', version: '>= 1.18')
 dependency('gstreamer-plugins-base-1.0', version: '>= 1.18')
 dependency('gstreamer-video-1.0', version: '>= 1.18')
+dependency('libsecret-1', version: '>= 0.19',
+  default_options: ['gtk_doc=false', 'gir=false', 'vapi=false'])
 
 glib_compile_resources = find_program('glib-compile-resources', required: true)
 glib_compile_schemas = find_program('glib-compile-schemas', required: true)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5ac240a4b..84f604743 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -24,6 +24,7 @@ data/resources/ui/content-room-history.ui
 data/resources/ui/content-state-creation.ui
 data/resources/ui/content-state-tombstone.ui
 data/resources/ui/content.ui
+data/resources/ui/error-page.ui
 data/resources/ui/event-menu.ui
 data/resources/ui/event-source-dialog.ui
 data/resources/ui/greeter.ui
@@ -41,6 +42,7 @@ data/resources/ui/qr-code-scanner.ui
 # Rust files
 src/application.rs
 src/components/editable_avatar.rs
+src/error_page.rs
 src/login.rs
 src/secret.rs
 src/session/account_settings/devices_page/device_list.rs
diff --git a/src/error_page.rs b/src/error_page.rs
new file mode 100644
index 000000000..7ada187fb
--- /dev/null
+++ b/src/error_page.rs
@@ -0,0 +1,133 @@
+use adw::subclass::prelude::BinImpl;
+use gettextrs::gettext;
+use gtk::{self, glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
+use log::error;
+
+use crate::{components::Toast, secret, secret::SecretError, spawn, window::Window};
+
+pub enum ErrorSubpage {
+    SecretErrorSession,
+    SecretErrorOther,
+}
+
+impl AsRef<str> for ErrorSubpage {
+    fn as_ref(&self) -> &str {
+        match self {
+            Self::SecretErrorSession => "secret-error-session",
+            Self::SecretErrorOther => "secret-error-other",
+        }
+    }
+}
+
+mod imp {
+    use std::cell::RefCell;
+
+    use glib::subclass::InitializingObject;
+
+    use super::*;
+
+    #[derive(Debug, Default, CompositeTemplate)]
+    #[template(resource = "/org/gnome/FractalNext/error-page.ui")]
+    pub struct ErrorPage {
+        #[template_child]
+        pub page: TemplateChild<adw::StatusPage>,
+        #[template_child]
+        pub stack: TemplateChild<gtk::Stack>,
+        pub secret_error: RefCell<Option<SecretError>>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for ErrorPage {
+        const NAME: &'static str = "ErrorPage";
+        type Type = super::ErrorPage;
+        type ParentType = adw::Bin;
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+            klass.set_accessible_role(gtk::AccessibleRole::Group);
+            klass.install_action(
+                "error-page.remove-secret-error-session",
+                None,
+                |obj, _, _| {
+                    spawn!(clone!(@weak obj => async move {
+                        obj.remove_secret_error_session().await;
+                    }));
+                },
+            );
+        }
+
+        fn instance_init(obj: &InitializingObject<Self>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for ErrorPage {
+        fn constructed(&self, obj: &Self::Type) {
+            obj.action_set_enabled("error-page.remove-secret-error-session", false);
+        }
+    }
+
+    impl WidgetImpl for ErrorPage {}
+
+    impl BinImpl for ErrorPage {}
+}
+
+glib::wrapper! {
+    pub struct ErrorPage(ObjectSubclass<imp::ErrorPage>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl ErrorPage {
+    pub fn new() -> Self {
+        glib::Object::new(&[]).expect("Failed to create ErrorPage")
+    }
+
+    pub fn display_secret_error(&self, message: &str, error: SecretError) {
+        let priv_ = self.imp();
+        self.action_set_enabled(
+            "error-page.remove-secret-error-session",
+            matches!(error, SecretError::CorruptSession(_)),
+        );
+        priv_.page.set_description(Some(message));
+        priv_
+            .stack
+            .set_visible_child_name(error.error_subpage().as_ref());
+        priv_.secret_error.replace(Some(error));
+    }
+
+    async fn remove_secret_error_session(&self) {
+        if let Some(SecretError::CorruptSession((_, item))) = self.imp().secret_error.take() {
+            match secret::remove_item(&item).await {
+                Ok(_) => {
+                    self.action_set_enabled("error-page.remove-secret-error-session", false);
+                    if let Some(window) = self
+                        .root()
+                        .as_ref()
+                        .and_then(|root| root.downcast_ref::<Window>())
+                    {
+                        window.add_toast(&Toast::new(&gettext("Session removed successfully.")));
+                        window.restore_sessions().await;
+                    }
+                }
+                Err(err) => {
+                    error!("Could not remove session from secret storage: {:?}", err);
+                    if let Some(window) = self
+                        .root()
+                        .as_ref()
+                        .and_then(|root| root.downcast_ref::<Window>())
+                    {
+                        window.add_toast(&Toast::new(&gettext(
+                            "Could not remove session from secret storage",
+                        )));
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl Default for ErrorPage {
+    fn default() -> Self {
+        Self::new()
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 9dd0791fa..72577f0f6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,6 +9,7 @@ mod prelude;
 
 mod components;
 mod contrib;
+mod error_page;
 mod greeter;
 mod login;
 mod login_advanced_dialog;
@@ -24,8 +25,13 @@ use gtk::{gdk::Display, gio, IconTheme};
 use once_cell::sync::Lazy;
 
 use self::{
-    application::Application, greeter::Greeter, login::Login, session::Session,
-    user_facing_error::UserFacingError, window::Window,
+    application::Application,
+    error_page::{ErrorPage, ErrorSubpage},
+    greeter::Greeter,
+    login::Login,
+    session::Session,
+    user_facing_error::UserFacingError,
+    window::Window,
 };
 
 /// The default tokio runtime to be used for async tasks
diff --git a/src/secret.rs b/src/secret.rs
index f504676a2..5ec64dee1 100644
--- a/src/secret.rs
+++ b/src/secret.rs
@@ -1,13 +1,66 @@
-use std::{path::PathBuf, string::FromUtf8Error};
+use std::{collections::HashMap, fmt, path::PathBuf, string::FromUtf8Error};
 
 use gettextrs::gettext;
+use gtk::glib;
+use libsecret::{
+    password_clear_future, password_search_future, password_store_binary_future, prelude::*,
+    Retrievable, Schema, SchemaAttributeType, SchemaFlags, SearchFlags, Value, COLLECTION_DEFAULT,
+};
+use log::error;
 use matrix_sdk::ruma::identifiers::{DeviceId, UserId};
-use secret_service::{EncryptionType, SecretService};
 use serde::{Deserialize, Serialize};
 use serde_json::error::Error as JsonError;
 use url::Url;
 
-use crate::config::APP_ID;
+use crate::{config::APP_ID, ErrorSubpage};
+
+/// Any error that can happen when interacting with the secret service.
+#[derive(Debug, Clone)]
+pub enum SecretError {
+    CorruptSession((String, Retrievable)),
+    Libsecret(glib::Error),
+    Unknown,
+}
+
+impl SecretError {
+    /// Get the error subpage that matches `self`.
+    pub fn error_subpage(&self) -> ErrorSubpage {
+        match self {
+            Self::CorruptSession(_) => ErrorSubpage::SecretErrorSession,
+            _ => ErrorSubpage::SecretErrorOther,
+        }
+    }
+}
+
+impl From<glib::Error> for SecretError {
+    fn from(error: glib::Error) -> Self {
+        Self::Libsecret(error)
+    }
+}
+
+impl fmt::Display for SecretError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "{}",
+            match self {
+                Self::CorruptSession((message, _)) => message.to_owned(),
+                Self::Libsecret(error) if error.is::<libsecret::Error>() => {
+                    match error.kind::<libsecret::Error>() {
+                        Some(libsecret::Error::Protocol) => error.message().to_owned(),
+                        Some(libsecret::Error::IsLocked) => {
+                            gettext("Could not unlock the secret storage")
+                        }
+                        _ => gettext(
+                            "An unknown error occurred when interacting with the secret storage",
+                        ),
+                    }
+                }
+                _ => gettext("An unknown error occurred when interacting with the secret storage"),
+            }
+        )
+    }
+}
 
 #[derive(Debug, Clone)]
 pub struct StoredSession {
@@ -18,7 +71,125 @@ pub struct StoredSession {
     pub secret: Secret,
 }
 
+impl StoredSession {
+    /// Build self from a secret.
+    pub async fn try_from_secret_item(item: Retrievable) -> Result<Self, SecretError> {
+        let attr = item.attributes();
+
+        let homeserver = match attr.get("homeserver") {
+            Some(string) => match Url::parse(string) {
+                Ok(homeserver) => homeserver,
+                Err(err) => {
+                    error!(
+                        "Could not parse 'homeserver' attribute in stored session: {:?}",
+                        err
+                    );
+                    return Err(SecretError::CorruptSession((
+                        gettext("Malformed homeserver in stored session"),
+                        item,
+                    )));
+                }
+            },
+            None => {
+                return Err(SecretError::CorruptSession((
+                    gettext("Could not find homeserver in stored session"),
+                    item,
+                )));
+            }
+        };
+        let user_id = match attr.get("user") {
+            Some(string) => match UserId::parse(string.as_str()) {
+                Ok(user_id) => user_id,
+                Err(err) => {
+                    error!(
+                        "Could not parse 'user' attribute in stored session: {:?}",
+                        err
+                    );
+                    return Err(SecretError::CorruptSession((
+                        gettext("Malformed user ID in stored session"),
+                        item,
+                    )));
+                }
+            },
+            None => {
+                return Err(SecretError::CorruptSession((
+                    gettext("Could not find user ID in stored session"),
+                    item,
+                )));
+            }
+        };
+        let device_id = match attr.get("device-id") {
+            Some(string) => <&DeviceId>::from(string.as_str()).to_owned(),
+            None => {
+                return Err(SecretError::CorruptSession((
+                    gettext("Could not find device ID in stored session"),
+                    item,
+                )));
+            }
+        };
+        let path = match attr.get("db-path") {
+            Some(string) => PathBuf::from(string),
+            None => {
+                return Err(SecretError::CorruptSession((
+                    gettext("Could not find database path in stored session"),
+                    item,
+                )));
+            }
+        };
+        let secret = match item.retrieve_secret_future().await {
+            Ok(Some(value)) => match Secret::from_utf8(value.get()) {
+                Ok(secret) => secret,
+                Err(err) => {
+                    error!("Could not parse secret in stored session: {:?}", err);
+                    return Err(SecretError::CorruptSession((
+                        gettext("Malformed secret in stored session"),
+                        item,
+                    )));
+                }
+            },
+            Ok(None) => {
+                return Err(SecretError::CorruptSession((
+                    gettext("No secret in stored session"),
+                    item,
+                )));
+            }
+            Err(err) => {
+                error!("Could not get secret in stored session: {:?}", err);
+                return Err(SecretError::CorruptSession((
+                    gettext("Could not get secret in stored session"),
+                    item,
+                )));
+            }
+        };
+
+        Ok(Self {
+            homeserver,
+            user_id,
+            device_id,
+            path,
+            secret,
+        })
+    }
+
+    /// Build a secret from `self`.
+    ///
+    /// Returns an (attributes, secret) tuple.
+    pub fn to_secret_item(&self) -> (HashMap<&str, &str>, Value) {
+        let attributes = HashMap::from([
+            ("homeserver", self.homeserver.as_str()),
+            ("user", self.user_id.as_str()),
+            ("device-id", self.device_id.as_str()),
+            ("db-path", self.path.to_str().unwrap()),
+        ]);
+
+        let secret = Value::new(&self.secret.to_string(), "application/json");
+
+        (attributes, secret)
+    }
+}
+
 /// A possible error value when converting a `Secret` from a UTF-8 byte vector.
+#[derive(Debug)]
 pub enum FromUtf8SecretError {
     Str(FromUtf8Error),
     Json(JsonError),
@@ -44,11 +215,6 @@ pub struct Secret {
 }
 
 impl Secret {
-    /// Returns a byte vec of this `Secret`’s contents.
-    pub fn as_bytes(&self) -> Vec<u8> {
-        serde_json::to_string(self).unwrap().as_bytes().to_vec()
-    }
-
     /// Converts a vector of bytes to a `Secret`.
     pub fn from_utf8(vec: Vec<u8>) -> Result<Self, FromUtf8SecretError> {
         let s = String::from_utf8(vec)?;
@@ -56,101 +222,77 @@ impl Secret {
     }
 }
 
+impl fmt::Display for Secret {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", serde_json::to_string(self).unwrap())
+    }
+}
+
+/// The `Schema` of the items in the `SecretService`.
+fn schema() -> Schema {
+    let attributes = HashMap::from([
+        ("homeserver", SchemaAttributeType::String),
+        ("user", SchemaAttributeType::String),
+        ("device-id", SchemaAttributeType::String),
+        ("db-path", SchemaAttributeType::String),
+    ]);
+
+    Schema::new(APP_ID, SchemaFlags::NONE, attributes)
+}
+
 /// Retrieves all sessions stored to the `SecretService`
-pub fn restore_sessions() -> Result<Vec<StoredSession>, secret_service::Error> {
-    let ss = SecretService::new(EncryptionType::Dh)?;
-    let collection = get_default_collection_unlocked(&ss)?;
-
-    // Sessions that contain or produce errors are ignored.
-    // TODO: Return error for corrupt sessions
-
-    let res = collection
-        .search_items([("xdg:schema", APP_ID)].into())?
-        .iter()
-        .filter_map(|item| {
-            let attr = item.get_attributes().ok()?;
-
-            let homeserver = Url::parse(attr.get("homeserver")?).ok()?;
-            let user_id = UserId::parse(attr.get("user")?.as_str()).ok()?;
-            let device_id = <&DeviceId>::from(attr.get("device-id")?.as_str()).to_owned();
-            let path = PathBuf::from(attr.get("db-path")?);
-            let secret = Secret::from_utf8(item.get_secret().ok()?).ok()?;
-
-            Some(StoredSession {
-                homeserver,
-                path,
-                user_id,
-                device_id,
-                secret,
-            })
-        })
-        .collect();
+pub async fn restore_sessions() -> Result<Vec<StoredSession>, SecretError> {
+    let items = password_search_future(
+        Some(&schema()),
+        HashMap::new(),
+        SearchFlags::ALL | SearchFlags::UNLOCK | SearchFlags::LOAD_SECRETS,
+    )
+    .await?;
+    let mut sessions = Vec::with_capacity(items.len());
+
+    for item in items {
+        sessions.push(StoredSession::try_from_secret_item(item).await?);
+    }
 
-    Ok(res)
+    Ok(sessions)
 }
 
 /// Writes a session to the `SecretService`, overwriting any previously stored
 /// session with the same `homeserver`, `username` and `device-id`.
-pub fn store_session(session: &StoredSession) -> Result<(), secret_service::Error> {
-    let ss = SecretService::new(EncryptionType::Dh)?;
-    let collection = get_default_collection_unlocked(&ss)?;
-
-    let attributes = [
-        ("xdg:schema", APP_ID),
-        ("homeserver", session.homeserver.as_str()),
-        ("user", session.user_id.as_str()),
-        ("device-id", session.device_id.as_str()),
-        ("db-path", session.path.to_str().unwrap()),
-    ]
-    .into();
-
-    collection.create_item(
+pub async fn store_session(session: &StoredSession) -> Result<(), SecretError> {
+    let (attributes, secret) = session.to_secret_item();
+
+    password_store_binary_future(
+        Some(&schema()),
+        attributes,
+        Some(&COLLECTION_DEFAULT),
         // Translators: The parameter is a Matrix User ID
         &gettext!("Fractal: Matrix credentials for {}", session.user_id),
-        attributes,
-        &session.secret.as_bytes(),
-        true,
-        "application/json",
-    )?;
+        &secret,
+    )
+    .await?;
 
     Ok(())
 }
 
 /// Removes a session from the `SecretService`
-pub fn remove_session(session: &StoredSession) -> Result<(), secret_service::Error> {
-    let ss = SecretService::new(EncryptionType::Dh)?;
-    let collection = get_default_collection_unlocked(&ss)?;
-
-    let attributes = [
-        ("xdg:schema", APP_ID),
-        ("homeserver", session.homeserver.as_str()),
-        ("user", session.user_id.as_str()),
-        ("device-id", session.device_id.as_str()),
-        ("db-path", session.path.to_str().unwrap()),
-    ]
-    .into();
+pub async fn remove_session(session: &StoredSession) -> Result<(), SecretError> {
+    let (attributes, _) = session.to_secret_item();
 
-    let items = collection.search_items(attributes)?;
-
-    for item in items {
-        item.delete()?;
-    }
+    password_clear_future(Some(&schema()), attributes).await?;
 
     Ok(())
 }
 
-fn get_default_collection_unlocked<'a>(
-    secret_service: &'a SecretService,
-) -> Result<secret_service::Collection<'a>, secret_service::Error> {
-    let collection = match secret_service.get_default_collection() {
-        Ok(col) => col,
-        Err(secret_service::Error::NoResult) => {
-            secret_service.create_collection("default", "default")?
-        }
-        Err(error) => return Err(error),
-    };
+/// Removes an item from the `SecretService`
+pub async fn remove_item(item: &Retrievable) -> Result<(), SecretError> {
+    let attributes = item.attributes();
+    let mut attr = HashMap::with_capacity(attributes.len());
 
-    collection.unlock()?;
+    for (key, value) in attributes.iter() {
+        attr.insert(key.as_str(), value.as_str());
+    }
+    password_clear_future(Some(&schema()), attr).await?;
 
-    Ok(collection)
+    Ok(())
 }
diff --git a/src/session/mod.rs b/src/session/mod.rs
index 3665442c9..dda550163 100644
--- a/src/session/mod.rs
+++ b/src/session/mod.rs
@@ -127,7 +127,7 @@ mod imp {
             klass.install_action("session.logout", None, move |session, _, _| {
                 spawn!(clone!(@weak session => async move {
                     session.imp().logout_on_dispose.set(false);
-                    session.logout().await
+                    session.logout(true).await
                 }));
             });
 
@@ -248,7 +248,7 @@ mod imp {
             }
 
             if self.logout_on_dispose.get() {
-                glib::MainContext::default().block_on(obj.logout());
+                glib::MainContext::default().block_on(obj.logout(true));
             }
         }
     }
@@ -347,7 +347,7 @@ impl Session {
         spawn!(
             glib::PRIORITY_DEFAULT_IDLE,
             clone!(@weak self as obj => async move {
-                obj.handle_login_result(handle.await.unwrap(), true);
+                obj.handle_login_result(handle.await.unwrap(), true).await;
             })
         );
     }
@@ -381,12 +381,12 @@ impl Session {
         spawn!(
             glib::PRIORITY_DEFAULT_IDLE,
             clone!(@weak self as obj => async move {
-                obj.handle_login_result(handle.await.unwrap(), false);
+                obj.handle_login_result(handle.await.unwrap(), false).await;
             })
         );
     }
 
-    fn handle_login_result(
+    async fn handle_login_result(
         &self,
         result: Result<(Client, StoredSession), matrix_sdk::Error>,
         store_session: bool,
@@ -401,19 +401,19 @@ impl Session {
 
                 self.update_user_profile();
 
-                let res = if store_session {
-                    match secret::store_session(&session) {
-                        Ok(()) => None,
-                        Err(error) => {
-                            warn!("Couldn't store session: {:?}", error);
-                            Some(Toast::new(&gettext!(
-                                "Unable to store session: {}",
-                                &error.to_user_facing()
-                            )))
+                if store_session {
+                    if let Err(error) = secret::store_session(&session).await {
+                        warn!("Couldn't store session: {:?}", error);
+                        if let Some(window) = self.parent_window() {
+                            window.switch_to_error_page(
+                                &gettext!("Unable to store session: {}", error),
+                                error,
+                            );
                         }
+                        self.logout(false).await;
+                        fs::remove_dir_all(session.path).unwrap();
+                        return;
                     }
-                } else {
-                    None
                 };
 
                 priv_.info.set(session).unwrap();
@@ -422,7 +422,7 @@ impl Session {
 
                 self.sync();
 
-                res
+                None
             }
             Err(error) => {
                 error!("Failed to prepare the session: {}", error);
@@ -686,7 +686,7 @@ impl Session {
         window.show();
     }
 
-    pub async fn logout(&self) {
+    pub async fn logout(&self, cleanup: bool) {
         let stack = &self.imp().stack;
         self.emit_by_name::<()>("logged-out", &[]);
 
@@ -704,7 +704,11 @@ impl Session {
         });
 
         match handle.await.unwrap() {
-            Ok(_) => self.cleanup_session(),
+            Ok(_) => {
+                if cleanup {
+                    self.cleanup_session().await
+                }
+            }
             Err(error) => {
                 error!("Couldn’t logout the session {}", error);
                 if let Some(window) = self.parent_window() {
@@ -720,10 +724,15 @@ impl Session {
     /// `Session::logout`.
     pub fn handle_logged_out(&self) {
         self.emit_by_name::<()>("logged-out", &[]);
-        self.cleanup_session();
+        spawn!(
+            glib::PRIORITY_LOW,
+            clone!(@strong self as obj => async move {
+                obj.cleanup_session().await;
+            })
+        );
     }
 
-    fn cleanup_session(&self) {
+    async fn cleanup_session(&self) {
         let priv_ = self.imp();
         let info = priv_.info.get().unwrap();
 
@@ -737,7 +746,7 @@ impl Session {
             handle.abort();
         }
 
-        if let Err(error) = secret::remove_session(info) {
+        if let Err(error) = secret::remove_session(info).await {
             error!(
                 "Failed to remove credentials from SecretService after logout: {}",
                 error
diff --git a/src/user_facing_error.rs b/src/user_facing_error.rs
index c07bed1ca..1b0162cd4 100644
--- a/src/user_facing_error.rs
+++ b/src/user_facing_error.rs
@@ -57,13 +57,3 @@ impl UserFacingError for Error {
         }
     }
 }
-
-impl UserFacingError for secret_service::Error {
-    fn to_user_facing(self) -> String {
-        use secret_service::Error::*;
-        match self {
-            Locked => gettext("Keychain locked."),
-            _ => gettext("Secret Service error."),
-        }
-    }
-}
diff --git a/src/window.rs b/src/window.rs
index fcdddfa15..35edf1859 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -7,7 +7,8 @@ use log::warn;
 use crate::{
     components::{InAppNotification, Toast},
     config::{APP_ID, PROFILE},
-    secret, Application, Greeter, Login, Session, UserFacingError,
+    secret::{self, SecretError},
+    spawn, Application, ErrorPage, Greeter, Login, Session,
 };
 
 mod imp {
@@ -28,6 +29,8 @@ mod imp {
         #[template_child]
         pub login: TemplateChild<Login>,
         #[template_child]
+        pub error_page: TemplateChild<ErrorPage>,
+        #[template_child]
         pub sessions: TemplateChild<gtk::Stack>,
         #[template_child]
         pub error_list: TemplateChild<gio::ListStore>,
@@ -85,19 +88,6 @@ mod imp {
             }
 
             obj.load_window_size();
-            obj.restore_sessions();
-
-            self.login
-                .connect_new_session(clone!(@weak obj => move |_login, session| {
-                    obj.add_session(&session);
-                    obj.switch_to_loading_page();
-                }));
-
-            self.main_stack.connect_visible_child_notify(
-                clone!(@weak obj => move |_| obj.set_default_by_child()),
-            );
-
-            obj.set_default_by_child();
 
             // Ask for the toggle fullscreen state
             let fullscreen = gio::SimpleAction::new("toggle-fullscreen", None);
@@ -109,6 +99,10 @@ mod imp {
                 }
             }));
             obj.add_action(&fullscreen);
+
+            spawn!(clone!(@weak obj => async move {
+                obj.restore_sessions().await;
+            }));
         }
     }
 
@@ -170,8 +164,8 @@ impl Window {
         }
     }
 
-    fn restore_sessions(&self) {
-        match secret::restore_sessions() {
+    pub async fn restore_sessions(&self) {
+        match secret::restore_sessions().await {
             Ok(sessions) => {
                 if sessions.is_empty() {
                     self.switch_to_greeter_page(false);
@@ -182,13 +176,27 @@ impl Window {
                         self.add_session(&session);
                     }
                 }
+
+                let priv_ = self.imp();
+                priv_.login.connect_new_session(
+                    clone!(@weak self as obj => move |_login, session| {
+                        obj.add_session(&session);
+                        obj.switch_to_loading_page();
+                    }),
+                );
+
+                priv_.main_stack.connect_visible_child_notify(
+                    clone!(@weak self as obj => move |_| obj.set_default_by_child()),
+                );
+
+                self.set_default_by_child();
             }
             Err(error) => {
                 warn!("Failed to restore previous sessions: {:?}", error);
-                self.add_toast(&Toast::new(&gettext!(
-                    "Unable to restore previous sessions: {}",
-                    &error.to_user_facing()
-                )));
+                self.switch_to_error_page(
+                    &gettext!("Failed to restore previous sessions: {}", error),
+                    error,
+                );
             }
         }
     }
@@ -262,6 +270,12 @@ impl Window {
         priv_.main_stack.set_visible_child(&*priv_.greeter);
     }
 
+    pub fn switch_to_error_page(&self, message: &str, error: SecretError) {
+        let priv_ = self.imp();
+        priv_.error_page.display_secret_error(message, error);
+        priv_.main_stack.set_visible_child(&*priv_.error_page);
+    }
+
     /// This appends a new toast to the list
     pub fn add_toast(&self, toast: &Toast) {
         self.imp().error_list.append(toast);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]